IDEA-161061 Quick-fix to replace nullableExpr.equals(...) with Objects.equals(nullableExpr, ...)

This commit is contained in:
peter
2016-09-20 15:47:14 +02:00
parent f8a835dc97
commit b08872448c
6 changed files with 174 additions and 19 deletions

View File

@@ -230,18 +230,16 @@ public class DataFlowInspectionBase extends BaseJavaBatchLocalInspectionTool {
}
}
@Nullable
private LocalQuickFix[] createNPEFixes(PsiExpression qualifier, PsiExpression expression, boolean onTheFly) {
if (qualifier == null || expression == null) return null;
if (qualifier instanceof PsiMethodCallExpression) return null;
@NotNull
private List<LocalQuickFix> createNPEFixes(PsiExpression qualifier, PsiExpression expression, boolean onTheFly) {
final List<LocalQuickFix> fixes = new SmartList<>();
if (qualifier == null || expression == null) return fixes;
try {
final List<LocalQuickFix> fixes = new SmartList<>();
if (isVolatileFieldReference(qualifier)) {
ContainerUtil.addIfNotNull(fixes, createIntroduceVariableFix(qualifier));
}
else if (!(qualifier instanceof PsiLiteralExpression && ((PsiLiteralExpression)qualifier).getValue() == null)) {
else if (!isNullLiteral(qualifier) && !(qualifier instanceof PsiMethodCallExpression)) {
if (PsiUtil.getLanguageLevel(qualifier).isAtLeast(LanguageLevel.JDK_1_4)) {
final Project project = qualifier.getProject();
final PsiElementFactory elementFactory = JavaPsiFacade.getInstance(project).getElementFactory();
@@ -258,12 +256,15 @@ public class DataFlowInspectionBase extends BaseJavaBatchLocalInspectionTool {
}
ContainerUtil.addIfNotNull(fixes, DfaOptionalSupport.registerReplaceOptionalOfWithOfNullableFix(qualifier));
return fixes.isEmpty() ? null : fixes.toArray(new LocalQuickFix[fixes.size()]);
}
catch (IncorrectOperationException e) {
LOG.error(e);
return null;
}
return fixes;
}
private static boolean isNullLiteral(PsiExpression qualifier) {
return qualifier instanceof PsiLiteralExpression && ((PsiLiteralExpression)qualifier).getValue() == null;
}
@Nullable
@@ -436,7 +437,7 @@ public class DataFlowInspectionBase extends BaseJavaBatchLocalInspectionTool {
final String text = isNullLiteralExpression(expr)
? "Passing <code>null</code> argument to non annotated parameter"
: "Argument <code>#ref</code> #loc might be null but passed to non annotated parameter";
LocalQuickFix[] fixes = createNPEFixes((PsiExpression)expr, (PsiExpression)expr, holder.isOnTheFly());
List<LocalQuickFix> fixes = createNPEFixes((PsiExpression)expr, (PsiExpression)expr, holder.isOnTheFly());
final PsiElement parent = expr.getParent();
if (parent instanceof PsiExpressionList) {
final int idx = ArrayUtilRt.find(((PsiExpressionList)parent).getExpressions(), expr);
@@ -447,9 +448,8 @@ public class DataFlowInspectionBase extends BaseJavaBatchLocalInspectionTool {
if (psiMethod != null && psiMethod.getManager().isInProject(psiMethod) && AnnotationUtil.isAnnotatingApplicable(psiMethod)) {
final PsiParameter[] parameters = psiMethod.getParameterList().getParameters();
if (idx < parameters.length) {
final AddNullableAnnotationFix addNullableAnnotationFix = new AddNullableAnnotationFix(parameters[idx]);
fixes = fixes == null ? new LocalQuickFix[]{addNullableAnnotationFix} : ArrayUtil.append(fixes, addNullableAnnotationFix);
holder.registerProblem(expr, text, fixes);
fixes.add(new AddNullableAnnotationFix(parameters[idx]));
holder.registerProblem(expr, text, fixes.toArray(LocalQuickFix.EMPTY_ARRAY));
reportedAnchors.add(expr);
}
}
@@ -462,24 +462,24 @@ public class DataFlowInspectionBase extends BaseJavaBatchLocalInspectionTool {
private void reportCallMayProduceNpe(ProblemsHolder holder, PsiMethodCallExpression callExpression, boolean onTheFly) {
PsiReferenceExpression methodExpression = callExpression.getMethodExpression();
LocalQuickFix[] fix = createNPEFixes(methodExpression.getQualifierExpression(), callExpression, onTheFly);
List<LocalQuickFix> fixes = createNPEFixes(methodExpression.getQualifierExpression(), callExpression, onTheFly);
ContainerUtil.addIfNotNull(fixes, ReplaceWithObjectsEqualsFix.createFix(callExpression, methodExpression));
PsiElement toHighlight = methodExpression.getReferenceNameElement();
if (toHighlight == null) toHighlight = methodExpression;
holder.registerProblem(toHighlight,
InspectionsBundle.message("dataflow.message.npe.method.invocation"),
fix);
fixes.toArray(LocalQuickFix.EMPTY_ARRAY));
}
private void reportFieldAccessMayProduceNpe(ProblemsHolder holder, PsiElement elementToAssert, @NotNull PsiExpression expression) {
LocalQuickFix[] fix = createNPEFixes((PsiExpression)elementToAssert, expression, holder.isOnTheFly()).toArray(LocalQuickFix.EMPTY_ARRAY);
if (expression instanceof PsiArrayAccessExpression) {
LocalQuickFix[] fix = createNPEFixes((PsiExpression)elementToAssert, expression, holder.isOnTheFly());
holder.registerProblem(expression,
InspectionsBundle.message("dataflow.message.npe.array.access"),
fix);
}
else {
LocalQuickFix[] fix = createNPEFixes((PsiExpression)elementToAssert, expression, holder.isOnTheFly());
assert elementToAssert != null;
//noinspection ConditionalExpressionWithIdenticalBranches
holder.registerProblem(elementToAssert,
@@ -570,8 +570,8 @@ public class DataFlowInspectionBase extends BaseJavaBatchLocalInspectionTool {
final String text = isNullLiteralExpression(expr)
? InspectionsBundle.message("dataflow.message.passing.null.argument")
: InspectionsBundle.message("dataflow.message.passing.nullable.argument");
LocalQuickFix[] fixes = createNPEFixes((PsiExpression)expr, (PsiExpression)expr, holder.isOnTheFly());
holder.registerProblem(expr, text, fixes);
List<LocalQuickFix> fixes = createNPEFixes((PsiExpression)expr, (PsiExpression)expr, holder.isOnTheFly());
holder.registerProblem(expr, text, fixes.toArray(LocalQuickFix.EMPTY_ARRAY));
}
}

View File

@@ -0,0 +1,88 @@
/*
* 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.dataFlow;
import com.intellij.codeInsight.FileModificationService;
import com.intellij.codeInspection.LocalQuickFix;
import com.intellij.codeInspection.ProblemDescriptor;
import com.intellij.openapi.project.Project;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* @author peter
*/
class ReplaceWithObjectsEqualsFix implements LocalQuickFix {
private final String myText;
private ReplaceWithObjectsEqualsFix(String text) {
myText = text;
}
@Nls
@NotNull
@Override
public String getName() {
return "Replace '" + myText + ".equals(...)' with 'Objects.equals(" + myText + ", ...)'";
}
@Nls
@NotNull
@Override
public String getFamilyName() {
return "Replace '.equals()' with 'Objects.equals()'";
}
@Override
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
PsiMethodCallExpression call = PsiTreeUtil.getParentOfType(descriptor.getPsiElement(), PsiMethodCallExpression.class);
if (call == null || !FileModificationService.getInstance().prepareFileForWrite(call.getContainingFile())) return;
PsiElement qualifier = call.getMethodExpression().getQualifier();
PsiExpression[] args = call.getArgumentList().getExpressions();
if (qualifier == null || args.length != 1) return;
String replacementText = "java.util.Objects.equals(" + qualifier.getText() + ", " + args[0].getText() + ")";
PsiElement replaced = call.replace(JavaPsiFacade.getElementFactory(project).createExpressionFromText(replacementText, call));
JavaCodeStyleManager.getInstance(project).shortenClassReferences(((PsiMethodCallExpression)replaced).getMethodExpression());
}
@Nullable
static ReplaceWithObjectsEqualsFix createFix(@NotNull PsiMethodCallExpression call, @NotNull PsiReferenceExpression methodExpression) {
if (!"equals".equals(methodExpression.getReferenceName()) ||
call.getArgumentList().getExpressions().length != 1 ||
!PsiUtil.getLanguageLevel(call).isAtLeast(LanguageLevel.JDK_1_7)) {
return null;
}
PsiExpression qualifier = methodExpression.getQualifierExpression();
if (qualifier == null) return null;
PsiMethod method = call.resolveMethod();
if (method != null &&
method.getParameterList().getParametersCount() == 1 &&
method.getParameterList().getParameters()[0].getType().equalsToText(CommonClassNames.JAVA_LANG_OBJECT)) {
return new ReplaceWithObjectsEqualsFix(qualifier.getText());
}
return null;
}
}

View File

@@ -0,0 +1,13 @@
// "Replace 'foo().equals(...)' with 'Objects.equals(foo(), ...)'" "true"
import org.jetbrains.annotations.*;
import java.util.Objects;
class A{
void test(Object bar) {
if (Objects.equals(foo(), bar)) {}
}
@Nullable Object foo();
}

View File

@@ -0,0 +1,11 @@
// "Replace 'foo().equals(...)' with 'Objects.equals(foo(), ...)'" "true"
import org.jetbrains.annotations.*;
class A{
void test(Object bar) {
if (foo().equa<caret>ls(bar)) {}
}
@Nullable Object foo();
}

View File

@@ -0,0 +1,42 @@
/*
* 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.
*/
/*
* User: anna
* Date: 21-Mar-2008
*/
package com.intellij.codeInsight.daemon.quickFix;
import com.intellij.codeInspection.LocalInspectionTool;
import com.intellij.codeInspection.dataFlow.DataFlowInspection;
import org.jetbrains.annotations.NotNull;
public class ReplaceWithObjectsEqualsTest extends LightQuickFixParameterizedTestCase {
@NotNull
@Override
protected LocalInspectionTool[] configureLocalInspectionTools() {
return new LocalInspectionTool[]{new DataFlowInspection()};
}
public void test() throws Exception {
doAllTests();
}
@Override
protected String getBasePath() {
return "/codeInsight/daemonCodeAnalyzer/quickFix/replaceWithObjectsEquals";
}
}

View File

@@ -50,6 +50,7 @@ public class DataFlowInspectionTestSuite {
suite.addTestSuite(AddAssertStatementFixTest.class);
suite.addTestSuite(SurroundWithIfFixTest.class);
suite.addTestSuite(ReplaceWithTernaryOperatorTest.class);
suite.addTestSuite(ReplaceWithObjectsEqualsTest.class);
suite.addTestSuite(ReplaceWithOfNullableFixTest.class);
suite.addTestSuite(ReplaceFromOfNullableFixTest.class);
suite.addTestSuite(UnwrapIfStatementFixTest.class);