diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/intention/AddAnnotationPsiFix.java b/java/java-analysis-impl/src/com/intellij/codeInsight/intention/AddAnnotationPsiFix.java index dbdac8a1a360..0d05c9bb0d9c 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/intention/AddAnnotationPsiFix.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/intention/AddAnnotationPsiFix.java @@ -1,7 +1,10 @@ // Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package com.intellij.codeInsight.intention; -import com.intellij.codeInsight.*; +import com.intellij.codeInsight.AnnotationTargetUtil; +import com.intellij.codeInsight.AnnotationUtil; +import com.intellij.codeInsight.ExternalAnnotationsManager; +import com.intellij.codeInsight.NullableNotNullManager; import com.intellij.codeInsight.daemon.impl.analysis.AnnotationsHighlightUtil; import com.intellij.codeInsight.intention.impl.BaseIntentionAction; import com.intellij.codeInspection.LocalQuickFixOnPsiElement; @@ -21,6 +24,7 @@ import com.intellij.util.ArrayUtilRt; import com.intellij.util.ObjectUtils; import com.siyeh.ig.psiutils.CommentTracker; import one.util.streamex.StreamEx; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -204,10 +208,53 @@ public class AddAnnotationPsiFix extends LocalQuickFixOnPsiElement { return annotationsManager.chooseAnnotationsPlaceNoUi(modifierListOwner); } + /** + * @deprecated use {@link #addPhysicalAnnotationIfAbsent(String, PsiNameValuePair[], PsiAnnotationOwner)} + */ + @Deprecated + @ApiStatus.ScheduledForRemoval(inVersion = "2020.3") public static PsiAnnotation addPhysicalAnnotation(String fqn, PsiNameValuePair[] pairs, PsiModifierList modifierList) { return addPhysicalAnnotationTo(fqn, pairs, modifierList); } + /** + * Add new physical (non-external) annotation to the annotation owner. Annotation will not be added if it already exists + * on the same annotation owner (externally or explicitly) or if there's a {@link PsiTypeElement} that follows the owner, + * and its innermost component type has the annotation with the same fully-qualified name. + * E.g. the method like {@code java.lang.@Foo String[] getStringArray()} will not be annotated with another {@code @Foo} + * annotation. + * + * @param fqn fully-qualified annotation name + * @param pairs name/value pairs for the new annotation (not changed by this method, + * could be result of {@link PsiAnnotationParameterList#getAttributes()} of existing annotation). + * @param owner an owner object to add the annotation to ({@link PsiModifierList} or {@link PsiType}). + * @return added physical annotation; null if annotation already exists (in this case, no changes are performed) + */ + @Nullable + public static PsiAnnotation addPhysicalAnnotationIfAbsent(@NotNull String fqn, + @NotNull PsiNameValuePair @NotNull [] pairs, + @NotNull PsiAnnotationOwner owner) { + if (owner.hasAnnotation(fqn)) return null; + if (owner instanceof PsiModifierList) { + PsiElement modListOwner = ((PsiModifierList)owner).getParent(); + if (modListOwner instanceof PsiModifierListOwner) { + if (ExternalAnnotationsManager.getInstance(modListOwner.getProject()) + .findExternalAnnotation((PsiModifierListOwner)modListOwner, fqn) != null) { + return null; + } + PsiTypeElement typeElement = modListOwner instanceof PsiMethod ? ((PsiMethod)modListOwner).getReturnTypeElement() : + modListOwner instanceof PsiVariable ? ((PsiVariable)modListOwner).getTypeElement() : null; + while (typeElement != null && typeElement.getType() instanceof PsiArrayType) { + typeElement = PsiTreeUtil.getChildOfType(typeElement, PsiTypeElement.class); + } + if (typeElement != null && typeElement.getType().hasAnnotation(fqn)) { + return null; + } + } + } + return addPhysicalAnnotationTo(fqn, pairs, owner); + } + public static PsiAnnotation addPhysicalAnnotationTo(String fqn, PsiNameValuePair[] pairs, PsiAnnotationOwner owner) { owner = expandParameterIfNecessary(owner); PsiAnnotation inserted = owner.addAnnotation(fqn); diff --git a/java/java-impl/src/com/intellij/codeInsight/generation/GenerateMembersUtil.java b/java/java-impl/src/com/intellij/codeInsight/generation/GenerateMembersUtil.java index 17a201e2e667..07778a8c8c53 100644 --- a/java/java-impl/src/com/intellij/codeInsight/generation/GenerateMembersUtil.java +++ b/java/java-impl/src/com/intellij/codeInsight/generation/GenerateMembersUtil.java @@ -615,13 +615,7 @@ public class GenerateMembersUtil { } 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); + AddAnnotationPsiFix.addPhysicalAnnotationIfAbsent(qualifiedName, annotation.getParameterList().getAttributes(), target); } } diff --git a/java/java-impl/src/com/intellij/codeInsight/generation/OverrideImplementUtil.java b/java/java-impl/src/com/intellij/codeInsight/generation/OverrideImplementUtil.java index 4fde88e84adf..68269218bee3 100644 --- a/java/java-impl/src/com/intellij/codeInsight/generation/OverrideImplementUtil.java +++ b/java/java-impl/src/com/intellij/codeInsight/generation/OverrideImplementUtil.java @@ -2,7 +2,6 @@ package com.intellij.codeInsight.generation; import com.intellij.application.options.CodeStyle; -import com.intellij.codeInsight.AnnotationUtil; import com.intellij.codeInsight.CodeInsightActionHandler; import com.intellij.codeInsight.MethodImplementor; import com.intellij.codeInsight.intention.AddAnnotationFix; @@ -241,10 +240,7 @@ public class OverrideImplementUtil extends OverrideImplementExploreUtil { @NotNull PsiMethod overridden, boolean insertOverride) { if (insertOverride && canInsertOverride(overridden, targetClass)) { - final String overrideAnnotationName = Override.class.getName(); - if (!AnnotationUtil.isAnnotated(method, overrideAnnotationName, 0)) { - AddAnnotationPsiFix.addPhysicalAnnotation(overrideAnnotationName, PsiNameValuePair.EMPTY_ARRAY, method.getModifierList()); - } + AddAnnotationPsiFix.addPhysicalAnnotationIfAbsent(Override.class.getName(), PsiNameValuePair.EMPTY_ARRAY, method.getModifierList()); } OverrideImplementsAnnotationsHandler.repeatAnnotationsFromSource(overridden, targetClass, method); } diff --git a/java/java-impl/src/com/intellij/codeInsight/generation/OverrideImplementsAnnotationsHandler.java b/java/java-impl/src/com/intellij/codeInsight/generation/OverrideImplementsAnnotationsHandler.java index 458fa04d1255..4e51a28bddad 100644 --- a/java/java-impl/src/com/intellij/codeInsight/generation/OverrideImplementsAnnotationsHandler.java +++ b/java/java-impl/src/com/intellij/codeInsight/generation/OverrideImplementsAnnotationsHandler.java @@ -78,6 +78,6 @@ public interface OverrideImplementsAnnotationsHandler { assert modifierList != null : target; PsiAnnotation srcAnnotation = AnnotationUtil.findAnnotation(source, annotation); PsiNameValuePair[] valuePairs = srcAnnotation != null ? srcAnnotation.getParameterList().getAttributes() : PsiNameValuePair.EMPTY_ARRAY; - AddAnnotationPsiFix.addPhysicalAnnotation(annotation, valuePairs, modifierList); + AddAnnotationPsiFix.addPhysicalAnnotationIfAbsent(annotation, valuePairs, modifierList); } } \ No newline at end of file diff --git a/java/java-impl/src/com/intellij/refactoring/extractMethod/ExtractMethodProcessor.java b/java/java-impl/src/com/intellij/refactoring/extractMethod/ExtractMethodProcessor.java index 254a0e2cb3a7..c75caceb0eed 100644 --- a/java/java-impl/src/com/intellij/refactoring/extractMethod/ExtractMethodProcessor.java +++ b/java/java-impl/src/com/intellij/refactoring/extractMethod/ExtractMethodProcessor.java @@ -1716,8 +1716,10 @@ public class ExtractMethodProcessor implements MatchProvider { AddAnnotationPsiFix.removePhysicalAnnotations(owner, ArrayUtilRt.toStringArray(toRemove)); PsiModifierList modifierList = owner.getModifierList(); if (modifierList != null && !AnnotationUtil.isAnnotated(owner, toKeep, CHECK_TYPE)) { - PsiAnnotation annotation = AddAnnotationPsiFix.addPhysicalAnnotation(toAdd, PsiNameValuePair.EMPTY_ARRAY, modifierList); - JavaCodeStyleManager.getInstance(myProject).shortenClassReferences(annotation); + PsiAnnotation annotation = AddAnnotationPsiFix.addPhysicalAnnotationIfAbsent(toAdd, PsiNameValuePair.EMPTY_ARRAY, modifierList); + if (annotation != null) { + JavaCodeStyleManager.getInstance(myProject).shortenClassReferences(annotation); + } } } diff --git a/java/java-impl/src/com/intellij/refactoring/extractMethod/newImpl/ExtractMethodHelper.kt b/java/java-impl/src/com/intellij/refactoring/extractMethod/newImpl/ExtractMethodHelper.kt index e539145b74cd..084c6ab6e1c1 100644 --- a/java/java-impl/src/com/intellij/refactoring/extractMethod/newImpl/ExtractMethodHelper.kt +++ b/java/java-impl/src/com/intellij/refactoring/extractMethod/newImpl/ExtractMethodHelper.kt @@ -76,8 +76,10 @@ object ExtractMethodHelper { else -> return } val modifierList = owner.modifierList ?: return - val annotationElement = AddAnnotationPsiFix.addPhysicalAnnotation(annotation, PsiNameValuePair.EMPTY_ARRAY, modifierList) - JavaCodeStyleManager.getInstance(owner.project).shortenClassReferences(annotationElement) + val annotationElement = AddAnnotationPsiFix.addPhysicalAnnotationIfAbsent(annotation, PsiNameValuePair.EMPTY_ARRAY, modifierList) + if (annotationElement != null) { + JavaCodeStyleManager.getInstance(owner.project).shortenClassReferences(annotationElement) + } } private fun findVariableReferences(element: PsiElement): Sequence { diff --git a/java/java-tests/testData/codeInsight/delegateMethods/afterTypeUseAnnotationsInArrayParameter.java b/java/java-tests/testData/codeInsight/delegateMethods/afterTypeUseAnnotationsInArrayParameter.java index 0365b1179bc4..fc2815edad54 100644 --- a/java/java-tests/testData/codeInsight/delegateMethods/afterTypeUseAnnotationsInArrayParameter.java +++ b/java/java-tests/testData/codeInsight/delegateMethods/afterTypeUseAnnotationsInArrayParameter.java @@ -1,4 +1,9 @@ -import org.jetbrains.annotations.NotNull; +package org.jetbrains.annotations; + +import java.lang.annotation.*; + +@Target({ElementType.PARAMETER, ElementType.TYPE_USE}) +@interface NotNull {} class Test1 { void m(@NotNull String... strings) { } diff --git a/java/java-tests/testData/codeInsight/delegateMethods/beforeTypeUseAnnotationsInArrayParameter.java b/java/java-tests/testData/codeInsight/delegateMethods/beforeTypeUseAnnotationsInArrayParameter.java index d9d4aa62a0c5..b263c01f4d33 100644 --- a/java/java-tests/testData/codeInsight/delegateMethods/beforeTypeUseAnnotationsInArrayParameter.java +++ b/java/java-tests/testData/codeInsight/delegateMethods/beforeTypeUseAnnotationsInArrayParameter.java @@ -1,4 +1,9 @@ -import org.jetbrains.annotations.NotNull; +package org.jetbrains.annotations; + +import java.lang.annotation.*; + +@Target({ElementType.PARAMETER, ElementType.TYPE_USE}) +@interface NotNull {} class Test1 { void m(@NotNull String... strings) { }