diff --git a/java/java-analysis-api/src/com/intellij/codeInsight/intention/QuickFixFactory.java b/java/java-analysis-api/src/com/intellij/codeInsight/intention/QuickFixFactory.java index 38143183e3a7..986b9fd4412b 100644 --- a/java/java-analysis-api/src/com/intellij/codeInsight/intention/QuickFixFactory.java +++ b/java/java-analysis-api/src/com/intellij/codeInsight/intention/QuickFixFactory.java @@ -545,4 +545,6 @@ public abstract class QuickFixFactory { @Nullable public abstract IntentionAction createDeleteDefaultFix(@NotNull PsiFile file, @Nullable Object highlightInfo); + + public abstract @NotNull IntentionAction createAddAnnotationTargetFix(@NotNull PsiAnnotation annotation, PsiAnnotation.TargetType target); } \ No newline at end of file diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/AnnotationsHighlightUtil.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/AnnotationsHighlightUtil.java index 7c9d526bae71..65abf84362ba 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/AnnotationsHighlightUtil.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/AnnotationsHighlightUtil.java @@ -396,7 +396,13 @@ public final class AnnotationsHighlightUtil { if (applicable == null) { String target = JavaAnalysisBundle.message("annotation.target." + targets[0]); String message = JavaErrorBundle.message("annotation.not.applicable", nameRef.getText(), target); - return annotationError(annotation, message); + HighlightInfo info = annotationError(annotation, message); + if (Objects.requireNonNull(annotation.resolveAnnotationType()).isWritable()) { + for (PsiAnnotation.TargetType targetType : targets) { + QuickFixAction.registerQuickFixAction(info, QUICK_FIX_FACTORY.createAddAnnotationTargetFix(annotation, targetType)); + } + } + return info; } if (applicable == PsiAnnotation.TargetType.TYPE_USE) { diff --git a/java/java-analysis-impl/src/messages/QuickFixBundle.properties b/java/java-analysis-impl/src/messages/QuickFixBundle.properties index 95ccfb800d41..e06891b22cbc 100644 --- a/java/java-analysis-impl/src/messages/QuickFixBundle.properties +++ b/java/java-analysis-impl/src/messages/QuickFixBundle.properties @@ -410,4 +410,7 @@ implement.or.extend.fix.extend.text=Extend ''{0}'' seal.class.from.permits.list.fix=Seal inheritor unwrap.array.initializer.fix=Replace array initializer with its element -replace.with.type.pattern.fix=Replace with type pattern \ No newline at end of file +replace.with.type.pattern.fix=Replace with type pattern + +add.annotation.target.fix=Add the ''{0}'' target +add.annotation.target.family=Add annotation target \ No newline at end of file diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/AddAnnotationTargetFix.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/AddAnnotationTargetFix.java new file mode 100644 index 000000000000..302ebcedb6d0 --- /dev/null +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/AddAnnotationTargetFix.java @@ -0,0 +1,66 @@ +// Copyright 2000-2021 JetBrains s.r.o. and contributors. 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.daemon.impl.quickfix; + +import com.intellij.codeInsight.AnnotationUtil; +import com.intellij.codeInsight.daemon.QuickFixBundle; +import com.intellij.codeInsight.intention.IntentionAction; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.project.Project; +import com.intellij.psi.*; +import com.intellij.psi.codeStyle.JavaCodeStyleManager; +import com.intellij.util.IncorrectOperationException; +import org.jetbrains.annotations.NotNull; + +public class AddAnnotationTargetFix implements IntentionAction { + @NotNull private final PsiAnnotation myAnnotation; + @NotNull private final PsiAnnotation.TargetType myTarget; + + public AddAnnotationTargetFix(@NotNull PsiAnnotation annotation, @NotNull PsiAnnotation.TargetType target) { + myAnnotation = annotation; + myTarget = target; + } + + @Override + public @NotNull String getText() { + return QuickFixBundle.message("add.annotation.target.fix", myTarget); + } + + @Override + public @NotNull String getFamilyName() { + return QuickFixBundle.message("add.annotation.target.family"); + } + + @Override + public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) { + return true; + } + + @Override + public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException { + final PsiClass annotationType = myAnnotation.resolveAnnotationType(); + if (annotationType == null) return; + final PsiModifierList modifierList = annotationType.getModifierList(); + if (modifierList == null) return; + final PsiAnnotation annotation = modifierList.findAnnotation(CommonClassNames.JAVA_LANG_ANNOTATION_TARGET); + if (annotation == null) return; + final PsiNameValuePair attribute = AnnotationUtil.findDeclaredAttribute(annotation, null); + if (attribute == null) return; + PsiAnnotationMemberValue value = attribute.getValue(); + if (value == null) return; + if (!(value instanceof PsiArrayInitializerMemberValue)) { + PsiAnnotation dummyAnnotation = + JavaPsiFacade.getElementFactory(project).createAnnotationFromText("@A({" + value.getText() + "})", null); + final PsiAnnotationMemberValue wrappedValue = dummyAnnotation.getParameterList().getAttributes()[0].getValue(); + value = annotation.setDeclaredAttributeValue("value", wrappedValue); + } + final PsiElementFactory factory = JavaPsiFacade.getElementFactory(project); + final PsiExpression expression = factory.createExpressionFromText("java.lang.annotation.ElementType." + myTarget, value); + JavaCodeStyleManager.getInstance(project).shortenClassReferences(expression); + value.addAfter(expression, value.getFirstChild()); + } + + @Override + public boolean startInWriteAction() { + return true; + } +} diff --git a/java/java-impl/src/com/intellij/codeInsight/intention/impl/config/QuickFixFactoryImpl.java b/java/java-impl/src/com/intellij/codeInsight/intention/impl/config/QuickFixFactoryImpl.java index 6a7e22ca5a20..ce1a7e4b28aa 100644 --- a/java/java-impl/src/com/intellij/codeInsight/intention/impl/config/QuickFixFactoryImpl.java +++ b/java/java-impl/src/com/intellij/codeInsight/intention/impl/config/QuickFixFactoryImpl.java @@ -1105,4 +1105,11 @@ public final class QuickFixFactoryImpl extends QuickFixFactory { if (descriptor == null) return null; return new LocalQuickFixAsIntentionAdapter(new UnnecessaryDefaultInspection.DeleteDefaultFix(), descriptor); } + + + @Override + public @NotNull IntentionAction createAddAnnotationTargetFix(@NotNull PsiAnnotation annotation, + @NotNull PsiAnnotation.TargetType target) { + return new AddAnnotationTargetFix(annotation, target); + } } diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addAnnotationTarget/afterEmptyArray1.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addAnnotationTarget/afterEmptyArray1.java new file mode 100644 index 000000000000..a956abf846f4 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addAnnotationTarget/afterEmptyArray1.java @@ -0,0 +1,10 @@ +// "Add the 'FIELD' target" "true" +import java.lang.annotation.ElementType; +import java.lang.annotation.Target; + +@Target({ElementType.FIELD}) +@interface Foo {} + +class Main { + @Foo int x = 42; +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addAnnotationTarget/afterEmptyArray2.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addAnnotationTarget/afterEmptyArray2.java new file mode 100644 index 000000000000..9bb2dee205ea --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addAnnotationTarget/afterEmptyArray2.java @@ -0,0 +1,10 @@ +// "Add the 'FIELD' target" "true" +import java.lang.annotation.ElementType; +import java.lang.annotation.Target; + +@Target({ElementType.FIELD,}) +@interface Foo {} + +class Main { + @Foo int x = 42; +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addAnnotationTarget/afterNotArray.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addAnnotationTarget/afterNotArray.java new file mode 100644 index 000000000000..93b488417f9b --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addAnnotationTarget/afterNotArray.java @@ -0,0 +1,10 @@ +// "Add the 'FIELD' target" "true" +import java.lang.annotation.ElementType; +import java.lang.annotation.Target; + +@Target({ElementType.FIELD, ElementType.METHOD}) +@interface Foo {} + +class Main { + @Foo int x = 42; +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addAnnotationTarget/afterNotEmptyArray.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addAnnotationTarget/afterNotEmptyArray.java new file mode 100644 index 000000000000..93b488417f9b --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addAnnotationTarget/afterNotEmptyArray.java @@ -0,0 +1,10 @@ +// "Add the 'FIELD' target" "true" +import java.lang.annotation.ElementType; +import java.lang.annotation.Target; + +@Target({ElementType.FIELD, ElementType.METHOD}) +@interface Foo {} + +class Main { + @Foo int x = 42; +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addAnnotationTarget/beforeAnnotationNotFromProjectCode.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addAnnotationTarget/beforeAnnotationNotFromProjectCode.java new file mode 100644 index 000000000000..31dddba25de7 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addAnnotationTarget/beforeAnnotationNotFromProjectCode.java @@ -0,0 +1,4 @@ +// "Add the 'FIELD' target" "false" +class Main { + @Override int x = 42; +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addAnnotationTarget/beforeEmptyArray1.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addAnnotationTarget/beforeEmptyArray1.java new file mode 100644 index 000000000000..e6b9f723ada2 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addAnnotationTarget/beforeEmptyArray1.java @@ -0,0 +1,10 @@ +// "Add the 'FIELD' target" "true" +import java.lang.annotation.ElementType; +import java.lang.annotation.Target; + +@Target({}) +@interface Foo {} + +class Main { + @Foo int x = 42; +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addAnnotationTarget/beforeEmptyArray2.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addAnnotationTarget/beforeEmptyArray2.java new file mode 100644 index 000000000000..90ab10c5618e --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addAnnotationTarget/beforeEmptyArray2.java @@ -0,0 +1,10 @@ +// "Add the 'FIELD' target" "true" +import java.lang.annotation.ElementType; +import java.lang.annotation.Target; + +@Target({,}) +@interface Foo {} + +class Main { + @Foo int x = 42; +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addAnnotationTarget/beforeNotArray.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addAnnotationTarget/beforeNotArray.java new file mode 100644 index 000000000000..6621831f025f --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addAnnotationTarget/beforeNotArray.java @@ -0,0 +1,10 @@ +// "Add the 'FIELD' target" "true" +import java.lang.annotation.ElementType; +import java.lang.annotation.Target; + +@Target(ElementType.METHOD) +@interface Foo {} + +class Main { + @Foo int x = 42; +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addAnnotationTarget/beforeNotEmptyArray.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addAnnotationTarget/beforeNotEmptyArray.java new file mode 100644 index 000000000000..3c0c77f400e8 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addAnnotationTarget/beforeNotEmptyArray.java @@ -0,0 +1,10 @@ +// "Add the 'FIELD' target" "true" +import java.lang.annotation.ElementType; +import java.lang.annotation.Target; + +@Target({ElementType.METHOD}) +@interface Foo {} + +class Main { + @Foo int x = 42; +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/quickFix/AddAnnotationTargetFixTest.java b/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/quickFix/AddAnnotationTargetFixTest.java new file mode 100644 index 000000000000..9a664c8822d5 --- /dev/null +++ b/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/quickFix/AddAnnotationTargetFixTest.java @@ -0,0 +1,12 @@ +// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package com.intellij.java.codeInsight.daemon.quickFix; + +import com.intellij.codeInsight.daemon.quickFix.LightQuickFixParameterizedTestCase; + +public class AddAnnotationTargetFixTest extends LightQuickFixParameterizedTestCase { + + @Override + protected String getBasePath() { + return "/codeInsight/daemonCodeAnalyzer/quickFix/addAnnotationTarget"; + } +}