GenerateMembersUtil#copyAnnotations: exclude annotations already copied to type (IDEA-236474)

Also: check that annotations resolve to the same type in target place.

GitOrigin-RevId: 0093c5966f37281b4f852e78a8cd5cd6667527f4
This commit is contained in:
Tagir Valeev
2020-04-07 10:04:02 +00:00
committed by intellij-monorepo-bot
parent 713cd98e68
commit 4db59b2205
6 changed files with 82 additions and 1 deletions
@@ -605,10 +605,22 @@ public class GenerateMembersUtil {
public static void copyAnnotations(@NotNull PsiModifierList source, @NotNull PsiModifierList target, String... skipAnnotations) {
for (PsiAnnotation annotation : source.getAnnotations()) {
String qualifiedName = annotation.getQualifiedName();
PsiJavaCodeReferenceElement ref = annotation.getNameReferenceElement();
if (ref == null) continue;
PsiClass oldClass = ObjectUtils.tryCast(ref.resolve(), PsiClass.class);
if (oldClass == null) continue;
String qualifiedName = oldClass.getQualifiedName();
if (qualifiedName == null || ArrayUtil.contains(qualifiedName, skipAnnotations) || target.hasAnnotation(qualifiedName)) {
continue;
}
PsiClass newClass = JavaPsiFacade.getInstance(target.getProject()).findClass(qualifiedName, target.getResolveScope());
if (newClass == null || !oldClass.getManager().areElementsEquivalent(oldClass, newClass)) continue;
PsiElement owner = target.getParent();
PsiType type = owner instanceof PsiMethod ? ((PsiMethod)owner).getReturnType() :
owner instanceof PsiVariable ? ((PsiVariable)owner).getType() : null;
if (type != null && type.hasAnnotation(qualifiedName)) {
continue;
}
AddAnnotationPsiFix.addPhysicalAnnotation(qualifiedName, annotation.getParameterList().getAttributes(), target);
}
}
@@ -0,0 +1,20 @@
import java.lang.annotation.*;
import java.util.Map;
@Target({ElementType.METHOD, ElementType.TYPE_USE})
@interface Foo {}
interface X {
@Foo Map.@Foo Entry getString();
}
static class Y implements X {
X x;
@Override
@Foo
public Map.@Foo Entry getString() {
return x.getString();
}
}
@@ -0,0 +1,12 @@
import pkg.Foo;
import java.io.Writer;
class Impl extends Abstract {
Abstract orig;
@Override
public @Foo Writer getWriter() {
return orig.getWriter();
}
}
@@ -0,0 +1,16 @@
import java.lang.annotation.*;
import java.util.Map;
@Target({ElementType.METHOD, ElementType.TYPE_USE})
@interface Foo {}
interface X {
@Foo Map.@Foo Entry getString();
}
static class Y implements X {
X x;
<caret>
}
@@ -0,0 +1,6 @@
class Impl extends Abstract {
Abstract orig;
<caret>
}
@@ -65,6 +65,21 @@ public class DelegateMethodsTest extends LightJavaCodeInsightTestCase {
public void testSingleField() { doTest(); }
public void testInsideLambdaWithNonInferredTypeParameters() { doTest(); }
public void testTypeUseAnnotationsInReturnType() { doTest(); }
public void testTypeUseAnnotationsInReturnType2() { doTest(); }
public void testTypeUseAnnotationsInReturnType3() {
createAndSaveFile("pkg/Foo.java",
"package pkg;\n" +
"import java.lang.annotation.*;\n" +
"@Target({ElementType.TYPE_USE})\n" +
"@interface Foo {}");
createAndSaveFile("Abstract.java",
"import java.io.Writer;\n" +
"import pkg.Foo;\n" +
"abstract class Abstract {\n" +
" @Foo public abstract Writer getWriter();\n" +
"}\n");
doTest();
}
public void testTypeUseAnnotationsInArrayParameter() { doTest(); }
public void testPreserveEllipsisType() { doTest(); }