mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-05-06 05:10:22 +07:00
IDEA-195069 Quick Fix: "Transform to mutable collection" in response to "Immutable object is modified" inspection
This commit is contained in:
@@ -331,17 +331,21 @@ public class DataFlowInspectionBase extends AbstractBaseJavaLocalInspectionTool
|
||||
});
|
||||
}
|
||||
|
||||
private static void reportMutabilityViolations(ProblemsHolder holder,
|
||||
private void reportMutabilityViolations(ProblemsHolder holder,
|
||||
Set<PsiElement> reportedAnchors,
|
||||
Set<PsiElement> violations,
|
||||
String message) {
|
||||
for (PsiElement violation : violations) {
|
||||
if (reportedAnchors.add(violation)) {
|
||||
holder.registerProblem(violation, message);
|
||||
holder.registerProblem(violation, message, createMutabilityViolationFix(holder, violation));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected LocalQuickFix createMutabilityViolationFix(ProblemsHolder holder, PsiElement violation) {
|
||||
return null;
|
||||
}
|
||||
|
||||
private void reportNullabilityProblems(ProblemsHolder holder,
|
||||
DataFlowInstructionVisitor visitor,
|
||||
HashSet<PsiElement> reportedAnchors) {
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
// Copyright 2000-2018 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.codeInspection;
|
||||
|
||||
import com.intellij.codeInsight.daemon.impl.analysis.HighlightControlFlowUtil;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.PsiDiamondTypeUtil;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.util.ObjectUtils;
|
||||
import com.siyeh.ig.psiutils.CommentTracker;
|
||||
import com.siyeh.ig.psiutils.ExpressionUtils;
|
||||
import com.siyeh.ig.psiutils.HighlightUtils;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.Nls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class WrapWithMutableCollectionFix implements LocalQuickFix {
|
||||
private final String myVariableName;
|
||||
private final String myCollectionName;
|
||||
private final boolean myOnTheFly;
|
||||
|
||||
public WrapWithMutableCollectionFix(String variableName, String collectionName, boolean onTheFly) {
|
||||
myVariableName = variableName;
|
||||
myCollectionName = collectionName;
|
||||
myOnTheFly = onTheFly;
|
||||
}
|
||||
|
||||
@Nls(capitalization = Nls.Capitalization.Sentence)
|
||||
@NotNull
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Wrap '"+myVariableName+"' with '" + StringUtil.getShortName(myCollectionName) + "'";
|
||||
}
|
||||
|
||||
@Nls(capitalization = Nls.Capitalization.Sentence)
|
||||
@NotNull
|
||||
@Override
|
||||
public String getFamilyName() {
|
||||
return "Wrap with mutable collection";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
|
||||
PsiLocalVariable variable = getVariable(descriptor.getStartElement());
|
||||
if (variable == null) return;
|
||||
PsiExpression initializer = variable.getInitializer();
|
||||
if (initializer == null) return;
|
||||
PsiClassType type = ObjectUtils.tryCast(variable.getType(), PsiClassType.class);
|
||||
if (type == null) return;
|
||||
String typeParameters = "";
|
||||
if (myCollectionName.equals(CommonClassNames.JAVA_UTIL_HASH_MAP)) {
|
||||
PsiType keyParameter = PsiUtil.substituteTypeParameter(type, CommonClassNames.JAVA_UTIL_MAP, 0, false);
|
||||
PsiType valueParameter = PsiUtil.substituteTypeParameter(type, CommonClassNames.JAVA_UTIL_MAP, 1, false);
|
||||
if (keyParameter != null && valueParameter != null) {
|
||||
typeParameters = "<"+keyParameter.getCanonicalText()+","+valueParameter.getCanonicalText()+">";
|
||||
}
|
||||
} else {
|
||||
PsiType elementParameter = PsiUtil.substituteTypeParameter(type, CommonClassNames.JAVA_LANG_ITERABLE, 0, false);
|
||||
if (elementParameter != null) {
|
||||
typeParameters = "<" + elementParameter.getCanonicalText() + ">";
|
||||
}
|
||||
}
|
||||
CommentTracker ct = new CommentTracker();
|
||||
PsiElement replacement =
|
||||
ct.replaceAndRestoreComments(initializer, "new " + myCollectionName + typeParameters + "(" + ct.text(initializer) + ")");
|
||||
PsiDiamondTypeUtil.removeRedundantTypeArguments(replacement);
|
||||
if (myOnTheFly) {
|
||||
HighlightUtils.highlightElement(replacement);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static WrapWithMutableCollectionFix createFix(@NotNull PsiElement anchor, boolean onTheFly) {
|
||||
PsiLocalVariable variable = getVariable(anchor);
|
||||
if (variable == null) return null;
|
||||
PsiExpression initializer = variable.getInitializer();
|
||||
if (initializer == null) return null;
|
||||
String wrapper = getWrapperByType(variable.getType());
|
||||
if (wrapper == null) return null;
|
||||
PsiElement block = PsiUtil.getVariableCodeBlock(variable, null);
|
||||
if (block == null) return null;
|
||||
if (!HighlightControlFlowUtil.isEffectivelyFinal(variable, block, null)) return null;
|
||||
return new WrapWithMutableCollectionFix(variable.getName(), wrapper, onTheFly);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiLocalVariable getVariable(@NotNull PsiElement anchor) {
|
||||
if (anchor.getParent() instanceof PsiReferenceExpression && anchor.getParent().getParent() instanceof PsiCallExpression) {
|
||||
anchor = ((PsiReferenceExpression)anchor.getParent()).getQualifierExpression();
|
||||
}
|
||||
if (!(anchor instanceof PsiExpression)) return null;
|
||||
return ExpressionUtils.resolveLocalVariable(PsiUtil.skipParenthesizedExprDown((PsiExpression)anchor));
|
||||
}
|
||||
|
||||
@Contract("null -> null")
|
||||
@Nullable
|
||||
private static String getWrapperByType(PsiType type) {
|
||||
if(!(type instanceof PsiClassType)) return null;
|
||||
PsiClass aClass = ((PsiClassType)type).resolve();
|
||||
if (aClass == null) return null;
|
||||
String name = aClass.getQualifiedName();
|
||||
if (name == null) return null;
|
||||
switch (name) {
|
||||
case CommonClassNames.JAVA_LANG_ITERABLE:
|
||||
case CommonClassNames.JAVA_UTIL_COLLECTION:
|
||||
case CommonClassNames.JAVA_UTIL_LIST: return CommonClassNames.JAVA_UTIL_ARRAY_LIST;
|
||||
case CommonClassNames.JAVA_UTIL_SET: return CommonClassNames.JAVA_UTIL_HASH_SET;
|
||||
case CommonClassNames.JAVA_UTIL_MAP:
|
||||
return CommonClassNames.JAVA_UTIL_HASH_MAP;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -69,6 +69,11 @@ public class DataFlowInspection extends DataFlowInspectionBase {
|
||||
return new ReplaceWithTrivialLambdaFix(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected LocalQuickFix createMutabilityViolationFix(ProblemsHolder holder, PsiElement violation) {
|
||||
return WrapWithMutableCollectionFix.createFix(violation, holder.isOnTheFly());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected LocalQuickFix createIntroduceVariableFix(final PsiExpression expression) {
|
||||
return new IntroduceVariableFix(true);
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
// "Wrap 'map' with 'HashMap'" "true"
|
||||
import java.util.*;
|
||||
|
||||
class Test {
|
||||
void testComparator() {
|
||||
Map<String, Integer> map = new HashMap<>(Math.random() > 0.5 ? Collections.emptyMap() : Collections.singletonMap("foo", 1));
|
||||
map.put(123, 456);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
// "Wrap 'integers' with 'HashSet'" "true"
|
||||
import java.util.*;
|
||||
|
||||
class Test {
|
||||
void testComparator() {
|
||||
var integers = new HashSet<>(Set.of(4, 3, 2, 1));
|
||||
integers.add(123);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
// "Wrap 'integers' with 'ArrayList'" "true"
|
||||
import java.util.*;
|
||||
|
||||
class Test {
|
||||
void testComparator() {
|
||||
List<Integer> integers = new ArrayList<>(List.of(4, 3, 2, 1));
|
||||
integers.sort(Comparator.naturalOrder());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
// "Wrap 'map' with 'HashMap'" "true"
|
||||
import java.util.*;
|
||||
|
||||
class Test {
|
||||
void testComparator() {
|
||||
Map<String, Integer> map = Math.random() > 0.5 ? Collections.emptyMap() : Collections.singletonMap("foo", 1);
|
||||
map.p<caret>ut(123, 456);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
// "Wrap 'integers' with 'HashSet'" "true"
|
||||
import java.util.*;
|
||||
|
||||
class Test {
|
||||
void testComparator() {
|
||||
var integers = Set.of(4, 3, 2, 1);
|
||||
integers.a<caret>dd(123);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
// "Wrap 'integers' with 'ArrayList'" "true"
|
||||
import java.util.*;
|
||||
|
||||
class Test {
|
||||
void testComparator() {
|
||||
List<Integer> integers = List.of(4, 3, 2, 1);
|
||||
integers.so<caret>rt(Comparator.naturalOrder());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
// Copyright 2000-2018 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.daemon.quickFix;
|
||||
|
||||
import com.intellij.codeInsight.daemon.quickFix.LightQuickFixParameterizedTestCase;
|
||||
import com.intellij.codeInspection.LocalInspectionTool;
|
||||
import com.intellij.codeInspection.dataFlow.DataFlowInspection;
|
||||
import com.intellij.openapi.projectRoots.Sdk;
|
||||
import com.intellij.testFramework.IdeaTestUtil;
|
||||
import com.intellij.testFramework.LightProjectDescriptor;
|
||||
import com.intellij.testFramework.PsiTestUtil;
|
||||
import com.intellij.testFramework.fixtures.DefaultLightProjectDescriptor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class WrapWithMutableCollectionFixTest extends LightQuickFixParameterizedTestCase {
|
||||
private static final DefaultLightProjectDescriptor PROJECT_DESCRIPTOR = new DefaultLightProjectDescriptor() {
|
||||
@Override
|
||||
public Sdk getSdk() {
|
||||
return PsiTestUtil.addJdkAnnotations(IdeaTestUtil.getMockJdk9());
|
||||
}
|
||||
};
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected LocalInspectionTool[] configureLocalInspectionTools() {
|
||||
return new LocalInspectionTool[]{new DataFlowInspection()};
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected LightProjectDescriptor getProjectDescriptor() {
|
||||
return PROJECT_DESCRIPTOR;
|
||||
}
|
||||
|
||||
public void test() {
|
||||
doAllTests();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getBasePath() {
|
||||
return "/codeInsight/daemonCodeAnalyzer/quickFix/wrapWithMutable";
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user