diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/sameParameterValue/SameParameterValueInspectionBase.java b/java/java-analysis-impl/src/com/intellij/codeInspection/sameParameterValue/SameParameterValueInspectionBase.java index 9c17a9f75a7f..f5a46f1734a0 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/sameParameterValue/SameParameterValueInspectionBase.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/sameParameterValue/SameParameterValueInspectionBase.java @@ -17,9 +17,13 @@ package com.intellij.codeInspection.sameParameterValue; import com.intellij.analysis.AnalysisScope; import com.intellij.codeInsight.daemon.GroupNames; +import com.intellij.codeInsight.daemon.impl.UnusedSymbolUtil; import com.intellij.codeInspection.*; +import com.intellij.codeInspection.deadCode.UnusedDeclarationInspectionBase; import com.intellij.codeInspection.reference.*; -import com.intellij.psi.PsiReference; +import com.intellij.openapi.progress.EmptyProgressIndicator; +import com.intellij.profile.codeInspection.InspectionProjectProfileManager; +import com.intellij.psi.*; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -51,11 +55,7 @@ public class SameParameterValueInspectionBase extends GlobalJavaBatchInspectionT if (value != null) { if (!globalContext.shouldCheck(refParameter, this)) continue; if (problems == null) problems = new ArrayList<>(1); - final String paramName = refParameter.getName(); - problems.add(manager.createProblemDescriptor(refParameter.getElement(), InspectionsBundle.message( - "inspection.same.parameter.problem.descriptor", "" + paramName + "", "" + value + ""), - createFix(paramName, value), - ProblemHighlightType.GENERIC_ERROR_OR_WARNING, false)); + problems.add(registerProblem(manager, refParameter.getElement(), value)); } } } @@ -63,7 +63,6 @@ public class SameParameterValueInspectionBase extends GlobalJavaBatchInspectionT return problems == null ? null : problems.toArray(new CommonProblemDescriptor[problems.size()]); } - @Override protected boolean queryExternalUsagesRequests(@NotNull final RefManager manager, @NotNull final GlobalJavaInspectionContext globalContext, @NotNull final ProblemDescriptionsProcessor processor) { @@ -118,7 +117,7 @@ public class SameParameterValueInspectionBase extends GlobalJavaBatchInspectionT } protected LocalQuickFix createFix(String paramName, String value) { - throw new UnsupportedOperationException(); + return null; } @Override @@ -127,4 +126,131 @@ public class SameParameterValueInspectionBase extends GlobalJavaBatchInspectionT return fix.toString(); } + @Nullable + @Override + public LocalInspectionTool getSharedLocalInspectionTool() { + return new LocalSameParameterValueInspection(this); + } + + private class LocalSameParameterValueInspection extends BaseJavaLocalInspectionTool { + private static final String NOT_CONST = "_NOT_CONST"; + + private final SameParameterValueInspectionBase myGlobal; + + private LocalSameParameterValueInspection(SameParameterValueInspectionBase global) { + myGlobal = global; + } + + @Override + @NotNull + public String getGroupDisplayName() { + return myGlobal.getGroupDisplayName(); + } + + @Override + @NotNull + public String getDisplayName() { + return myGlobal.getDisplayName(); + } + + @Override + @NotNull + public String getShortName() { + return myGlobal.getShortName(); + } + + @NotNull + @Override + public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, + boolean isOnTheFly, + @NotNull LocalInspectionToolSession session) { + return new JavaElementVisitor() { + private final UnusedDeclarationInspectionBase myDeadCodeTool; + + { + InspectionProfile profile = InspectionProjectProfileManager.getInstance(holder.getProject()).getCurrentProfile(); + UnusedDeclarationInspectionBase deadCodeTool = (UnusedDeclarationInspectionBase)profile.getUnwrappedTool(UnusedDeclarationInspectionBase.SHORT_NAME, holder.getFile()); + myDeadCodeTool = deadCodeTool == null ? new UnusedDeclarationInspectionBase() : deadCodeTool; + } + + @Override + public void visitMethod(PsiMethod method) { + if (method.isConstructor()) return; + PsiParameter[] parameters = method.getParameterList().getParameters(); + if (parameters.length == 0) return; + + if (myDeadCodeTool.isEntryPoint(method)) return; + if (!method.getHierarchicalMethodSignature().getSuperSignatures().isEmpty()) return; + + PsiParameter lastParameter = parameters[parameters.length - 1]; + final String[] paramValues; + final boolean hasVarArg = lastParameter.getType() instanceof PsiEllipsisType; + if (hasVarArg) { + if (parameters.length == 1) return; + paramValues = new String[parameters.length - 1]; + } else { + paramValues = new String[parameters.length]; + } + + if (UnusedSymbolUtil.processUsages(holder.getProject(), method.getContainingFile(), method, new EmptyProgressIndicator(), null, info -> { + PsiElement element = info.getElement(); + + if (!(element instanceof PsiReferenceExpression)) { + return false; + } + PsiElement parent = element.getParent(); + if (!(parent instanceof PsiMethodCallExpression)) { + return false; + } + PsiMethodCallExpression methodCall = (PsiMethodCallExpression) parent; + PsiExpression[] arguments = methodCall.getArgumentList().getExpressions(); + if (arguments.length < paramValues.length) return false; + + boolean needFurtherProcess = false; + for (int i = 0; i < paramValues.length; i++) { + Object value = paramValues[i]; + final String currentArg = getArgValue(arguments[i]); + if (value == null) { + paramValues[i] = currentArg; + if (currentArg != NOT_CONST) { + needFurtherProcess = true; + } + } else if (value != NOT_CONST) { + if (!paramValues[i].equals(currentArg)) { + paramValues[i] = NOT_CONST; + } else { + needFurtherProcess = true; + } + } + } + + return needFurtherProcess; + })) { + for (int i = 0, length = paramValues.length; i < length; i++) { + String value = paramValues[i]; + if (value != null && value != NOT_CONST) { + holder.registerProblem(registerProblem(holder.getManager(), parameters[i], value)); + } + } + } + } + }; + } + + private String getArgValue(PsiExpression arg) { + return arg instanceof PsiLiteralExpression ? arg.getText() : NOT_CONST; + } + } + + private ProblemDescriptor registerProblem(@NotNull InspectionManager manager, + PsiParameter parameter, + String value) { + final String name = parameter.getName(); + return manager.createProblemDescriptor(parameter, + InspectionsBundle.message("inspection.same.parameter.problem.descriptor", + "" + name + "", + "" + value + ""), + createFix(name, value), + ProblemHighlightType.GENERIC_ERROR_OR_WARNING, false); + } } diff --git a/java/java-tests/testData/inspection/sameParameterValue/methodWithSuper/expected.xml b/java/java-tests/testData/inspection/sameParameterValue/methodWithSuper/expected.xml new file mode 100644 index 000000000000..5b0b2c35f487 --- /dev/null +++ b/java/java-tests/testData/inspection/sameParameterValue/methodWithSuper/expected.xml @@ -0,0 +1,12 @@ + + + + Test.java + 4 + testMethodWithSuper + Actual method parameter is the same constant + Actual value of parameter '<code>i</code>' is always '<code>10</code>' + + + + diff --git a/java/java-tests/testData/inspection/sameParameterValue/methodWithSuper/src/Test.java b/java/java-tests/testData/inspection/sameParameterValue/methodWithSuper/src/Test.java new file mode 100644 index 000000000000..bf50ae40be5b --- /dev/null +++ b/java/java-tests/testData/inspection/sameParameterValue/methodWithSuper/src/Test.java @@ -0,0 +1,20 @@ +public class Test { + + static class AAA { + public void mmm(int i) { + System.out.println(i + 10); + } + } + + static class BBB extends AAA { + @Override + public void mmm(int i) { + System.out.println(i + 20); + } + } + + public static void main(String[] args) { + new BBB().mmm(10); + new AAA().mmm(10); + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInspection/SameParameterValueLocalTest.java b/java/java-tests/testSrc/com/intellij/codeInspection/SameParameterValueLocalTest.java new file mode 100644 index 000000000000..61d5eb9954e5 --- /dev/null +++ b/java/java-tests/testSrc/com/intellij/codeInspection/SameParameterValueLocalTest.java @@ -0,0 +1,45 @@ +/* + * Copyright 2000-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.codeInspection; + +import com.intellij.JavaTestUtil; +import com.intellij.codeInspection.sameParameterValue.SameParameterValueInspection; +import com.intellij.testFramework.InspectionTestCase; + +public class SameParameterValueLocalTest extends InspectionTestCase { + private final LocalInspectionTool myTool = new SameParameterValueInspection().getSharedLocalInspectionTool(); + + @Override + protected String getTestDataPath() { + return JavaTestUtil.getJavaTestDataPath() + "/inspection"; + } + + private String getGlobalTestDir() { + return "sameParameterValue/" + getTestName(true); + } + + public void testEntryPoint() { + doTest(getGlobalTestDir(), myTool); + } + + public void testMethodWithSuper() { + doTest(getGlobalTestDir(), myTool); + } + + public void testVarargs() { + doTest(getGlobalTestDir(), myTool); + } +} diff --git a/java/java-tests/testSrc/com/intellij/codeInspection/SameParameterValueTest.java b/java/java-tests/testSrc/com/intellij/codeInspection/SameParameterValueTest.java index 9f1890acbf0f..79a1be79e215 100644 --- a/java/java-tests/testSrc/com/intellij/codeInspection/SameParameterValueTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInspection/SameParameterValueTest.java @@ -16,19 +16,23 @@ public class SameParameterValueTest extends InspectionTestCase { return "sameParameterValue/" + getTestName(true); } - public void testEntryPoint() throws Exception { + public void testEntryPoint() { doTest(getTestDir(), myTool, false, true); } - public void testWithoutDeadCode() throws Exception { + public void testWithoutDeadCode() { doTest(getTestDir(), myTool, false, false); } - public void testVarargs() throws Exception { + public void testVarargs() { doTest(getTestDir(), myTool, false, true); } - public void testSimpleVararg() throws Exception { + public void testSimpleVararg() { + doTest(getTestDir(), myTool, false, true); + } + + public void testMethodWithSuper() { doTest(getTestDir(), myTool, false, true); } }