mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[java-psi] Proper order of annotations inside PsiNewExpressionImpl
Fixes IDEA-366918 Wrong order of annotations in new array expressions GitOrigin-RevId: 29bbfb29123cd273f4c3d34f74e8a00bc6ab5ec1
This commit is contained in:
committed by
intellij-monorepo-bot
parent
8e6bbbf1e1
commit
7e3a48e6ad
+15
-5
@@ -22,6 +22,7 @@ import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
@@ -46,6 +47,7 @@ public class PsiNewExpressionImpl extends ExpressionPsiElement implements PsiNew
|
||||
private @Nullable PsiType doGetType(@Nullable PsiAnnotation stopAt) {
|
||||
PsiType type = null;
|
||||
List<PsiAnnotation> annotations = new SmartList<>();
|
||||
List<TypeAnnotationProvider> arrayComponentAnnotations = new ArrayList<>();
|
||||
boolean stop = false;
|
||||
|
||||
for (ASTNode child = getFirstChildNode(); child != null; child = child.getTreeNext()) {
|
||||
@@ -53,7 +55,13 @@ public class PsiNewExpressionImpl extends ExpressionPsiElement implements PsiNew
|
||||
if (elementType == JavaElementType.ANNOTATION) {
|
||||
PsiAnnotation annotation = (PsiAnnotation)child.getPsi();
|
||||
annotations.add(annotation);
|
||||
if (annotation == stopAt) stop = true;
|
||||
if (annotation == stopAt) {
|
||||
// Drop previous array annotations. Only the subsequent annotations matter
|
||||
// E.g., if we have "new int @A [] @B [] @C []" and we search an owner for "@B"
|
||||
// then we should return "new int @B [] @C []"
|
||||
arrayComponentAnnotations.clear();
|
||||
stop = true;
|
||||
}
|
||||
}
|
||||
else if (elementType == JavaElementType.JAVA_CODE_REFERENCE) {
|
||||
assert type == null : this;
|
||||
@@ -70,8 +78,7 @@ public class PsiNewExpressionImpl extends ExpressionPsiElement implements PsiNew
|
||||
else if (elementType == JavaTokenType.LBRACKET) {
|
||||
assert type != null : this;
|
||||
PsiAnnotation[] copy = ContainerUtil.copyAndClear(annotations, PsiAnnotation.ARRAY_FACTORY, true);
|
||||
type = type.createArrayType().annotate(TypeAnnotationProvider.Static.create(copy));
|
||||
if (stop) return type;
|
||||
arrayComponentAnnotations.add(TypeAnnotationProvider.Static.create(copy));
|
||||
}
|
||||
else if (elementType == JavaElementType.ANONYMOUS_CLASS) {
|
||||
PsiElementFactory factory = JavaPsiFacade.getElementFactory(getProject());
|
||||
@@ -83,8 +90,11 @@ public class PsiNewExpressionImpl extends ExpressionPsiElement implements PsiNew
|
||||
}
|
||||
}
|
||||
|
||||
// stop == true means annotation is misplaced
|
||||
return stop ? null : type;
|
||||
for (int i = arrayComponentAnnotations.size() - 1; i >= 0; i--) {
|
||||
TypeAnnotationProvider provider = arrayComponentAnnotations.get(i);
|
||||
type = new PsiArrayType(type, provider);
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
import org.jetbrains.annotations.*;
|
||||
import java.util.*;
|
||||
|
||||
class A {
|
||||
int @NotNull [] @Nullable [] a = new int @NotNull [] @Nullable [] { null };
|
||||
}
|
||||
@@ -154,6 +154,7 @@ public class DataFlowInspection21Test extends DataFlowInspectionTestCase {
|
||||
public void testGetterVsDirectAccessObjectEquals() { doTest(); }
|
||||
public void testSetterAndGetter() { doTest(); }
|
||||
public void testStaticEqualsContract() { doTest(); }
|
||||
public void testNewExpressionAnnotations() { doTest(); }
|
||||
|
||||
public void testJSpecifyLocalWithGenerics() {
|
||||
addJSpecifyNullMarked(myFixture);
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.psi.impl;
|
||||
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase;
|
||||
import one.util.streamex.StreamEx;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
public final class PsiNewExpressionTest extends LightJavaCodeInsightFixtureTestCase {
|
||||
public void testArrayAnnotations() {
|
||||
myFixture.configureByText("Foo.java", """
|
||||
@interface A { }
|
||||
@interface B { }
|
||||
@interface C { }
|
||||
@interface D { }
|
||||
class Foo {
|
||||
int[][] data = new @A <caret>int @B @C [] @D [] {};
|
||||
}
|
||||
""");
|
||||
PsiFile file = myFixture.getFile();
|
||||
PsiNewExpression newExpression =
|
||||
PsiTreeUtil.getParentOfType(file.findElementAt(myFixture.getCaretOffset()), PsiNewExpression.class);
|
||||
assertNotNull(newExpression);
|
||||
assertEquals("@A int @B @C [] @D []", newExpression.getType().getCanonicalText(true));
|
||||
Map<String, PsiAnnotation> allAnnotations =
|
||||
StreamEx.of(PsiTreeUtil.collectElementsOfType(newExpression, PsiAnnotation.class))
|
||||
.toMap(anno -> anno.getQualifiedName(), Function.identity());
|
||||
assertEquals(4, allAnnotations.size());
|
||||
|
||||
|
||||
Map<String, String> expectedOwnerTexts = Map.of(
|
||||
"A", "@A int",
|
||||
"B", "@A int @B @C [] @D []",
|
||||
"C", "@A int @B @C [] @D []",
|
||||
"D", "@A int @D []"
|
||||
);
|
||||
|
||||
for (Map.Entry<String, String> entry : expectedOwnerTexts.entrySet()) {
|
||||
String anno = entry.getKey();
|
||||
String expectedType = entry.getValue();
|
||||
PsiAnnotationOwner owner = allAnnotations.get(anno).getOwner();
|
||||
PsiType type = assertInstanceOf(owner, PsiType.class);
|
||||
assertEquals(anno, expectedType, type.getCanonicalText(true));
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user