From 096d52cf891b3a6c5ebaf56c49cd297cbb7dca5e Mon Sep 17 00:00:00 2001 From: Marcin Mikosik Date: Thu, 5 Feb 2026 12:54:15 +0000 Subject: [PATCH] IDEA-384557 [java]: PsiTypeElementImpl.getType() incorrectly applies annotations to qualified types Merge-request: IJ-MR-188692 Merged-by: Marcin Mikosik GitOrigin-RevId: 566d6e92c6828472a825198af38383e304b62830 --- .../newImpl/ExtractOptionsPipeline.kt | 11 +- .../impl/source/PsiClassReferenceType.java | 39 ++- .../psi/impl/source/PsiTypeElementImpl.java | 24 +- .../impl/source/tree/JavaSharedImplUtil.java | 6 + .../java/refactoring/ChangeSignatureTest.java | 2 +- .../impl/source/PsiTypeElementImplTest.java | 259 ++++++++++++------ 6 files changed, 245 insertions(+), 96 deletions(-) diff --git a/java/java-impl-refactorings/src/com/intellij/refactoring/extractMethod/newImpl/ExtractOptionsPipeline.kt b/java/java-impl-refactorings/src/com/intellij/refactoring/extractMethod/newImpl/ExtractOptionsPipeline.kt index 519e8cf89b56..5be515550864 100644 --- a/java/java-impl-refactorings/src/com/intellij/refactoring/extractMethod/newImpl/ExtractOptionsPipeline.kt +++ b/java/java-impl-refactorings/src/com/intellij/refactoring/extractMethod/newImpl/ExtractOptionsPipeline.kt @@ -31,9 +31,11 @@ import com.intellij.psi.PsiType import com.intellij.psi.PsiTypeCastExpression import com.intellij.psi.PsiVariable import com.intellij.psi.formatter.java.MultipleFieldDeclarationHelper +import com.intellij.psi.impl.source.PsiClassReferenceType import com.intellij.psi.search.LocalSearchScope import com.intellij.psi.search.PsiElementProcessor import com.intellij.psi.util.PsiTreeUtil +import com.intellij.psi.util.PsiTreeUtil.findChildrenOfType import com.intellij.psi.util.PsiTypesUtil import com.intellij.psi.util.PsiUtil import com.intellij.refactoring.RefactoringBundle @@ -287,10 +289,13 @@ object ExtractMethodPipeline { } private fun withFilteredAnnotations(type: PsiType, context: PsiElement?): PsiType { - val project = type.annotations.firstOrNull()?.project ?: return type + val project = (type.annotations.firstOrNull() ?: (type as? PsiClassReferenceType)?.reference)?.project ?: return type val factory = PsiElementFactory.getInstance(project) val typeHolder = factory.createParameter("x", type, context) - typeHolder.type.annotations.filterNot { it.qualifiedName in annotationsToKeep }.forEach { it.delete() } + findChildrenOfType(typeHolder, PsiAnnotation::class.java) + .filterNotNull() + .filterNot { it.qualifiedName in annotationsToKeep } + .forEach { it.delete() } return typeHolder.type } @@ -317,4 +322,4 @@ object ExtractMethodPipeline { dataOutput = withFilteredAnnotation(extractOptions.dataOutput, extractOptions.targetClass) ) } -} \ No newline at end of file +} diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiClassReferenceType.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiClassReferenceType.java index b4a7d1fcac1d..56acff5f78f3 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiClassReferenceType.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiClassReferenceType.java @@ -38,10 +38,15 @@ import java.util.LinkedHashSet; import java.util.List; import java.util.Objects; +import static com.intellij.psi.impl.source.tree.JavaSharedImplUtil.filteringTypeAnnotationProvider; import static com.intellij.util.ObjectUtils.notNull; public class PsiClassReferenceType extends PsiClassType.Stub { private final ClassReferencePointer myReference; + /** + * Annotations that precede qualifier if qualifier exists. + */ + private final @NotNull TypeAnnotationProvider myQualifierAnnotationsProvider; private TypeNullability myNullability = null; public PsiClassReferenceType(@NotNull PsiJavaCodeReferenceElement reference, LanguageLevel level) { @@ -51,20 +56,25 @@ public class PsiClassReferenceType extends PsiClassType.Stub { public PsiClassReferenceType(@NotNull PsiJavaCodeReferenceElement reference, LanguageLevel level, PsiAnnotation @NotNull [] annotations) { super(level, annotations); myReference = ClassReferencePointer.constant(reference); + myQualifierAnnotationsProvider = TypeAnnotationProvider.EMPTY; } public PsiClassReferenceType(@NotNull PsiJavaCodeReferenceElement reference, LanguageLevel level, @NotNull TypeAnnotationProvider provider) { - this(ClassReferencePointer.constant(reference), level, provider); + this(ClassReferencePointer.constant(reference), level, provider, TypeAnnotationProvider.EMPTY); } - PsiClassReferenceType(@NotNull ClassReferencePointer reference, LanguageLevel level, @NotNull TypeAnnotationProvider provider) { - this(reference, level, provider, null); + PsiClassReferenceType(@NotNull ClassReferencePointer reference, + LanguageLevel level, + @NotNull TypeAnnotationProvider provider, + @NotNull TypeAnnotationProvider qualifierAnnotationsProvider) { + this(reference, level, provider, qualifierAnnotationsProvider, null); } private PsiClassReferenceType(@NotNull ClassReferencePointer reference, LanguageLevel level, @NotNull TypeAnnotationProvider provider, - @Nullable TypeNullability nullability) { + @NotNull TypeAnnotationProvider qualifierAnnotationsProvider, @Nullable TypeNullability nullability) { super(level, provider); myReference = reference; + myQualifierAnnotationsProvider = qualifierAnnotationsProvider; myNullability = nullability; } @@ -86,6 +96,9 @@ public class PsiClassReferenceType extends PsiClassType.Stub { for (PsiAnnotation annotation : getAnnotations(false)) { if (!annotation.isValid()) return false; } + for (PsiAnnotation annotation : myQualifierAnnotationsProvider.getAnnotations()) { + if (!annotation.isValid()) return false; + } return true; } return false; @@ -165,7 +178,16 @@ public class PsiClassReferenceType extends PsiClassType.Stub { @Override public @NotNull PsiClassType withNullability(@NotNull TypeNullability nullability) { if (myNullability == nullability) return this; - return new PsiClassReferenceType(myReference, myLanguageLevel, getAnnotationProvider(), nullability); + return new PsiClassReferenceType(myReference, myLanguageLevel, getAnnotationProvider(), myQualifierAnnotationsProvider, nullability); + } + + /** + * Returns a copy of this PsiClassReferenceType with annotations from qualifierAnnotations parameter, + * which target is {@link PsiAnnotation.TargetType#TYPE_USE}, added to qualifier annotations. + */ + public @NotNull PsiClassReferenceType withAddedQualifierAnnotations(@NotNull PsiAnnotation @NotNull [] qualifierAnnotations) { + TypeAnnotationProvider merged = filteringTypeAnnotationProvider(qualifierAnnotations, myQualifierAnnotationsProvider); + return new PsiClassReferenceType(myReference, myLanguageLevel, getAnnotationProvider(), merged, myNullability); } @Override @@ -310,7 +332,7 @@ public class PsiClassReferenceType extends PsiClassType.Stub { PsiElement qualifier = ref.getQualifier(); String qualifierInfo = ""; if (qualifier != null) { - PsiAnnotation[] qualifierAnnotations = getAnnotations(false); + PsiAnnotation[] qualifierAnnotations = myQualifierAnnotationsProvider.getAnnotations(); if (qualifierAnnotations.length > 0 && qualifier instanceof PsiJavaCodeReferenceElement && ((PsiJavaCodeReferenceElement)qualifier).resolve() instanceof PsiClass) { // Display qualifier if it's annotated @@ -341,7 +363,10 @@ public class PsiClassReferenceType extends PsiClassType.Stub { PsiJavaCodeReferenceElement reference = getReference(); if (reference instanceof PsiAnnotatedJavaCodeReferenceElement) { PsiAnnotatedJavaCodeReferenceElement ref = (PsiAnnotatedJavaCodeReferenceElement)reference; - PsiAnnotation[] annotations = annotated ? getAnnotations(false) : PsiAnnotation.EMPTY_ARRAY; + PsiAnnotation[] annotations = + annotated + ? ArrayUtil.mergeArrays(getAnnotations(false), myQualifierAnnotationsProvider.getAnnotations()) + : PsiAnnotation.EMPTY_ARRAY; return ref.getCanonicalText(annotated, annotations.length == 0 ? null : annotations); } return reference.getCanonicalText(); diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiTypeElementImpl.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiTypeElementImpl.java index 4c0308e13d55..3bc2745f9f5b 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiTypeElementImpl.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiTypeElementImpl.java @@ -147,7 +147,7 @@ public class PsiTypeElementImpl extends CompositePsiElement implements PsiTypeEl } else if (child instanceof PsiJavaCodeReferenceElement) { assert type == null : this; - type = new PsiClassReferenceType(getReferenceComputable((PsiJavaCodeReferenceElement)child), null, createProvider(annotations)); + type = getPsiClassReferenceType((PsiJavaCodeReferenceElement)child, createProvider(annotations)); } else if (PsiUtil.isJavaToken(child, JavaTokenType.LBRACKET)) { assert type != null : this; @@ -222,6 +222,12 @@ public class PsiTypeElementImpl extends CompositePsiElement implements PsiTypeEl if (modifierList == null) return type; PsiAnnotation[] annotations = modifierList.getAnnotations(); if (annotations.length == 0) return type; + if (type instanceof PsiClassReferenceType) { + PsiJavaCodeReferenceElement innermost = getInnermostComponentReferenceElement(); + if (innermost != null && innermost.isQualified()) { + return ((PsiClassReferenceType)type).withAddedQualifierAnnotations(annotations); + } + } return JavaSharedImplUtil.annotate(type, modifierList, annotations); } @@ -235,7 +241,7 @@ public class PsiTypeElementImpl extends CompositePsiElement implements PsiTypeEl return (PsiModifierListOwner)parent; } } - if (isDeepestComponentInsideArrayType(parent) || isFirstExceptionTypeInsideMultiCatch(parent)) { + if (isArrayTypeElement(parent) || isFirstExceptionTypeInsideMultiCatch(parent)) { return ObjectUtils.tryCast(getNonDummyContext(parent), PsiModifierListOwner.class); } return null; @@ -256,7 +262,7 @@ public class PsiTypeElementImpl extends CompositePsiElement implements PsiTypeEl && PsiTreeUtil.findSiblingForward(this, JavaTokenType.OR, null) != null; } - private static boolean isDeepestComponentInsideArrayType(PsiElement parent) { + private static boolean isArrayTypeElement(PsiElement parent) { return parent instanceof PsiTypeElement && PsiUtil.isJavaToken(parent.getLastChild(), TokenSet.create(JavaTokenType.RBRACKET, JavaTokenType.ELLIPSIS)); } @@ -324,6 +330,18 @@ public class PsiTypeElementImpl extends CompositePsiElement implements PsiTypeEl PsiAugmentProvider.isInferredType(this); } + private static @NotNull PsiType getPsiClassReferenceType(PsiJavaCodeReferenceElement psiJavaCodeReferenceElement, + TypeAnnotationProvider provider) { + if (psiJavaCodeReferenceElement.isQualified()) { + return new PsiClassReferenceType( + getReferenceComputable(psiJavaCodeReferenceElement), null, TypeAnnotationProvider.EMPTY, provider); + } + else { + return new PsiClassReferenceType( + getReferenceComputable(psiJavaCodeReferenceElement), null, provider, TypeAnnotationProvider.EMPTY); + } + } + private static @NotNull ClassReferencePointer getReferenceComputable(@NotNull PsiJavaCodeReferenceElement ref) { PsiElement parent = getAnchorParent(ref); if (parent instanceof PsiMethod || parent instanceof PsiVariable) { diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/JavaSharedImplUtil.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/JavaSharedImplUtil.java index 00a4199d2778..eb33413b8e12 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/JavaSharedImplUtil.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/JavaSharedImplUtil.java @@ -286,6 +286,12 @@ public final class JavaSharedImplUtil { variable.addAfter(initializer, eq.getPsi()); } + public static @NotNull TypeAnnotationProvider filteringTypeAnnotationProvider(@NotNull PsiAnnotation @NotNull [] candidates, + @NotNull TypeAnnotationProvider originalProvider) { + if (candidates.length == 0) return originalProvider; + return new FilteringTypeAnnotationProvider(candidates, originalProvider); + } + private static final class FilteringTypeAnnotationProvider implements TypeAnnotationProvider { private final PsiAnnotation[] myCandidates; private final TypeAnnotationProvider myOriginalProvider; diff --git a/java/java-tests/testSrc/com/intellij/java/refactoring/ChangeSignatureTest.java b/java/java-tests/testSrc/com/intellij/java/refactoring/ChangeSignatureTest.java index 439c77d8653f..1e0b81369496 100644 --- a/java/java-tests/testSrc/com/intellij/java/refactoring/ChangeSignatureTest.java +++ b/java/java-tests/testSrc/com/intellij/java/refactoring/ChangeSignatureTest.java @@ -701,7 +701,7 @@ public class ChangeSignatureTest extends ChangeSignatureBaseTest { } public void testAddReturnAnnotation() { - doTest(null, null, "@org.jetbrains.annotations.NotNull java.lang.String", method -> new ParameterInfoImpl[0], false); + doTest(null, null, "java.lang.@org.jetbrains.annotations.NotNull String", method -> new ParameterInfoImpl[0], false); } public void testMultilineJavadoc() { // IDEA-281568 diff --git a/java/java-tests/testSrc/com/intellij/psi/impl/source/PsiTypeElementImplTest.java b/java/java-tests/testSrc/com/intellij/psi/impl/source/PsiTypeElementImplTest.java index 8cf63f3caa6e..c2a3448f9b6a 100644 --- a/java/java-tests/testSrc/com/intellij/psi/impl/source/PsiTypeElementImplTest.java +++ b/java/java-tests/testSrc/com/intellij/psi/impl/source/PsiTypeElementImplTest.java @@ -65,97 +65,128 @@ public class PsiTypeElementImplTest extends LightJavaCodeInsightTestCase { } private void doTestAddingAnnotationToTypeAtCaret(String classFile) { - configureFromFileWithMyAnnotationAdded(classFile); + configureFromFileWithAnnotationsAdded(classFile); PsiTypeElement typeElement = getPsiTypeElementAtCaret(); - WriteAction.run(() -> typeElement.addAnnotation("MyAnnotation")); - assertTypeAnnotations(typeElement.getType(), "MyAnnotation"); + WriteAction.run(() -> typeElement.addAnnotation("A")); + assertTypeAnnotations(typeElement.getType(), "A"); } public void testTypeUseAnnotationOnMethodThatReturnsSimpleTypeIsAppliedToThatType() { - configureFromFileWithMyAnnotationAdded( + configureFromFileWithAnnotationsAdded( """ class AClass { - @MyAnnotation public String foo() { return null; } + @A public @B String foo() { return null; } } """); - assertTypeAnnotations(getPsiTypeElementAtCaret().getType(), "MyAnnotation"); + assertTypeAnnotations(getPsiTypeElementAtCaret().getType(), "A", "B"); + } + + public void testTypeUseAnnotationOnMethodThatReturnsNestedTypeQualifiedWithOuterTypeIsNotAppliedToNestedType() { + configureFromFileWithAnnotationsAdded( + """ + class AClass { + class Nested {} + @A public @B AClass.Nested foo() { return null; } + } + """); + assertTypeAnnotations(getPsiTypeElementAtCaret().getType()); + } + + public void testTypeUseAnnotationOnMethodThatReturnsFullyQualifiedTypeIsNotAppliedToThatType() { + configureFromFileWithAnnotationsAdded( + """ + class AClass { + @A public @B java.lang.String foo() { return null; } + } + """); + assertTypeAnnotations(getPsiTypeElementAtCaret().getType()); } public void testTypeUseAnnotationOnMethodThatReturnsArrayTypeIsAppliedToArrayComponent() { - configureFromFileWithMyAnnotationAdded( + configureFromFileWithAnnotationsAdded( """ class AClass { - @MyAnnotation public String[] foo() { return null; } + @A public @B String[] foo() { return null; } } """); - assertTypeAnnotations(getPsiTypeElementAtCaret().getType(), "MyAnnotation"); + assertTypeAnnotations(getPsiTypeElementAtCaret().getType(), "A", "B"); + } + + public void testTypeUseAnnotationOnMethodThatReturnsArrayTypeWithQualifiedComponentIsNotAppliedToArrayComponent() { + configureFromFileWithAnnotationsAdded( + """ + class AClass { + @A public @B java.lang.String[] foo() { return null; } + } + """); + assertTypeAnnotations(getPsiTypeElementAtCaret().getType()); } public void testTypeUseAnnotationOnMethodThatReturnsArrayTypeIsNotAppliedToArrayType() { - configureFromFileWithMyAnnotationAdded( + configureFromFileWithAnnotationsAdded( """ class AClass { - @MyAnnotation public String[] foo() { return null; } + @A public @B String[] foo() { return null; } } """); assertTypeAnnotations(getPsiTypeElementAtCaret().getType()); } public void testTypeUseAnnotationOnMethodThatReturnsArrayOfGenericTypeIsAppliedToArrayComponent() { - configureFromFileWithMyAnnotationAdded( + configureFromFileWithAnnotationsAdded( """ class AClass { - @MyAnnotation public List[] foo() { return null; } + @A public @B List[] foo() { return null; } } """); - assertTypeAnnotations(getPsiTypeElementAtCaret().getType(), "MyAnnotation"); + assertTypeAnnotations(getPsiTypeElementAtCaret().getType(), "A", "B"); } public void testTypeUseAnnotationOnMethodThatReturnsArrayOfGenericTypeIsNotAppliedToArrayType() { - configureFromFileWithMyAnnotationAdded( + configureFromFileWithAnnotationsAdded( """ class AClass { - @MyAnnotation public List[] foo() { return null; } + @A public @B List[] foo() { return null; } } """); assertTypeAnnotations(getPsiTypeElementAtCaret().getType()); } public void testTypeUseAnnotationOnMethodThatReturnsMultidimensionalArrayTypeIsAppliedToArrayComponent() { - configureFromFileWithMyAnnotationAdded( + configureFromFileWithAnnotationsAdded( """ class AClass { - @MyAnnotation public String[][][] foo() { return null; } + @A public @B String[][][] foo() { return null; } } """); - assertTypeAnnotations(getPsiTypeElementAtCaret().getType(), "MyAnnotation"); + assertTypeAnnotations(getPsiTypeElementAtCaret().getType(), "A", "B"); } public void testTypeUseAnnotationOnMethodThatReturnsMultidimensionalArrayTypeIsNotAppliedToArrayType() { - configureFromFileWithMyAnnotationAdded( + configureFromFileWithAnnotationsAdded( """ class AClass { - @MyAnnotation public String[][][] foo() { return null; } + @A public @B String[][][] foo() { return null; } } """); assertTypeAnnotations(getPsiTypeElementAtCaret().getType()); } public void testTypeUseAnnotationOnMethodThatReturnsGenericTypeIsAppliedToThatType() { - configureFromFileWithMyAnnotationAdded( + configureFromFileWithAnnotationsAdded( """ class AClass { - @MyAnnotation public List foo() { return null; } + @A public @B List foo() { return null; } } """); - assertTypeAnnotations(getPsiTypeElementAtCaret().getType(), "MyAnnotation"); + assertTypeAnnotations(getPsiTypeElementAtCaret().getType(), "A", "B"); } public void testTypeUseAnnotationOnMethodThatReturnsGenericTypeIsNotAppliedToThatTypeComponentType() { - configureFromFileWithMyAnnotationAdded( + configureFromFileWithAnnotationsAdded( """ class AClass { - @MyAnnotation public List< String> foo() { return null; } + @A public @B List< String> foo() { return null; } } """); assertTypeAnnotations(getPsiTypeElementAtCaret().getType()); @@ -163,175 +194,237 @@ public class PsiTypeElementImplTest extends LightJavaCodeInsightTestCase { public void testTypeUseAnnotationOnMethodThatReturnsNestedGenericTypeIsAppliedToOuterType() { - configureFromFileWithMyAnnotationAdded( + configureFromFileWithAnnotationsAdded( """ class AClass { - @MyAnnotation public List> foo() { return null; } + @A public @B List> foo() { return null; } } """); - assertTypeAnnotations(getPsiTypeElementAtCaret().getType(), "MyAnnotation"); + assertTypeAnnotations(getPsiTypeElementAtCaret().getType(), "A", "B"); } public void testTypeUseAnnotationOnMethodThatReturnsNestedGenericTypeIsNotAppliedToInnerType() { - configureFromFileWithMyAnnotationAdded( + configureFromFileWithAnnotationsAdded( """ class AClass { - @MyAnnotation public List< List> foo() { return null; } + @A public @B List< List> foo() { return null; } } """); assertTypeAnnotations(getPsiTypeElementAtCaret().getType()); } public void testTypeUseAnnotationOnMethodThatReturnsNestedGenericTypeIsNotAppliedToInnermostType() { - configureFromFileWithMyAnnotationAdded( + configureFromFileWithAnnotationsAdded( """ class AClass { - @MyAnnotation public ListString>> foo() { return null; } + @A public @B ListString>> foo() { return null; } } """); assertTypeAnnotations(getPsiTypeElementAtCaret().getType()); } public void testTypeUseAnnotationOnMethodThatReturnsPrimitiveTypeIsAppliedToThatType() { - configureFromFileWithMyAnnotationAdded( + configureFromFileWithAnnotationsAdded( """ class AClass { - @MyAnnotation public int foo() { return 0; } + @A public @B int foo() { return 0; } } """); - assertTypeAnnotations(getPsiTypeElementAtCaret().getType(), "MyAnnotation"); + assertTypeAnnotations(getPsiTypeElementAtCaret().getType(), "A", "B"); } public void testTypeUseAnnotationOnMethodThatReturnsPrimitiveArrayTypeIsAppliedToArrayComponent() { - configureFromFileWithMyAnnotationAdded( + configureFromFileWithAnnotationsAdded( """ class AClass { - @MyAnnotation public int[] foo() { return null; } + @A public @B int[] foo() { return null; } } """); - assertTypeAnnotations(getPsiTypeElementAtCaret().getType(), "MyAnnotation"); + assertTypeAnnotations(getPsiTypeElementAtCaret().getType(), "A", "B"); } public void testTypeUseAnnotationOnMethodThatReturnsPrimitiveArrayTypeIsNotAppliedToArrayType() { - configureFromFileWithMyAnnotationAdded( + configureFromFileWithAnnotationsAdded( """ class AClass { - @MyAnnotation public int[] foo() { return null; } + @A public @B int[] foo() { return null; } } """); assertTypeAnnotations(getPsiTypeElementAtCaret().getType()); } public void testTypeUseAnnotationOnParameterWhichTypeIsSimpleTypeIsAppliedToThatType() { - configureFromFileWithMyAnnotationAdded( + configureFromFileWithAnnotationsAdded( """ class AClass { - public void foo(@MyAnnotation String param) {} + public void foo(@A String param) {} } """); - assertTypeAnnotations(getPsiTypeElementAtCaret().getType(), "MyAnnotation"); + assertTypeAnnotations(getPsiTypeElementAtCaret().getType(), "A"); + } + + public void testTypeUseAnnotationOnParameterWhichTypeIsNestedTypeQualifiedWithOuterTypeIsNotAppliedToNestedType() { + configureFromFileWithAnnotationsAdded( + """ + class AClass { + class Nested {} + public void foo(@MyAnnotation AClass.Nested param) {} + } + """); + assertTypeAnnotations(getPsiTypeElementAtCaret().getType()); + } + + public void testTypeUseAnnotationOnParameterWhichTypeIsFullyQualifiedIsNotAppliedToThatType() { + configureFromFileWithAnnotationsAdded( + """ + class AClass { + public void foo(@MyAnnotation java.lang.String param) {} + } + """); + assertTypeAnnotations(getPsiTypeElementAtCaret().getType()); } public void testTypeUseAnnotationOnParameterWhichTypeIsArrayTypeIsAppliedToArrayComponent() { - configureFromFileWithMyAnnotationAdded( + configureFromFileWithAnnotationsAdded( """ class AClass { - public void foo(@MyAnnotation String[] array) { return null; } + public void foo(@A String[] array) { return null; } } """); - assertTypeAnnotations(getPsiTypeElementAtCaret().getType(), "MyAnnotation"); + assertTypeAnnotations(getPsiTypeElementAtCaret().getType(), "A"); + } + + public void testTypeUseAnnotationOnParameterWhichTypeIsArrayTypeWithQualifiedComponentIsNotAppliedToArrayComponent() { + configureFromFileWithAnnotationsAdded( + """ + class AClass { + public void foo(@MyAnnotation java.lang.String[] array) { return null; } + } + """); + assertTypeAnnotations(getPsiTypeElementAtCaret().getType()); } public void testTypeUseAnnotationOnParameterWhichTypeIsArrayTypeIsNotAppliedToArrayType() { - configureFromFileWithMyAnnotationAdded( + configureFromFileWithAnnotationsAdded( """ class AClass { - public void foo(@MyAnnotation String[] array) { return null; } + public void foo(@A String[] array) { return null; } } """); assertTypeAnnotations(getPsiTypeElementAtCaret().getType()); } public void testTypeUseAnnotationOnParameterWhichTypeIsVarargIsAppliedToArrayComponent() { - configureFromFileWithMyAnnotationAdded( + configureFromFileWithAnnotationsAdded( """ class AClass { - public void foo(@MyAnnotation String... array) { return null; } + public void foo(@A String... array) { return null; } } """); - assertTypeAnnotations(getPsiTypeElementAtCaret().getType(), "MyAnnotation"); + assertTypeAnnotations(getPsiTypeElementAtCaret().getType(), "A"); } public void testTypeUseAnnotationOnParameterWhichTypeIsVarargIsNotAppliedToArrayType() { - configureFromFileWithMyAnnotationAdded( + configureFromFileWithAnnotationsAdded( """ class AClass { - public void foo(@MyAnnotation String... array) { return null; } + public void foo(@A String... array) { return null; } } """); assertTypeAnnotations(getPsiTypeElementAtCaret().getType()); } public void testTypeUseAnnotationOnFieldWhichTypeIsSimpleTypeIsAppliedToThatType() { - configureFromFileWithMyAnnotationAdded( + configureFromFileWithAnnotationsAdded( """ class AClass { - private @MyAnnotation String field; + private @A String field; } """); - assertTypeAnnotations(getPsiTypeElementAtCaret().getType(), "MyAnnotation"); + assertTypeAnnotations(getPsiTypeElementAtCaret().getType(), "A"); + } + + public void testTypeUseAnnotationOnFieldWhichTypeIsNestedTypeQualifiedWithOuterTypeIsNotAppliedToThatType() { + configureFromFileWithAnnotationsAdded( + """ + class AClass { + class Nested {} + private @MyAnnotation AClass.Nested field; + } + """); + assertTypeAnnotations(getPsiTypeElementAtCaret().getType()); + } + + public void testTypeUseAnnotationOnFieldWhichTypeIsFullyQualifiedIsNotAppliedToThatType() { + configureFromFileWithAnnotationsAdded( + """ + class AClass { + private @MyAnnotation java.lang.String field; + } + """); + assertTypeAnnotations(getPsiTypeElementAtCaret().getType()); } public void testTypeUseAnnotationOnFieldWhichTypeIsArrayTypeIsAppliedToArrayComponent() { - configureFromFileWithMyAnnotationAdded( + configureFromFileWithAnnotationsAdded( """ class AClass { - private @MyAnnotation String[] array; + private @A String[] array; } """); - assertTypeAnnotations(getPsiTypeElementAtCaret().getType(), "MyAnnotation"); + assertTypeAnnotations(getPsiTypeElementAtCaret().getType(), "A"); + } + + public void testTypeUseAnnotationOnFieldWhichTypeIsArrayTypeWithQualifiedComponentIsNotAppliedToArrayComponent() { + configureFromFileWithAnnotationsAdded( + """ + class AClass { + private @MyAnnotation java.lang.String[] array; + } + """); + assertTypeAnnotations(getPsiTypeElementAtCaret().getType()); } public void testTypeUseAnnotationOnFieldWhichTypeIsArrayTypeIsNotAppliedToArrayType() { - configureFromFileWithMyAnnotationAdded( + configureFromFileWithAnnotationsAdded( """ class AClass { - private @MyAnnotation String[] array; + private @A String[] array; } """); assertTypeAnnotations(getPsiTypeElementAtCaret().getType()); } public void testTypeUseAnnotationOnVariableWhichTypeIsSimpleTypeIsAppliedToThatType() { - configureFromFileWithMyAnnotationAdded( + configureFromFileWithAnnotationsAdded( """ class AClass { void foo() { - @MyAnnotation String variable; + @A String variable; } } """); - assertTypeAnnotations(getPsiTypeElementAtCaret().getType(), "MyAnnotation"); + assertTypeAnnotations(getPsiTypeElementAtCaret().getType(), "A"); } public void testTypeUseAnnotationOnVariableWhichTypeIsArrayTypeIsAppliedToArrayComponent() { - configureFromFileWithMyAnnotationAdded( + configureFromFileWithAnnotationsAdded( """ class AClass { void foo() { - @MyAnnotation String[] array; + @A String[] array; } } """); - assertTypeAnnotations(getPsiTypeElementAtCaret().getType(), "MyAnnotation"); + assertTypeAnnotations(getPsiTypeElementAtCaret().getType(), "A"); } public void testTypeUseAnnotationOnVariableWhichTypeIsArrayTypeIsNotAppliedToArrayType() { - configureFromFileWithMyAnnotationAdded( + configureFromFileWithAnnotationsAdded( """ class AClass { void foo() { - @MyAnnotation String[] array; + @A String[] array; } } """); @@ -339,26 +432,26 @@ public class PsiTypeElementImplTest extends LightJavaCodeInsightTestCase { } public void testTypeUseAnnotationOnMultiCatchIsAppliedToFirstException() { - configureFromFileWithMyAnnotationAdded( + configureFromFileWithAnnotationsAdded( """ class AClass { public void foo() { try { - } catch (@MyAnnotation EOFException | FileNotFoundException | ObjectStreamException e) { + } catch (@A EOFException | FileNotFoundException | ObjectStreamException e) { } } } """); - assertTypeAnnotations(getPsiTypeElementAtCaret().getType(), "MyAnnotation"); + assertTypeAnnotations(getPsiTypeElementAtCaret().getType(), "A"); } public void testTypeUseAnnotationOnMultiCatchIsNotAppliedToMiddleException() { - configureFromFileWithMyAnnotationAdded( + configureFromFileWithAnnotationsAdded( """ class AClass { public void foo() { try { - } catch (@MyAnnotation EOFException | FileNotFoundException | ObjectStreamException e) { + } catch (@A EOFException | FileNotFoundException | ObjectStreamException e) { } } } @@ -367,12 +460,12 @@ public class PsiTypeElementImplTest extends LightJavaCodeInsightTestCase { } public void testTypeUseAnnotationOnMultiCatchIsNotAppliedToLastException() { - configureFromFileWithMyAnnotationAdded( + configureFromFileWithAnnotationsAdded( """ class AClass { public void foo() { try { - } catch (@MyAnnotation EOFException | FileNotFoundException | ObjectStreamException e) { + } catch (@A EOFException | FileNotFoundException | ObjectStreamException e) { } } } @@ -381,17 +474,17 @@ public class PsiTypeElementImplTest extends LightJavaCodeInsightTestCase { } public void testTypeUseAnnotationOnSingleCatchIsAppliedToException() { - configureFromFileWithMyAnnotationAdded( + configureFromFileWithAnnotationsAdded( """ class AClass { public void foo() { try { - } catch (@MyAnnotation EOFException e) { + } catch (@A EOFException e) { } } } """); - assertTypeAnnotations(getPsiTypeElementAtCaret().getType(), "MyAnnotation"); + assertTypeAnnotations(getPsiTypeElementAtCaret().getType(), "A"); } private static void assertTypeAnnotations(PsiType psiType, String... annotation) { @@ -413,7 +506,7 @@ public class PsiTypeElementImplTest extends LightJavaCodeInsightTestCase { return psiElement; } - private void configureFromFileWithMyAnnotationAdded(String classFile) { + private void configureFromFileWithAnnotationsAdded(String classFile) { configureFromFileText( "AClass.java", """ @@ -424,7 +517,9 @@ public class PsiTypeElementImplTest extends LightJavaCodeInsightTestCase { import java.util.List; @Target(ElementType.TYPE_USE) - public @interface MyAnnotation {} + public @interface A {} + @Target(ElementType.TYPE_USE) + public @interface B {} """ + classFile );