mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Java: more accurate preview for "Method parameter always has the same value" inspection
and add test GitOrigin-RevId: 957be987fbc9414e1d099ee86c72193bcfc43c19
This commit is contained in:
committed by
intellij-monorepo-bot
parent
63d274bf59
commit
d8ba8bd567
+74
-77
@@ -4,13 +4,14 @@ package com.intellij.codeInspection.sameParameterValue;
|
||||
import com.intellij.analysis.AnalysisScope;
|
||||
import com.intellij.codeInsight.daemon.impl.UnusedSymbolUtil;
|
||||
import com.intellij.codeInsight.intention.preview.IntentionPreviewInfo;
|
||||
import com.intellij.codeInsight.intention.preview.IntentionPreviewUtils;
|
||||
import com.intellij.codeInsight.options.JavaInspectionControls;
|
||||
import com.intellij.codeInspection.*;
|
||||
import com.intellij.codeInspection.deadCode.UnusedDeclarationInspectionBase;
|
||||
import com.intellij.codeInspection.options.OptPane;
|
||||
import com.intellij.codeInspection.reference.*;
|
||||
import com.intellij.java.JavaBundle;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.application.WriteAction;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.progress.EmptyProgressIndicator;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
@@ -18,6 +19,7 @@ import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Comparing;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.search.LocalSearchScope;
|
||||
import com.intellij.psi.search.searches.OverridingMethodsSearch;
|
||||
import com.intellij.psi.search.searches.ReferencesSearch;
|
||||
import com.intellij.psi.util.PsiFormatUtil;
|
||||
@@ -65,10 +67,6 @@ public class SameParameterValueInspection extends GlobalJavaBatchInspectionTool
|
||||
);
|
||||
}
|
||||
|
||||
protected LocalQuickFix createFix(String paramName, String value) {
|
||||
return new InlineParameterValueFix(paramName, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonProblemDescriptor @Nullable [] checkElement(@NotNull RefEntity refEntity,
|
||||
@NotNull AnalysisScope scope,
|
||||
@@ -76,7 +74,7 @@ public class SameParameterValueInspection extends GlobalJavaBatchInspectionTool
|
||||
@NotNull GlobalInspectionContext globalContext,
|
||||
@NotNull ProblemDescriptionsProcessor processor) {
|
||||
List<ProblemDescriptor> problems = null;
|
||||
if (refEntity instanceof final RefMethod refMethod) {
|
||||
if (refEntity instanceof RefMethod refMethod) {
|
||||
|
||||
if (refMethod.hasSuperMethods() ||
|
||||
VisibilityUtil.compare(refMethod.getAccessModifier(), highestModifier) < 0 ||
|
||||
@@ -109,13 +107,8 @@ public class SameParameterValueInspection extends GlobalJavaBatchInspectionTool
|
||||
@Nullable
|
||||
@Contract("null -> null; !null -> !null")
|
||||
private static List<Object> valueToList(@Nullable Object rootValue) {
|
||||
if (rootValue == null) return null;
|
||||
if (rootValue instanceof List<?>) {
|
||||
//noinspection unchecked
|
||||
return (List<Object>)rootValue;
|
||||
} else {
|
||||
return new SmartList<>(rootValue);
|
||||
}
|
||||
//noinspection unchecked
|
||||
return rootValue == null || rootValue instanceof List<?> ? (List<Object>)rootValue : new SmartList<>(rootValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -161,9 +154,7 @@ public class SameParameterValueInspection extends GlobalJavaBatchInspectionTool
|
||||
if (hint == null) return null;
|
||||
final int spaceIdx = hint.indexOf(' ');
|
||||
if (spaceIdx == -1 || spaceIdx >= hint.length() - 1) return null; //invalid hint
|
||||
final String paramName = hint.substring(0, spaceIdx);
|
||||
final String value = hint.substring(spaceIdx + 1);
|
||||
return createFix(paramName, value);
|
||||
return new InlineParameterValueFix(hint.substring(0, spaceIdx), hint.substring(spaceIdx + 1));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -178,15 +169,14 @@ public class SameParameterValueInspection extends GlobalJavaBatchInspectionTool
|
||||
return new LocalSameParameterValueInspection(this);
|
||||
}
|
||||
|
||||
private @Nullable ProblemDescriptor registerProblem(@NotNull InspectionManager manager,
|
||||
@NotNull UParameter parameter,
|
||||
Object value,
|
||||
boolean suggestFix) {
|
||||
private static @Nullable ProblemDescriptor registerProblem(@NotNull InspectionManager manager,
|
||||
@NotNull UParameter parameter,
|
||||
Object value,
|
||||
boolean suggestFix) {
|
||||
final String name = parameter.getName();
|
||||
if (name == null || name.isEmpty()) return null;
|
||||
String shortName;
|
||||
String stringPresentation;
|
||||
|
||||
if (value instanceof PsiType) {
|
||||
stringPresentation = ((PsiType)value).getCanonicalText() + ".class";
|
||||
shortName = ((PsiType)value).getPresentableText() + ".class";
|
||||
@@ -209,12 +199,13 @@ public class SameParameterValueInspection extends GlobalJavaBatchInspectionTool
|
||||
}
|
||||
PsiElement anchor = ObjectUtils.notNull(UDeclarationKt.getAnchorPsi(parameter), parameter);
|
||||
if (!anchor.isPhysical()) return null;
|
||||
String value1 = stringPresentation.startsWith("\"\"")
|
||||
? stringPresentation
|
||||
: StringUtil.escapeLineBreak(stringPresentation);
|
||||
return manager.createProblemDescriptor(anchor,
|
||||
JavaBundle.message("inspection.same.parameter.problem.descriptor",
|
||||
StringUtil.unquoteString(shortName)),
|
||||
suggestFix ? createFix(name, stringPresentation.startsWith("\"\"")
|
||||
? stringPresentation
|
||||
: StringUtil.escapeLineBreak(stringPresentation)) : null,
|
||||
suggestFix ? new InlineParameterValueFix(name, value1) : null,
|
||||
ProblemHighlightType.GENERIC_ERROR_OR_WARNING, false);
|
||||
}
|
||||
|
||||
@@ -256,42 +247,22 @@ public class SameParameterValueInspection extends GlobalJavaBatchInspectionTool
|
||||
final PsiElement element = descriptor.getPsiElement();
|
||||
final PsiMethod method = PsiTreeUtil.getParentOfType(element, PsiMethod.class);
|
||||
if (method == null) return;
|
||||
PsiParameter parameter = findParameter(method, element);
|
||||
PsiParameter parameter = PsiTreeUtil.getParentOfType(element, PsiParameter.class, false);
|
||||
if (parameter == null) {
|
||||
parameter =
|
||||
ContainerUtil.find(method.getParameterList().getParameters(), (param) -> Comparing.strEqual(param.getName(), myParameterName));
|
||||
}
|
||||
if (parameter == null) return;
|
||||
final PsiExpression defToInline;
|
||||
try {
|
||||
defToInline = JavaPsiFacade.getElementFactory(project).createExpressionFromText(myValue, parameter);
|
||||
}
|
||||
catch (IncorrectOperationException e) {
|
||||
return;
|
||||
}
|
||||
inlineSameParameterValue(method, parameter, defToInline);
|
||||
final PsiExpression expression = JavaPsiFacade.getElementFactory(project).createExpressionFromText(myValue, parameter);
|
||||
inlineSameParameterValue(method, parameter, expression);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull IntentionPreviewInfo generatePreview(@NotNull Project project, @NotNull ProblemDescriptor previewDescriptor) {
|
||||
final PsiElement element = previewDescriptor.getPsiElement();
|
||||
final PsiMethod method = PsiTreeUtil.getParentOfType(element, PsiMethod.class);
|
||||
if (method == null) return IntentionPreviewInfo.EMPTY;
|
||||
final PsiParameter parameter = findParameterByName(method);
|
||||
if (parameter == null) return IntentionPreviewInfo.EMPTY;
|
||||
Collection<PsiReference> references = ReferencesSearch.search(parameter).findAll();
|
||||
final PsiExpression defToInline = JavaPsiFacade.getElementFactory(project).createExpressionFromText(myValue, parameter);
|
||||
references.forEach((ref) -> ref.getElement().replace(defToInline));
|
||||
parameter.delete();
|
||||
applyFix(project, previewDescriptor);
|
||||
return IntentionPreviewInfo.DIFF;
|
||||
}
|
||||
|
||||
private @Nullable PsiParameter findParameter(PsiMethod method, PsiElement descriptorElement) {
|
||||
PsiParameter parameter = PsiTreeUtil.getParentOfType(descriptorElement, PsiParameter.class, false);
|
||||
if (parameter == null) parameter = findParameterByName(method);
|
||||
return parameter;
|
||||
}
|
||||
|
||||
private @Nullable PsiParameter findParameterByName(PsiMethod method) {
|
||||
return ContainerUtil.find(method.getParameterList().getParameters(), (param) -> Comparing.strEqual(param.getName(), myParameterName));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean startInWriteAction() {
|
||||
return false;
|
||||
@@ -302,12 +273,17 @@ public class SameParameterValueInspection extends GlobalJavaBatchInspectionTool
|
||||
Collection<PsiMethod> methods = new ArrayList<>();
|
||||
methods.add(method);
|
||||
Project project = method.getProject();
|
||||
if (!ProgressManager.getInstance()
|
||||
.runProcessWithProgressSynchronously(() -> { methods.addAll(OverridingMethodsSearch.search(method).findAll()); },
|
||||
JavaBundle.message("progress.title.search.for.overriding.methods"), true, project)) {
|
||||
return;
|
||||
boolean preview = IntentionPreviewUtils.isIntentionPreviewActive();
|
||||
if (!preview) {
|
||||
if (!ProgressManager.getInstance().runProcessWithProgressSynchronously(
|
||||
() -> {
|
||||
methods.addAll(OverridingMethodsSearch.search(method).findAll());
|
||||
},
|
||||
JavaBundle.message("progress.title.search.for.overriding.methods"), true, project)) {
|
||||
return;
|
||||
}
|
||||
if (!CommonRefactoringUtil.checkReadOnlyStatus(project, methods, true)) return;
|
||||
}
|
||||
if (!CommonRefactoringUtil.checkReadOnlyStatus(project, methods, true)) return;
|
||||
|
||||
int parameterIndex = method.getParameterList().getParameterIndex(parameter);
|
||||
Map<PsiParameter, Collection<PsiReference>> paramsToInline = new HashMap<>();
|
||||
@@ -326,29 +302,50 @@ public class SameParameterValueInspection extends GlobalJavaBatchInspectionTool
|
||||
}
|
||||
if (!BaseRefactoringProcessor.processConflicts(project, conflicts)) return;
|
||||
|
||||
ApplicationManager.getApplication().runWriteAction(() -> {
|
||||
for (Map.Entry<PsiParameter, Collection<PsiReference>> entry : paramsToInline.entrySet()) {
|
||||
Collection<PsiReference> refsToInline = entry.getValue();
|
||||
try {
|
||||
PsiExpression[] exprs = new PsiExpression[refsToInline.size()];
|
||||
int idx = 0;
|
||||
for (PsiReference reference : refsToInline) {
|
||||
if (reference instanceof PsiJavaCodeReferenceElement) {
|
||||
exprs[idx++] = CommonJavaInlineUtil.getInstance().inlineVariable(entry.getKey(), defToInline, (PsiJavaCodeReferenceElement)reference, null);
|
||||
}
|
||||
if (preview) {
|
||||
inlineParameters(defToInline, paramsToInline);
|
||||
final boolean vararg = parameter.isVarArgs();
|
||||
parameter.delete();
|
||||
Collection<PsiReference> calls = ReferencesSearch.search(method, new LocalSearchScope(method.getContainingFile())).findAll();
|
||||
for (PsiReference call : calls) {
|
||||
PsiElement parent = call.getElement().getParent();
|
||||
if (parent instanceof PsiMethodCallExpression methodCallExpression) {
|
||||
PsiExpression[] arguments = methodCallExpression.getArgumentList().getExpressions();
|
||||
if (vararg) {
|
||||
methodCallExpression.deleteChildRange(arguments[parameterIndex], arguments[arguments.length - 1]);
|
||||
}
|
||||
|
||||
for (PsiExpression expr : exprs) {
|
||||
if (expr != null) CommonJavaRefactoringUtil.tryToInlineArrayCreationForVarargs(expr);
|
||||
else {
|
||||
arguments[parameterIndex].delete();
|
||||
}
|
||||
}
|
||||
catch (IncorrectOperationException e) {
|
||||
LOG.error(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
WriteAction.run(() -> inlineParameters(defToInline, paramsToInline));
|
||||
removeParameter(method, parameter);
|
||||
}
|
||||
}
|
||||
|
||||
removeParameter(method, parameter);
|
||||
private static void inlineParameters(PsiExpression defToInline, Map<PsiParameter, Collection<PsiReference>> paramsToInline) {
|
||||
for (Map.Entry<PsiParameter, Collection<PsiReference>> entry : paramsToInline.entrySet()) {
|
||||
Collection<PsiReference> refsToInline = entry.getValue();
|
||||
try {
|
||||
PsiExpression[] exprs = new PsiExpression[refsToInline.size()];
|
||||
int idx = 0;
|
||||
for (PsiReference reference : refsToInline) {
|
||||
if (reference instanceof PsiJavaCodeReferenceElement) {
|
||||
exprs[idx++] = CommonJavaInlineUtil.getInstance().inlineVariable(entry.getKey(), defToInline, (PsiJavaCodeReferenceElement)reference, null);
|
||||
}
|
||||
}
|
||||
|
||||
for (PsiExpression expr : exprs) {
|
||||
if (expr != null) CommonJavaRefactoringUtil.tryToInlineArrayCreationForVarargs(expr);
|
||||
}
|
||||
}
|
||||
catch (IncorrectOperationException e) {
|
||||
LOG.error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void removeParameter(PsiMethod method, PsiParameter parameter) {
|
||||
@@ -473,8 +470,8 @@ public class SameParameterValueInspection extends GlobalJavaBatchInspectionTool
|
||||
if (Boolean.FALSE.equals(isFixAvailable) && myGlobal.ignoreWhenRefactoringIsComplicated) return true;
|
||||
Object presentableValue = value;
|
||||
if (valueList != null && valueList.size() == 1 && valueList.get(0) == null) presentableValue = valueList.get(0);
|
||||
ProblemDescriptor descriptor =
|
||||
myGlobal.registerProblem(holder.getManager(), parameter, presentableValue, Boolean.TRUE.equals(isFixAvailable));
|
||||
ProblemDescriptor descriptor =
|
||||
registerProblem(holder.getManager(), parameter, presentableValue, Boolean.TRUE.equals(isFixAvailable));
|
||||
if (descriptor != null) {
|
||||
holder.registerProblem(descriptor);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
class Simple {
|
||||
|
||||
void x() {
|
||||
System.out.println(10);
|
||||
}
|
||||
|
||||
void y() {
|
||||
x();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
class Simple {
|
||||
|
||||
void x(int <warning descr="Actual value of parameter 'i' is always '10'"><caret>i</warning>) {
|
||||
System.out.println(i);
|
||||
}
|
||||
|
||||
void y() {
|
||||
x(10);
|
||||
}
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.java.codeInspection;
|
||||
|
||||
import com.intellij.JavaTestUtil;
|
||||
import com.intellij.codeInsight.intention.IntentionAction;
|
||||
import com.intellij.codeInspection.LocalInspectionTool;
|
||||
import com.intellij.codeInspection.sameParameterValue.SameParameterValueInspection;
|
||||
import com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Bas Leijdekkers
|
||||
*/
|
||||
public final class SameParameterValueQuickFixTest extends LightJavaCodeInsightFixtureTestCase {
|
||||
|
||||
public void testSimple() {
|
||||
doTest();
|
||||
}
|
||||
|
||||
private void doTest() {
|
||||
doNamedTest(getTestName(false));
|
||||
}
|
||||
|
||||
private void doNamedTest(String name) {
|
||||
LocalInspectionTool inspection = new SameParameterValueInspection().getSharedLocalInspectionTool();
|
||||
myFixture.enableInspections(inspection);
|
||||
myFixture.configureByFile(name + ".java");
|
||||
myFixture.testHighlighting(true, false, false);
|
||||
final @NotNull List<IntentionAction> intentions = myFixture.filterAvailableIntentions("Inline value");
|
||||
assertEquals("intention not found", 1, intentions.size());
|
||||
myFixture.checkPreviewAndLaunchAction(intentions.get(0));
|
||||
myFixture.checkResultByFile(name + ".after.java");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getTestDataPath() {
|
||||
return JavaTestUtil.getJavaTestDataPath() + "/inspection/sameParameterValueQuickFix";
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user