IG: care about polyadic expressions (IDEA-152012)

This commit is contained in:
Bas Leijdekkers
2016-02-23 19:24:39 +01:00
parent e62fcc9d2b
commit f1ce10c4af
2 changed files with 122 additions and 64 deletions
@@ -1,5 +1,5 @@
/*
* Copyright 2011-2013 Jetbrains s.r.o.
* Copyright 2011-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.
@@ -26,6 +26,7 @@ import com.siyeh.ig.BaseInspectionVisitor;
import com.siyeh.ig.InspectionGadgetsFix;
import com.siyeh.ig.PsiReplacementUtil;
import com.siyeh.ig.psiutils.ParenthesesUtils;
import com.siyeh.ig.psiutils.VariableAccessUtils;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -111,69 +112,124 @@ public class PointlessNullCheckInspection extends BaseInspection {
private static class PointlessNullCheckVisitor extends BaseInspectionVisitor {
@Override
public void visitBinaryExpression(PsiBinaryExpression expression) {
super.visitBinaryExpression(expression);
public void visitPolyadicExpression(PsiPolyadicExpression expression) {
super.visitPolyadicExpression(expression);
final IElementType operationTokenType = expression.getOperationTokenType();
final PsiExpression lhs = ParenthesesUtils.stripParentheses(expression.getLOperand());
final PsiExpression rhs = ParenthesesUtils.stripParentheses(expression.getROperand());
final PsiBinaryExpression binaryExpression;
final PsiExpression possibleInstanceofExpression;
if (operationTokenType.equals(JavaTokenType.ANDAND)) {
if (lhs instanceof PsiBinaryExpression) {
binaryExpression = (PsiBinaryExpression)lhs;
possibleInstanceofExpression = rhs;
}
else if (rhs instanceof PsiBinaryExpression) {
binaryExpression = (PsiBinaryExpression)rhs;
possibleInstanceofExpression = lhs;
}
else {
return;
}
final IElementType tokenType = binaryExpression.getOperationTokenType();
if (!tokenType.equals(JavaTokenType.NE)) {
return;
final PsiExpression[] operands = expression.getOperands();
for (int i = 0; i < operands.length - 1; i++) {
for (int j = i + 1; j < operands.length; j++) {
if (checkAndedExpressions(operands, i, j)) {
return;
}
}
}
}
else if (operationTokenType.equals(JavaTokenType.OROR)) {
if (lhs instanceof PsiBinaryExpression && rhs instanceof PsiPrefixExpression) {
final PsiPrefixExpression prefixExpression = (PsiPrefixExpression)rhs;
final IElementType prefixTokenType = prefixExpression.getOperationTokenType();
if (!JavaTokenType.EXCL.equals(prefixTokenType)) {
return;
final PsiExpression[] operands = expression.getOperands();
for (int i = 0; i < operands.length - 1; i++) {
for (int j = i + 1; j < operands.length; j++) {
if (checkOrredExpressions(operands, i, j)) {
return;
}
}
binaryExpression = (PsiBinaryExpression)lhs;
possibleInstanceofExpression = ParenthesesUtils.stripParentheses(prefixExpression.getOperand());
}
else if (rhs instanceof PsiBinaryExpression && lhs instanceof PsiPrefixExpression) {
final PsiPrefixExpression prefixExpression = (PsiPrefixExpression)lhs;
final IElementType prefixTokenType = prefixExpression.getOperationTokenType();
if (!JavaTokenType.EXCL.equals(prefixTokenType)) {
return;
}
binaryExpression = (PsiBinaryExpression)rhs;
possibleInstanceofExpression = ParenthesesUtils.stripParentheses(prefixExpression.getOperand());
}
else {
return;
}
final IElementType tokenType = binaryExpression.getOperationTokenType();
if (!tokenType.equals(JavaTokenType.EQEQ)) {
return;
}
}
}
public boolean checkOrredExpressions(PsiExpression[] operands, int i, int j) {
final PsiExpression lhs = ParenthesesUtils.stripParentheses(operands[i]);
final PsiExpression rhs = ParenthesesUtils.stripParentheses(operands[j]);
final PsiBinaryExpression binaryExpression;
final PsiPrefixExpression prefixExpression;
final boolean checkRef;
if (lhs instanceof PsiBinaryExpression && rhs instanceof PsiPrefixExpression) {
prefixExpression = (PsiPrefixExpression)rhs;
binaryExpression = (PsiBinaryExpression)lhs;
checkRef = true;
}
else if (rhs instanceof PsiBinaryExpression && lhs instanceof PsiPrefixExpression) {
prefixExpression = (PsiPrefixExpression)lhs;
binaryExpression = (PsiBinaryExpression)rhs;
checkRef = false;
}
else {
return;
return false;
}
final PsiReferenceExpression referenceExpression1 = getReferenceFromNullCheck(binaryExpression);
if (referenceExpression1 == null) {
return;
final IElementType prefixTokenType = prefixExpression.getOperationTokenType();
if (!JavaTokenType.EXCL.equals(prefixTokenType)) {
return false;
}
final PsiReferenceExpression referenceExpression2 = getReferenceFromInstanceofExpression(possibleInstanceofExpression);
if (!referencesEqual(referenceExpression1, referenceExpression2)) {
return;
final PsiExpression possibleInstanceofExpression = ParenthesesUtils.stripParentheses(prefixExpression.getOperand());
final IElementType tokenType = binaryExpression.getOperationTokenType();
if (!tokenType.equals(JavaTokenType.EQEQ)) {
return false;
}
final PsiVariable variable = checkExpressions(binaryExpression, possibleInstanceofExpression);
if (variable == null || checkRef && isVariableUsed(operands, i, j, variable)) {
return false;
}
registerError(binaryExpression, binaryExpression);
return true;
}
public boolean checkAndedExpressions(PsiExpression[] operands, int i, int j) {
final PsiExpression lhs = ParenthesesUtils.stripParentheses(operands[i]);
final PsiExpression rhs = ParenthesesUtils.stripParentheses(operands[j]);
final PsiBinaryExpression binaryExpression;
final PsiExpression possibleInstanceofExpression;
final boolean checkRef;
if (lhs instanceof PsiBinaryExpression) {
binaryExpression = (PsiBinaryExpression)lhs;
possibleInstanceofExpression = rhs;
checkRef = true;
}
else if (rhs instanceof PsiBinaryExpression) {
binaryExpression = (PsiBinaryExpression)rhs;
possibleInstanceofExpression = lhs;
checkRef = false;
}
else {
return false;
}
final IElementType tokenType = binaryExpression.getOperationTokenType();
if (!tokenType.equals(JavaTokenType.NE)) {
return false;
}
final PsiVariable variable = checkExpressions(binaryExpression, possibleInstanceofExpression);
if (variable == null || checkRef && isVariableUsed(operands, i, j, variable)) {
return false;
}
registerError(binaryExpression, binaryExpression);
return true;
}
private static boolean isVariableUsed(PsiExpression[] operands, int i, int j, PsiVariable variable) {
i++;
while (i < j) {
if (VariableAccessUtils.variableIsUsed(variable, operands[i])) {
return true;
}
i++;
}
return false;
}
public static PsiVariable checkExpressions(PsiBinaryExpression binaryExpression, PsiExpression possibleInstanceofExpression) {
final PsiReferenceExpression referenceExpression1 = getReferenceFromNullCheck(binaryExpression);
if (referenceExpression1 == null) {
return null;
}
final PsiElement target1 = referenceExpression1.resolve();
if (!(target1 instanceof PsiVariable)) {
return null;
}
final PsiVariable variable = (PsiVariable)target1;
final PsiReferenceExpression referenceExpression2 = getReferenceFromInstanceofExpression(possibleInstanceofExpression);
if (referenceExpression2 == null || !referenceExpression2.isReferenceTo(variable)) {
return null;
}
return variable;
}
@Nullable
@@ -222,8 +278,14 @@ public class PointlessNullCheckInspection extends BaseInspection {
if (referenceExpression == null) {
return null;
}
final PsiElement target = referenceExpression.resolve();
if (!(target instanceof PsiVariable)) {
return null;
}
final PsiVariable variable = (PsiVariable)target;
for (int i = 1, operandsLength = operands.length; i < operandsLength; i++) {
if (!referencesEqual(referenceExpression, getReferenceFromInstanceofExpression(operands[i]))) {
final PsiReferenceExpression reference2 = getReferenceFromInstanceofExpression(operands[i]);
if (reference2 == null || !reference2.isReferenceTo(variable)) {
return null;
}
}
@@ -233,16 +295,4 @@ public class PointlessNullCheckInspection extends BaseInspection {
}
}
}
private static boolean referencesEqual(PsiReferenceExpression reference1, PsiReferenceExpression reference2) {
if (reference1 == null || reference2 == null) {
return false;
}
final PsiElement target1 = reference1.resolve();
if (target1 == null) {
return false;
}
final PsiElement target2 = reference2.resolve();
return target1.equals(target2);
}
}
@@ -33,6 +33,10 @@ public class PointlessNullCheck {
if (<warning descr="Unnecessary 'null' check before 'instanceof' expression">arg != null</warning> && (arg instanceof String || arg instanceof Integer)) {
System.out.println("this should trigger a warning");
}
if (arg instanceof String && arg.equals(arg) && <warning descr="Unnecessary 'null' check before 'instanceof' expression">arg != null</warning>) {
System.out.println("warning");
}
}
String arg1 = "foo";
@@ -64,5 +68,9 @@ public class PointlessNullCheck {
if (this.arg1 != null && arg1 instanceof String) {
System.out.println("this should not trigger a warning");
}
if (arg1 != null && arg1.equals(arg1) && arg1 instanceof String) {
System.out.println("no warning");
}
}
}