actual method parameter is the same const inspection should work for negative numbers (IDEA-185830)

This commit is contained in:
Dmitry Batkovich
2018-02-05 17:46:58 +03:00
parent a130cd7885
commit db33a7203a
8 changed files with 93 additions and 54 deletions
@@ -25,6 +25,9 @@ import org.jetbrains.annotations.Nullable;
* @since 6.0
*/
public interface RefParameter extends RefJavaElement {
Object VALUE_IS_NOT_CONST = new Object();
Object VALUE_UNDEFINED = new Object();
/**
* Checks if the parameter is used for reading.
*
@@ -49,11 +52,11 @@ public interface RefParameter extends RefJavaElement {
/**
* If all invocations of the method pass the same value to the parameter, returns
* that value (the name of a static final field or the text of a literal expression).
* Otherwise, returns null.
* Otherwise, returns {@link RefParameter#VALUE_IS_NOT_CONST}.
*
* @return the parameter value or null if it's different or impossible to determine.
*/
@Nullable String getActualValueIfSame();
@Nullable Object getActualValueIfSame();
/**
* Marks the parameter as referenced for reading or writing.
@@ -19,6 +19,7 @@ package com.intellij.codeInspection.reference;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.util.Comparing;
import com.intellij.psi.*;
import com.intellij.psi.impl.JavaConstantExpressionEvaluator;
import com.intellij.psi.util.PsiFormatUtil;
import com.intellij.psi.util.PsiFormatUtilBase;
import com.intellij.psi.util.PsiTreeUtil;
@@ -28,10 +29,10 @@ import org.jetbrains.annotations.Nullable;
public class RefParameterImpl extends RefJavaElementImpl implements RefParameter {
private static final int USED_FOR_READING_MASK = 0x10000;
private static final int USED_FOR_WRITING_MASK = 0x20000;
private static final String VALUE_UNDEFINED = "#";
private final short myIndex;
private String myActualValueTemplate;
private Object myActualValueTemplate;
RefParameterImpl(PsiParameter parameter, int index, RefManager manager) {
super(parameter, manager);
@@ -100,37 +101,20 @@ public class RefParameterImpl extends RefJavaElementImpl implements RefParameter
}
void updateTemplateValue(PsiExpression expression) {
if (myActualValueTemplate == null) return;
String newTemplate = null;
if (expression instanceof PsiLiteralExpression) {
PsiLiteralExpression psiLiteralExpression = (PsiLiteralExpression) expression;
newTemplate = psiLiteralExpression.getText();
} else if (expression instanceof PsiReferenceExpression) {
PsiReferenceExpression referenceExpression = (PsiReferenceExpression) expression;
PsiElement resolved = referenceExpression.resolve();
if (resolved instanceof PsiField) {
PsiField psiField = (PsiField) resolved;
if (psiField.hasModifierProperty(PsiModifier.STATIC) &&
psiField.hasModifierProperty(PsiModifier.FINAL) &&
psiField.getContainingClass().getQualifiedName() != null) {
newTemplate = PsiFormatUtil.formatVariable(psiField, PsiFormatUtilBase.SHOW_NAME |
PsiFormatUtilBase.SHOW_CONTAINING_CLASS | PsiFormatUtilBase.SHOW_FQ_NAME, PsiSubstitutor.EMPTY);
}
}
}
if (myActualValueTemplate == VALUE_IS_NOT_CONST) return;
Object newTemplate = getExpressionValue(expression);
if (myActualValueTemplate == VALUE_UNDEFINED) {
myActualValueTemplate = newTemplate;
}
else if (!Comparing.equal(myActualValueTemplate, newTemplate)) {
myActualValueTemplate = null;
myActualValueTemplate = VALUE_IS_NOT_CONST;
}
}
@Nullable
@Override
public String getActualValueIfSame() {
if (myActualValueTemplate == VALUE_UNDEFINED) return null;
public Object getActualValueIfSame() {
return myActualValueTemplate;
}
@@ -152,6 +136,30 @@ public class RefParameterImpl extends RefJavaElementImpl implements RefParameter
return result[0];
}
@Nullable
public static Object getExpressionValue(PsiExpression expression) {
if (expression instanceof PsiReferenceExpression) {
PsiReferenceExpression referenceExpression = (PsiReferenceExpression) expression;
PsiElement resolved = referenceExpression.resolve();
if (resolved instanceof PsiField) {
PsiField psiField = (PsiField) resolved;
if (psiField.hasModifierProperty(PsiModifier.STATIC) &&
psiField.hasModifierProperty(PsiModifier.FINAL) &&
psiField.getContainingClass().getQualifiedName() != null) {
return PsiFormatUtil.formatVariable(psiField, PsiFormatUtilBase.SHOW_NAME |
PsiFormatUtilBase.SHOW_CONTAINING_CLASS |
PsiFormatUtilBase.SHOW_FQ_NAME,
PsiSubstitutor.EMPTY);
}
}
}
if (expression instanceof PsiLiteralExpression && ((PsiLiteralExpression)expression).getValue() == null) {
return null;
}
Object constValue = JavaConstantExpressionEvaluator.computeConstantExpression(expression, false);
return constValue == null ? VALUE_IS_NOT_CONST : constValue;
}
@Nullable
static RefElement parameterFromExternalName(final RefManager manager, final String fqName) {
final int idx = fqName.lastIndexOf(' ');
@@ -16,8 +16,12 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import static com.intellij.codeInspection.reference.RefParameter.VALUE_IS_NOT_CONST;
import static com.intellij.codeInspection.reference.RefParameter.VALUE_UNDEFINED;
/**
* @author max
*/
@@ -44,8 +48,8 @@ public class SameParameterValueInspectionBase extends GlobalJavaBatchInspectionT
RefParameter[] parameters = refMethod.getParameters();
for (RefParameter refParameter : parameters) {
String value = refParameter.getActualValueIfSame();
if (value != null) {
Object value = refParameter.getActualValueIfSame();
if (value != VALUE_IS_NOT_CONST && value != RefParameter.VALUE_UNDEFINED) {
if (!globalContext.shouldCheck(refParameter, this)) continue;
if (problems == null) problems = new ArrayList<>(1);
problems.add(registerProblem(manager, refParameter.getElement(), value, refParameter.isUsedForWriting()));
@@ -109,7 +113,7 @@ public class SameParameterValueInspectionBase extends GlobalJavaBatchInspectionT
return createFix(paramName, value);
}
protected LocalQuickFix createFix(String paramName, String value) {
protected LocalQuickFix createFix(String paramName, Object value) {
return null;
}
@@ -126,8 +130,6 @@ public class SameParameterValueInspectionBase extends GlobalJavaBatchInspectionT
}
private class LocalSameParameterValueInspection extends AbstractBaseJavaLocalInspectionTool {
private static final String NOT_CONST = "_NOT_CONST";
private final SameParameterValueInspectionBase myGlobal;
private LocalSameParameterValueInspection(SameParameterValueInspectionBase global) {
@@ -178,14 +180,15 @@ public class SameParameterValueInspectionBase extends GlobalJavaBatchInspectionT
if (!method.getHierarchicalMethodSignature().getSuperSignatures().isEmpty()) return;
PsiParameter lastParameter = parameters[parameters.length - 1];
final String[] paramValues;
final Object[] paramValues;
final boolean hasVarArg = lastParameter.getType() instanceof PsiEllipsisType;
if (hasVarArg) {
if (parameters.length == 1) return;
paramValues = new String[parameters.length - 1];
paramValues = new Object[parameters.length - 1];
} else {
paramValues = new String[parameters.length];
paramValues = new Object[parameters.length];
}
Arrays.fill(paramValues, VALUE_UNDEFINED);
if (UnusedSymbolUtil.processUsages(holder.getProject(), method.getContainingFile(), method, new EmptyProgressIndicator(), null, info -> {
PsiElement element = info.getElement();
@@ -204,15 +207,15 @@ public class SameParameterValueInspectionBase extends GlobalJavaBatchInspectionT
boolean needFurtherProcess = false;
for (int i = 0; i < paramValues.length; i++) {
Object value = paramValues[i];
final String currentArg = getArgValue(arguments[i]);
if (value == null) {
final Object currentArg = getArgValue(arguments[i]);
if (value == VALUE_UNDEFINED) {
paramValues[i] = currentArg;
if (currentArg != NOT_CONST) {
if (currentArg != VALUE_IS_NOT_CONST) {
needFurtherProcess = true;
}
} else if (value != NOT_CONST) {
} else if (value != VALUE_IS_NOT_CONST) {
if (!paramValues[i].equals(currentArg)) {
paramValues[i] = NOT_CONST;
paramValues[i] = VALUE_IS_NOT_CONST;
} else {
needFurtherProcess = true;
}
@@ -222,8 +225,8 @@ public class SameParameterValueInspectionBase extends GlobalJavaBatchInspectionT
return needFurtherProcess;
})) {
for (int i = 0, length = paramValues.length; i < length; i++) {
String value = paramValues[i];
if (value != null && value != NOT_CONST) {
Object value = paramValues[i];
if (value != VALUE_UNDEFINED && value != VALUE_IS_NOT_CONST) {
holder.registerProblem(registerProblem(holder.getManager(), parameters[i], value, false));
}
}
@@ -232,20 +235,20 @@ public class SameParameterValueInspectionBase extends GlobalJavaBatchInspectionT
};
}
private String getArgValue(PsiExpression arg) {
return arg instanceof PsiLiteralExpression ? arg.getText() : NOT_CONST;
private Object getArgValue(PsiExpression arg) {
return RefParameterImpl.getExpressionValue(arg);
}
}
private ProblemDescriptor registerProblem(@NotNull InspectionManager manager,
PsiParameter parameter,
String value,
Object value,
boolean usedForWriting) {
final String name = parameter.getName();
return manager.createProblemDescriptor(ObjectUtils.notNull(parameter.getNameIdentifier(), parameter),
InspectionsBundle.message("inspection.same.parameter.problem.descriptor",
name,
StringUtil.unquoteString(value)),
StringUtil.unquoteString(String.valueOf(value))),
usedForWriting ? null : createFix(name, value),
ProblemHighlightType.GENERIC_ERROR_OR_WARNING, false);
}
@@ -75,23 +75,24 @@ public class SameParameterValueInspection extends SameParameterValueInspectionBa
}
public static class InlineParameterValueFix implements LocalQuickFix {
private final String myValue;
private final Object myValue;
private final String myParameterName;
private InlineParameterValueFix(final String parameterName, final String value) {
private InlineParameterValueFix(String parameterName, Object value) {
myValue = value;
myParameterName = parameterName;
}
@Override
public String toString() {
return getParamName() + " " + getValue();
return getParamName() + " " + myValue;
}
@Override
@NotNull
public String getName() {
return InspectionsBundle.message("inspection.same.parameter.fix.name", myParameterName, StringUtil.unquoteString(myValue));
return InspectionsBundle
.message("inspection.same.parameter.fix.name", myParameterName, StringUtil.unquoteString(String.valueOf(myValue)));
}
@Override
@@ -120,7 +121,7 @@ public class SameParameterValueInspection extends SameParameterValueInspectionBa
final PsiExpression defToInline;
try {
defToInline = JavaPsiFacade.getInstance(project).getElementFactory().createExpressionFromText(myValue, parameter);
defToInline = JavaPsiFacade.getInstance(project).getElementFactory().createExpressionFromText(String.valueOf(myValue), parameter);
}
catch (IncorrectOperationException e) {
return;
@@ -213,10 +214,6 @@ public class SameParameterValueInspection extends SameParameterValueInspectionBa
psiParameters.toArray(new ParameterInfoImpl[0])).run();
}
public String getValue() {
return myValue;
}
public String getParamName() {
return myParameterName;
}
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<problems>
<problem>
<file>Test.java</file>
<line>3</line>
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Actual method parameter is the same constant</problem_class>
<description>Actual value of parameter 'val' is always '-1.111'</description>
</problem>
</problems>
@@ -0,0 +1,10 @@
public class Test {
static void someMethod(double val) {
}
public static void main(String[] args) {
someMethod(-1.111);
}
}
@@ -71,4 +71,8 @@ public class SameParameterValueLocalTest extends InspectionTestCase {
public void testNativeMethod() {
doTest(getGlobalTestDir(), myTool);
}
public void testNegativeDouble() {
doTest(getGlobalTestDir(), myTool);
}
}
@@ -78,4 +78,8 @@ public class SameParameterValueTest extends InspectionTestCase {
public void testNotReportedDueToHighVisibility() {
doTest(getTestDir(), myTool, false, false);
}
public void testNegativeDouble() {
doTest(getTestDir(), myTool, false, true);
}
}