IDEA-167980 Add quick fix to merge two annotations which are not marked as repeatable

GitOrigin-RevId: 6cc5742354b484ea996ec6a0d2bfa29f3013a08a
This commit is contained in:
Tagir Valeev
2019-05-30 12:07:55 +03:00
committed by intellij-monorepo-bot
parent 73ed3aa311
commit 2ba9a65828
13 changed files with 197 additions and 2 deletions
@@ -511,4 +511,6 @@ public abstract class QuickFixFactory {
public abstract IntentionAction createAddMissingEnumBranchesFix(@NotNull PsiSwitchBlock switchBlock, @NotNull Set<String> missingCases);
public abstract IntentionAction createAddSwitchDefaultFix(@NotNull PsiSwitchBlock switchBlock, @Nullable String message);
public abstract IntentionAction createCollapseAnnotationsFix(PsiAnnotation annotation);
}
@@ -204,7 +204,10 @@ public class AnnotationsHighlightUtil {
if (metaAnno == null) {
String explanation = JavaErrorMessages.message("annotation.non.repeatable", annotationType.getQualifiedName());
String description = JavaErrorMessages.message("annotation.duplicate.explained", explanation);
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(element).descriptionAndTooltip(description).create();
HighlightInfo info =
HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(element).descriptionAndTooltip(description).create();
QuickFixAction.registerQuickFixAction(info, QuickFixFactory.getInstance().createCollapseAnnotationsFix(annotationToCheck));
return info;
}
String explanation = doCheckRepeatableAnnotation(metaAnno);
@@ -0,0 +1,124 @@
// 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.impl;
import com.intellij.codeInspection.LocalQuickFixAndIntentionActionOnPsiElement;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.ArrayUtil;
import com.intellij.util.containers.ContainerUtil;
import com.siyeh.ig.psiutils.CommentTracker;
import one.util.streamex.StreamEx;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
@SuppressWarnings("foo")
public class CollapseAnnotationsFix extends LocalQuickFixAndIntentionActionOnPsiElement {
private CollapseAnnotationsFix(PsiAnnotation annotation) {
super(annotation);
}
@NotNull
@Override
public String getText() {
return "Collapse repeating annotations";
}
int[] x;
@Override
public void invoke(@NotNull Project project,
@NotNull PsiFile file,
@Nullable Editor editor,
@NotNull PsiElement startElement,
@NotNull PsiElement endElement) {
if (!(startElement instanceof PsiAnnotation)) return;
PsiAnnotation annotation = (PsiAnnotation)startElement;
PsiNameValuePair attribute = ArrayUtil.getFirstElement(annotation.getParameterList().getAttributes());
if (attribute == null) return;
PsiAnnotationMemberValue origValue = attribute.getValue();
if (origValue == null) return;
List<PsiAnnotation> annotations = findCollapsibleAnnotations(annotation, attribute);
List<PsiAnnotationMemberValue> values = new ArrayList<>();
CommentTracker ct = new CommentTracker();
for (PsiAnnotation anno : annotations) {
PsiAnnotationMemberValue value = anno.getParameterList().getAttributes()[0].getValue();
if (value instanceof PsiArrayInitializerMemberValue) {
PsiAnnotationMemberValue[] initializers = ((PsiArrayInitializerMemberValue)value).getInitializers();
for (PsiAnnotationMemberValue initializer : initializers) {
values.add(ct.markUnchanged(initializer));
}
}
else if (value != null) {
values.add(ct.markUnchanged(value));
}
if (anno != annotation) {
ct.delete(anno);
}
}
String newValue = StreamEx.of(values).map(PsiElement::getText).joining(", ", "{", "}");
PsiAnnotation dummy = JavaPsiFacade.getElementFactory(project).createAnnotationFromText("@x(" + newValue + ")", origValue);
ct.replaceAndRestoreComments(origValue, Objects.requireNonNull(dummy.getParameterList().getAttributes()[0].getValue()));
}
@Nls(capitalization = Nls.Capitalization.Sentence)
@NotNull
@Override
public String getFamilyName() {
return getText();
}
private static List<PsiAnnotation> findCollapsibleAnnotations(PsiAnnotation annotation, PsiNameValuePair attribute) {
PsiAnnotationOwner owner = annotation.getOwner();
String name = annotation.getQualifiedName();
if (owner == null || name == null) {
return Collections.emptyList();
}
List<PsiAnnotation> annotations = new ArrayList<>();
for (PsiAnnotation other : owner.getAnnotations()) {
if (name.equals(other.getQualifiedName())) {
PsiNameValuePair[] otherAttributes = other.getParameterList().getAttributes();
if (otherAttributes.length == 1) {
PsiNameValuePair otherAttribute = otherAttributes[0];
if (otherAttribute.getAttributeName().equals(attribute.getAttributeName())) {
annotations.add(other);
}
}
}
}
return annotations;
}
@Nullable
public static CollapseAnnotationsFix from(PsiAnnotation annotation) {
PsiAnnotationOwner owner = annotation.getOwner();
String name = annotation.getQualifiedName();
PsiNameValuePair[] attributes = annotation.getParameterList().getAttributes();
if (owner == null || name == null || attributes.length != 1) return null;
PsiNameValuePair attribute = attributes[0];
if (attribute.getValue() == null) return null;
PsiMethod annoMethod = findAttributeMethod(attribute);
if (annoMethod == null || !(annoMethod.getReturnType() instanceof PsiArrayType)) return null;
List<PsiAnnotation> annotations = findCollapsibleAnnotations(annotation, attribute);
if (annotations.size() < 2) return null;
return new CollapseAnnotationsFix(annotation);
}
@Nullable
private static PsiMethod findAttributeMethod(PsiNameValuePair attribute) {
PsiReference ref = attribute.getReference();
if (ref == null) return null;
PsiElement target = ref.resolve();
if (PsiUtil.isAnnotationMethod(target)) {
return (PsiAnnotationMethod)target;
}
return null;
}
}
@@ -931,5 +931,10 @@ public class QuickFixFactoryImpl extends QuickFixFactory {
@Override
public IntentionAction createAddSwitchDefaultFix(@NotNull PsiSwitchBlock switchBlock, String message) {
return new CreateDefaultBranchFix(switchBlock, message);
}
}
@Override
public IntentionAction createCollapseAnnotationsFix(PsiAnnotation annotation) {
return CollapseAnnotationsFix.from(annotation);
}
}
@@ -0,0 +1,4 @@
// "Collapse repeating annotations" "true"
@SuppressWarnings({"foo", "bar", "baz"})
class X{}
@@ -0,0 +1,10 @@
// "Collapse repeating annotations" "true"
@XYZ(data = {1, 2, 3, 4})
@XYZ(5)
class X{}
@interface XYZ {
int value() default 0;
int[] data();
}
@@ -0,0 +1,4 @@
// "Collapse repeating annotations" "true"
@SuppressWarnings({"foo", "bar", "baz"})
class X{}
@@ -0,0 +1,4 @@
// "Collapse repeating annotations" "true"
@SuppressWarnings({"foo", "bar"})
class X{}
@@ -0,0 +1,5 @@
// "Collapse repeating annotations" "true"
@SuppressWarnings({"foo", "bar"})
@<caret>SuppressWarnings("baz")
class X{}
@@ -0,0 +1,12 @@
// "Collapse repeating annotations" "true"
@X<caret>YZ(data = 1)
@XYZ(data = {2, 3})
@XYZ(data = {4})
@XYZ(5)
class X{}
@interface XYZ {
int value() default 0;
int[] data();
}
@@ -0,0 +1,6 @@
// "Collapse repeating annotations" "true"
@<caret>SuppressWarnings("foo")
@SuppressWarnings("bar")
@SuppressWarnings("baz")
class X{}
@@ -0,0 +1,5 @@
// "Collapse repeating annotations" "true"
@<caret>SuppressWarnings("foo")
@SuppressWarnings("bar")
class X{}
@@ -0,0 +1,11 @@
// 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.java.codeInsight.intention;
import com.intellij.codeInsight.daemon.LightIntentionActionTestCase;
public class CollapseAnnotationsFixTest extends LightIntentionActionTestCase {
@Override
protected String getBasePath() {
return "/codeInsight/daemonCodeAnalyzer/quickFix/collapseAnnotationsFix";
}
}