[java-highlighting] Provide fixes for the "Duplicate attribute" error (IDEA-216119)

GitOrigin-RevId: ae3db3461abcfa2578b50657ec99dc51b0bbd699
This commit is contained in:
Andrey.Cherkasov
2021-09-27 23:00:56 +03:00
committed by intellij-monorepo-bot
parent 1a10d8214d
commit 7815b148f2
21 changed files with 162 additions and 5 deletions

View File

@@ -547,4 +547,7 @@ public abstract class QuickFixFactory {
public abstract IntentionAction createDeleteDefaultFix(@NotNull PsiFile file, @Nullable Object highlightInfo);
public abstract @NotNull IntentionAction createAddAnnotationTargetFix(@NotNull PsiAnnotation annotation, PsiAnnotation.TargetType target);
@Nullable
public abstract IntentionAction createMergeDuplicateAttributesFix(@NotNull PsiNameValuePair pair);
}

View File

@@ -104,7 +104,11 @@ public final class AnnotationsHighlightUtil {
if (Objects.equals(attribute.getName(), name)) {
String description = JavaErrorBundle.message("annotation.duplicate.attribute",
name == null ? PsiAnnotation.DEFAULT_REFERENCED_METHOD_NAME : name);
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(pair).descriptionAndTooltip(description).create();
HighlightInfo info =
HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(pair).descriptionAndTooltip(description).create();
QuickFixAction.registerQuickFixAction(info, QUICK_FIX_FACTORY.createDeleteFix(pair));
QuickFixAction.registerQuickFixAction(info, QUICK_FIX_FACTORY.createMergeDuplicateAttributesFix(pair));
return info;
}
}
@@ -680,7 +684,7 @@ public final class AnnotationsHighlightUtil {
PsiClass container = getRepeatableContainer(annotation);
if (container == null) return null;
PsiMethod[] methods = !container.isAnnotationType() ? PsiMethod.EMPTY_ARRAY
PsiMethod[] methods = !container.isAnnotationType() ? PsiMethod.EMPTY_ARRAY
: container.findMethodsByName("value", false);
if (methods.length == 0) {
return JavaErrorBundle.message("annotation.container.no.value", container.getQualifiedName());
@@ -709,11 +713,11 @@ public final class AnnotationsHighlightUtil {
if (repeatableTargets.contains(containerTarget)) {
continue;
}
if (containerTarget == PsiAnnotation.TargetType.ANNOTATION_TYPE &&
if (containerTarget == PsiAnnotation.TargetType.ANNOTATION_TYPE &&
(repeatableTargets.contains(PsiAnnotation.TargetType.TYPE) || repeatableTargets.contains(PsiAnnotation.TargetType.TYPE_USE))) {
continue;
}
if ((containerTarget == PsiAnnotation.TargetType.TYPE || containerTarget == PsiAnnotation.TargetType.TYPE_PARAMETER) &&
if ((containerTarget == PsiAnnotation.TargetType.TYPE || containerTarget == PsiAnnotation.TargetType.TYPE_PARAMETER) &&
repeatableTargets.contains(PsiAnnotation.TargetType.TYPE_USE)) {
continue;
}

View File

@@ -413,4 +413,6 @@ unwrap.array.initializer.fix=Replace array initializer with its element
replace.with.type.pattern.fix=Replace with type pattern
add.annotation.target.fix=Add the ''{0}'' target
add.annotation.target.family=Add annotation target
add.annotation.target.family=Add annotation target
merge.duplicate.attributes.family=Merge duplicate attributes

View File

@@ -0,0 +1,78 @@
// 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.daemon.QuickFixBundle;
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.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.siyeh.ig.psiutils.CommentTracker;
import one.util.streamex.StreamEx;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Objects;
import java.util.StringJoiner;
public class MergeDuplicateAttributesFix extends LocalQuickFixAndIntentionActionOnPsiElement {
public MergeDuplicateAttributesFix(PsiElement element) {
super(element);
}
@Override
public void invoke(@NotNull Project project,
@NotNull PsiFile file,
@Nullable Editor editor,
@NotNull PsiElement startElement,
@NotNull PsiElement endElement) {
final PsiNameValuePair pair = (PsiNameValuePair)startElement;
final PsiAnnotationParameterList parameterList = (PsiAnnotationParameterList)pair.getParent();
final PsiNameValuePair[] attributes = parameterList.getAttributes();
final StringJoiner joiner = new StringJoiner(", ");
final String name = pair.getName();
final boolean[] isFirstValue = {true};
StreamEx.of(attributes).filterBy(PsiNameValuePair::getName, name).map(attribute -> attribute.getValue()).filter(Objects::nonNull)
.forEach(value -> {
CommentTracker ct = new CommentTracker();
if (value instanceof PsiArrayInitializerMemberValue) {
if (((PsiArrayInitializerMemberValue)value).getInitializers().length == 0 && !isFirstValue[0]) {
new CommentTracker().deleteAndRestoreComments(value.getParent());
return;
}
else {
final PsiElement lBrace = value.getFirstChild();
final PsiElement lastChild = value.getLastChild();
PsiElement maybeTrailingComma = PsiTreeUtil.skipWhitespacesAndCommentsBackward(lastChild);
if (PsiUtil.isJavaToken(maybeTrailingComma, JavaTokenType.COMMA)) {
maybeTrailingComma = maybeTrailingComma.getPrevSibling();
}
if (maybeTrailingComma == null) return;
joiner.add(ct.rangeText(lBrace.getNextSibling(), maybeTrailingComma));
}
}
else {
joiner.add(value.getText());
}
if (!isFirstValue[0]) {
ct.deleteAndRestoreComments(value.getParent());
}
isFirstValue[0] = false;
});
final PsiAnnotation dummyAnnotation = JavaPsiFacade.getElementFactory(project).createAnnotationFromText("@A({" + joiner + "})", null);
final PsiAnnotationMemberValue mergedValue = dummyAnnotation.getParameterList().getAttributes()[0].getValue();
final PsiAnnotation annotation = (PsiAnnotation)parameterList.getParent();
annotation.setDeclaredAttributeValue(pair.getName(), mergedValue);
}
@Override
public @NotNull String getText() {
return getFamilyName();
}
@Override
public @NotNull String getFamilyName() {
return QuickFixBundle.message("merge.duplicate.attributes.family");
}
}

View File

@@ -48,6 +48,7 @@ import com.intellij.psi.util.PropertyMemberType;
import com.intellij.refactoring.memberPushDown.JavaPushDownHandler;
import com.intellij.util.DocumentUtil;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.ObjectUtils;
import com.siyeh.ig.controlflow.UnnecessaryDefaultInspection;
import com.siyeh.ig.fixes.CreateDefaultBranchFix;
import com.siyeh.ig.fixes.CreateEnumMissingSwitchBranchesFix;
@@ -1112,4 +1113,16 @@ public final class QuickFixFactoryImpl extends QuickFixFactory {
@NotNull PsiAnnotation.TargetType target) {
return new AddAnnotationTargetFix(annotation, target);
}
@Override
@Nullable
public IntentionAction createMergeDuplicateAttributesFix(@NotNull PsiNameValuePair pair) {
final PsiReference reference = pair.getReference();
if (reference == null) return null;
final PsiMethod resolved = ObjectUtils.tryCast(reference.resolve(), PsiMethod.class);
if (resolved == null) return null;
final PsiType returnType = resolved.getReturnType();
if (!(returnType instanceof PsiArrayType)) return null;
return new MergeDuplicateAttributesFix(pair);
}
}

View File

@@ -0,0 +1,3 @@
// "Merge duplicate attributes" "true"
@SuppressWarnings(value= {"a", "b", "c"})
class Main { }

View File

@@ -0,0 +1,3 @@
// "Merge duplicate attributes" "true"
@SuppressWarnings(/*1*/value/*2*/=/*3*/{"a", "b", /*12*/"c"/*13*/, /*14*/"d"/*15*/}/*4*/ /*5*//*6*//*7*//*8*/ /*9*//*10*//*11*//*16*//*17*/)
class Main { }

View File

@@ -0,0 +1,3 @@
// "Merge duplicate attributes" "true"
@SuppressWarnings(value= {"a", "b", "c"})
class Main { }

View File

@@ -0,0 +1,3 @@
// "Merge duplicate attributes" "true"
@SuppressWarnings(value= {"a", "b", "c"})
class Main { }

View File

@@ -0,0 +1,3 @@
// "Merge duplicate attributes" "true"
@SuppressWarnings(value= {"a", "b", "c"})
class Main { }

View File

@@ -0,0 +1,3 @@
// "Merge duplicate attributes" "true"
@SuppressWarnings(value= {"a", "b", "c"})
class Main { }

View File

@@ -0,0 +1,3 @@
// "Merge duplicate attributes" "true"
@SuppressWarnings(value={"a"}, value={"b"}<caret>, value={"c"})
class Main { }

View File

@@ -0,0 +1,3 @@
// "Merge duplicate attributes" "true"
@SuppressWarnings(/*1*/value/*2*/=/*3*/"a"/*4*/, /*5*/value/*6*/=/*7*/"b"/*8*/, /*9*/value/*10*/=/*11*/{/*12*/"c"/*13*/, /*14*/"d"/*15*/, /*16*/<caret>}/*17*/)
class Main { }

View File

@@ -0,0 +1,3 @@
// "Merge duplicate attributes" "true"
@SuppressWarnings(value={"a"}, value="b"<caret>, value={"c"})
class Main { }

View File

@@ -0,0 +1,3 @@
// "Merge duplicate attributes" "true"
@SuppressWarnings(value="a", value={"b"}<caret>, value="c")
class Main { }

View File

@@ -0,0 +1,3 @@
// "Merge duplicate attributes" "false"
@Deprecated(since = "1.2", since = "1.2"<caret>)
class Main { }

View File

@@ -0,0 +1,3 @@
// "Merge duplicate attributes" "true"
@SuppressWarnings(value="a", value="b"<caret>, value="c")
class Main { }

View File

@@ -0,0 +1,3 @@
// "Merge duplicate attributes" "true"
@SuppressWarnings(value={"a",}, value={"b", }<caret>, value={"c", })
class Main { }

View File

@@ -0,0 +1,3 @@
// "Remove element" "true"
@SuppressWarnings(value = "1")
class Main { }

View File

@@ -0,0 +1,3 @@
// "Remove element" "true"
@SuppressWarnings(value = "1", value = "2"<caret>)
class Main { }

View File

@@ -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 MergeDuplicateAttributesFixTest extends LightQuickFixParameterizedTestCase {
@Override
protected String getBasePath() {
return "/codeInsight/daemonCodeAnalyzer/quickFix/mergeDuplicates";
}
}