From 311fc75ff8789ef84f905eda0c9329e72f695f64 Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Tue, 18 Apr 2017 11:40:54 +0200 Subject: [PATCH] [java] PsiClassRefType returns annotations embedded in a ref (IDEA-140234, IDEA-166062) --- .../intellij/codeInsight/AnnotationUtil.java | 5 +++ .../impl/source/PsiClassReferenceType.java | 37 +++++++++++++++++-- .../codeInsight/OverrideImplementTest.groovy | 11 ++++-- .../codeInsight/psi/AnnotatedTypeTest.groovy | 13 ++++++- 4 files changed, 58 insertions(+), 8 deletions(-) diff --git a/java/java-psi-api/src/com/intellij/codeInsight/AnnotationUtil.java b/java/java-psi-api/src/com/intellij/codeInsight/AnnotationUtil.java index 3373879cb6bd..f56a7628fa06 100644 --- a/java/java-psi-api/src/com/intellij/codeInsight/AnnotationUtil.java +++ b/java/java-psi-api/src/com/intellij/codeInsight/AnnotationUtil.java @@ -288,6 +288,11 @@ public class AnnotationUtil { PsiAnnotation annotation = modifierList.findAnnotation(annotationFQN); if (annotation != null) return true; + PsiType type = null; + if (listOwner instanceof PsiMethod) type = ((PsiMethod)listOwner).getReturnType(); + else if (listOwner instanceof PsiVariable) type = ((PsiVariable)listOwner).getType(); + if (type != null && type.findAnnotation(annotationFQN) != null) return true; + if (!skipExternal) { final Project project = listOwner.getProject(); if (ExternalAnnotationsManager.getInstance(project).findExternalAnnotation(listOwner, annotationFQN) != null || 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 06dc0c3c6df7..8a460a3eac80 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 @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,10 +23,13 @@ import com.intellij.psi.impl.light.LightClassReference; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.util.PsiUtil; import com.intellij.psi.util.PsiUtilCore; +import com.intellij.util.ArrayUtil; import com.intellij.util.ObjectUtils; import com.intellij.util.SmartList; +import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; +import java.util.LinkedHashSet; import java.util.List; /** @@ -82,6 +85,34 @@ public class PsiClassReferenceType extends PsiClassType.Stub { return getReference().getResolveScope(); } + @NotNull + @Override + public PsiAnnotation[] getAnnotations() { + return getAnnotations(true); + } + + private PsiAnnotation[] getAnnotations(boolean merge) { + PsiAnnotation[] annotations = super.getAnnotations(); + + if (merge) { + PsiJavaCodeReferenceElement reference = myReference.compute(); + if (reference != null && reference.isValid() && reference.isQualified()) { + PsiAnnotation[] embedded = collectAnnotations(reference); + if (annotations.length > 0 && embedded.length > 0) { + LinkedHashSet set = ContainerUtil.newLinkedHashSet(); + ContainerUtil.addAll(set, annotations); + ContainerUtil.addAll(set, embedded); + annotations = set.toArray(PsiAnnotation.EMPTY_ARRAY); + } + else { + annotations = ArrayUtil.mergeArrays(annotations, embedded); + } + } + } + + return annotations; + } + @Override @NotNull public LanguageLevel getLanguageLevel() { @@ -198,7 +229,7 @@ public class PsiClassReferenceType extends PsiClassType.Stub { public String getPresentableText(boolean annotated) { String presentableText = PsiNameHelper.getPresentableText(getReference()); - PsiAnnotation[] annotations = annotated ? getAnnotations() : PsiAnnotation.EMPTY_ARRAY; + PsiAnnotation[] annotations = annotated ? getAnnotations(false) : PsiAnnotation.EMPTY_ARRAY; if (annotations.length == 0) return presentableText; StringBuilder sb = new StringBuilder(); @@ -223,7 +254,7 @@ public class PsiClassReferenceType extends PsiClassType.Stub { PsiJavaCodeReferenceElement reference = getReference(); if (reference instanceof PsiAnnotatedJavaCodeReferenceElement) { PsiAnnotatedJavaCodeReferenceElement ref = (PsiAnnotatedJavaCodeReferenceElement)reference; - PsiAnnotation[] annotations = annotated ? getAnnotations() : PsiAnnotation.EMPTY_ARRAY; + PsiAnnotation[] annotations = annotated ? getAnnotations(false) : PsiAnnotation.EMPTY_ARRAY; return ref.getCanonicalText(annotated, annotations.length == 0 ? null : annotations); } return reference.getCanonicalText(); diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/OverrideImplementTest.groovy b/java/java-tests/testSrc/com/intellij/codeInsight/OverrideImplementTest.groovy index 16b2032f446e..75b1275aee97 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/OverrideImplementTest.groovy +++ b/java/java-tests/testSrc/com/intellij/codeInsight/OverrideImplementTest.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -126,7 +126,7 @@ class Main2 { } } """ - + def file = myFixture.addClass("""\ class B implements Main1.I, Main2.I { @@ -171,9 +171,9 @@ class Test implements A { } """).containingFile.virtualFile myFixture.configureFromExistingVirtualFile(file) - + invokeAction(true) - + myFixture.checkResult """\ package bar; class Test implements A { @@ -188,6 +188,9 @@ class Test implements A { } void testTypeAnnotationsInImplementedMethod() { + def handler = new OverrideImplementsAnnotationsHandler() { @Override String[] getAnnotations(Project project) { return ["TA"] } } + PlatformTestUtil.registerExtension(OverrideImplementsAnnotationsHandler.EP_NAME, handler, testRootDisposable) + myFixture.addClass """\ import java.lang.annotation.*; @Target(ElementType.TYPE_USE) diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/psi/AnnotatedTypeTest.groovy b/java/java-tests/testSrc/com/intellij/codeInsight/psi/AnnotatedTypeTest.groovy index a649f3fbf678..970de71334a8 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/psi/AnnotatedTypeTest.groovy +++ b/java/java-tests/testSrc/com/intellij/codeInsight/psi/AnnotatedTypeTest.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,6 +15,7 @@ */ package com.intellij.codeInsight.psi +import com.intellij.codeInsight.AnnotationUtil import com.intellij.pom.java.LanguageLevel import com.intellij.psi.* import com.intellij.psi.impl.source.PsiImmediateClassType @@ -104,6 +105,16 @@ class AnnotatedTypeTest extends LightCodeInsightFixtureTestCase { assertAnnotations psi.returnType, "@TA(1)", "@TA(2)" } + void testIsAnnotated() { + def unqualified = factory.createParameterFromText("@A @TA(1) String p", context) + assert AnnotationUtil.isAnnotated(unqualified, "pkg.A", false) + assert AnnotationUtil.isAnnotated(unqualified, "pkg.TA", false) + + def qualified = factory.createParameterFromText("@A java.lang.@TA(1) String p", context) + assert AnnotationUtil.isAnnotated(qualified, "pkg.A", false) + assert AnnotationUtil.isAnnotated(qualified, "pkg.TA", false) + } + private void doTest(String text, String annotated, String canonical) { assertTypeText(factory.createParameterFromText(text, context).type, annotated, canonical) }