IDEA-215325 Provide "surround with instanceof" fix for possible class cast warning

Also 'assert x instanceof Type' fix is provided

GitOrigin-RevId: 56603a5cb9ce2040525b4147cc8fcabd6f6d7d5d
This commit is contained in:
Tagir Valeev
2019-06-03 09:06:15 +03:00
committed by intellij-monorepo-bot
parent f0dcbe74ae
commit a67207d6c8
8 changed files with 66 additions and 10 deletions
@@ -46,7 +46,7 @@ import java.util.*;
import static com.intellij.util.ObjectUtils.tryCast;
public class DataFlowInspectionBase extends AbstractBaseJavaLocalInspectionTool {
public abstract class DataFlowInspectionBase extends AbstractBaseJavaLocalInspectionTool {
static final Logger LOG = Logger.getInstance("#com.intellij.codeInspection.dataFlow.DataFlowInspection");
@NonNls private static final String SHORT_NAME = "ConstantConditions";
public boolean SUGGEST_NULLABLE_ANNOTATIONS;
@@ -229,6 +229,11 @@ public class DataFlowInspectionBase extends AbstractBaseJavaLocalInspectionTool
}
}
@NotNull
protected List<LocalQuickFix> createCastFixes(PsiTypeCastExpression castExpression, boolean onTheFly) {
return Collections.emptyList();
}
@NotNull
protected List<LocalQuickFix> createNPEFixes(PsiExpression qualifier, PsiExpression expression, boolean onTheFly) {
return Collections.emptyList();
@@ -697,11 +702,11 @@ public class DataFlowInspectionBase extends AbstractBaseJavaLocalInspectionTool
PsiTypeElement castType = typeCast.getCastType();
assert castType != null;
assert operand != null;
LocalQuickFix fix = null;
List<LocalQuickFix> fixes = new ArrayList<>(createCastFixes(typeCast, reporter.isOnTheFly()));
if (reporter.isOnTheFly()) {
fix = createExplainFix(typeCast, new TrackingRunner.CastDfaProblemType());
fixes.add(createExplainFix(typeCast, new TrackingRunner.CastDfaProblemType()));
}
reporter.registerProblem(castType, InspectionsBundle.message("dataflow.message.cce", operand.getText()), fix);
reporter.registerProblem(castType, InspectionsBundle.message("dataflow.message.cce", operand.getText()), fixes.toArray(LocalQuickFix.EMPTY_ARRAY));
}
}
@@ -38,15 +38,17 @@ import org.jetbrains.annotations.NotNull;
public class SurroundWithIfFix implements LocalQuickFix {
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInspection.SurroundWithIfFix");
private final String myText;
private final String mySuffix;
@Override
@NotNull
public String getName() {
return InspectionsBundle.message("inspection.surround.if.quickfix", myText);
return InspectionsBundle.message("inspection.surround.if.quickfix", myText, mySuffix);
}
public SurroundWithIfFix(@NotNull PsiExpression expressionToAssert) {
public SurroundWithIfFix(@NotNull PsiExpression expressionToAssert, String suffix) {
myText = ParenthesesUtils.getText(expressionToAssert, ParenthesesUtils.BINARY_AND_PRECEDENCE);
mySuffix = suffix;
}
@Override
@@ -73,7 +75,7 @@ public class SurroundWithIfFix implements LocalQuickFix {
TextRange textRange = new JavaWithIfSurrounder().surroundElements(project, editor, elements);
if (textRange == null) return;
@NonNls String newText = myText + " != null";
@NonNls String newText = myText + mySuffix;
document.replaceString(textRange.getStartOffset(), textRange.getEndOffset(),newText);
editor.getCaretModel().moveToOffset(textRange.getEndOffset() + newText.length());
@@ -26,6 +26,7 @@ import com.intellij.codeInspection.nullable.NullableStuffInspection;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.*;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.PsiPrecedenceUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.refactoring.util.RefactoringUtil;
@@ -133,6 +134,22 @@ public class DataFlowInspection extends DataFlowInspectionBase {
return new DeleteSideEffectsAwareFix((PsiStatement)assignment.getParent(), assignment.getRExpression(), true);
}
@Override
@NotNull
protected List<LocalQuickFix> createCastFixes(PsiTypeCastExpression castExpression, boolean onTheFly) {
List<LocalQuickFix> fixes = new ArrayList<>();
PsiExpression operand = castExpression.getOperand();
PsiTypeElement typeElement = castExpression.getCastType();
if (typeElement != null && operand != null && !SideEffectChecker.mayHaveSideEffects(operand)) {
String suffix = " instanceof " + typeElement.getText();
fixes.add(new AddAssertStatementFix(ParenthesesUtils.getText(operand, PsiPrecedenceUtil.RELATIONAL_PRECEDENCE) + suffix));
if (onTheFly && SurroundWithIfFix.isAvailable(operand)) {
fixes.add(new SurroundWithIfFix(operand, suffix));
}
}
return fixes;
}
@Override
@NotNull
protected List<LocalQuickFix> createNPEFixes(PsiExpression qualifier, PsiExpression expression, boolean onTheFly) {
@@ -148,14 +165,15 @@ public class DataFlowInspection extends DataFlowInspectionBase {
ContainerUtil.addIfNotNull(fixes, createIntroduceVariableFix(qualifier));
}
else if (!ExpressionUtils.isNullLiteral(qualifier) && !SideEffectChecker.mayHaveSideEffects(qualifier)) {
String suffix = " != null";
if (PsiUtil.getLanguageLevel(qualifier).isAtLeast(LanguageLevel.JDK_1_4) &&
RefactoringUtil.getParentStatement(expression, false) != null) {
String replacement = ParenthesesUtils.getText(qualifier, ParenthesesUtils.EQUALITY_PRECEDENCE) + " != null";
String replacement = ParenthesesUtils.getText(qualifier, ParenthesesUtils.EQUALITY_PRECEDENCE) + suffix;
fixes.add(new AddAssertStatementFix(replacement));
}
if (onTheFly && SurroundWithIfFix.isAvailable(qualifier)) {
fixes.add(new SurroundWithIfFix(qualifier));
fixes.add(new SurroundWithIfFix(qualifier, suffix));
}
if (onTheFly && ReplaceWithTernaryOperatorFix.isAvailable(qualifier, expression)) {
@@ -0,0 +1,8 @@
// "Assert 'obj instanceof String'" "true"
class X {
void test(Object obj) {
if (obj instanceof Integer) System.out.println();
assert obj instanceof String;
String string = (String)obj;
}
}
@@ -0,0 +1,7 @@
// "Assert 'obj instanceof String'" "true"
class X {
void test(Object obj) {
if (obj instanceof Integer) System.out.println();
String string = (<caret>String)obj;
}
}
@@ -0,0 +1,9 @@
// "Surround with 'if (obj instanceof String)'" "true"
class X {
void test(Object obj) {
if (obj instanceof Integer) System.out.println();
if (obj instanceof String) {
String string = (String)obj;
}
}
}
@@ -0,0 +1,7 @@
// "Surround with 'if (obj instanceof String)'" "true"
class X {
void test(Object obj) {
if (obj instanceof Integer) System.out.println();
String string = (<caret>String)obj;
}
}
@@ -361,7 +361,7 @@ inspection.export.results.implicit.constructor=implicit constructor of
inspection.problem.resolution=Problem resolution
inspection.quickfix.assert.family=Assert
inspection.assert.quickfix=Assert ''{0}''
inspection.surround.if.quickfix=Surround with ''if ({0} != null)''
inspection.surround.if.quickfix=Surround with ''if ({0}{1})''
inspection.replace.ternary.quickfix=Replace with ''{0} != null ?:''
inspection.replace.methodref.ternary.quickfix=Replace with null-checking lambda
inspection.surround.if.family=Surround with if