IDEA-240403 Analyze dataflow to here: perform backpropagation through simple expressions

GitOrigin-RevId: 6c7d2e9f30121748c624a20ab14cbcf7dd7c71ec
This commit is contained in:
Tagir Valeev
2020-05-15 11:16:42 +00:00
committed by intellij-monorepo-bot
parent 646b343400
commit 7d4c5ea4d8
9 changed files with 397 additions and 257 deletions
@@ -0,0 +1,291 @@
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.slicer;
import com.intellij.codeInsight.Nullability;
import com.intellij.codeInspection.dataFlow.CommonDataflow;
import com.intellij.codeInspection.dataFlow.NullabilityProblemKind;
import com.intellij.codeInspection.dataFlow.rangeSet.LongRangeSet;
import com.intellij.codeInspection.dataFlow.types.DfIntegralType;
import com.intellij.codeInspection.dataFlow.types.DfLongType;
import com.intellij.codeInspection.dataFlow.types.DfType;
import com.intellij.codeInspection.dataFlow.types.DfTypes;
import com.intellij.codeInspection.dataFlow.value.RelationType;
import com.intellij.psi.*;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.ConstantExpressionUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.ArrayUtil;
import com.intellij.util.ObjectUtils;
import com.siyeh.ig.psiutils.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collections;
import java.util.Objects;
class AnalysisStartingPoint {
final DfType myDfType;
final PsiExpression myAnchor;
AnalysisStartingPoint(DfType type, PsiExpression anchor) {
myDfType = type;
myAnchor = anchor;
}
@Nullable AnalysisStartingPoint tryMeet(@NotNull AnalysisStartingPoint next) {
if (!EquivalenceChecker.getCanonicalPsiEquivalence().expressionsAreEquivalent(this.myAnchor, next.myAnchor)) return null;
DfType meet = this.myDfType.meet(next.myDfType);
if (meet == DfTypes.BOTTOM) return null;
return new AnalysisStartingPoint(meet, this.myAnchor);
}
@Nullable AnalysisStartingPoint tryJoin(@NotNull AnalysisStartingPoint next) {
if (!EquivalenceChecker.getCanonicalPsiEquivalence().expressionsAreEquivalent(this.myAnchor, next.myAnchor)) return null;
DfType meet = this.myDfType.join(next.myDfType);
if (meet == DfTypes.TOP) return null;
return new AnalysisStartingPoint(meet, this.myAnchor);
}
static @Nullable AnalysisStartingPoint create(@NotNull DfType type, @Nullable PsiExpression anchor) {
anchor = extractAnchor(anchor);
if (anchor == null) return null;
if (DfTypes.typedObject(anchor.getType(), Nullability.UNKNOWN).meet(type) == DfTypes.BOTTOM) return null;
return new AnalysisStartingPoint(type, anchor);
}
@Nullable
static PsiExpression extractAnchor(@Nullable PsiExpression target) {
target = PsiUtil.skipParenthesizedExprDown(target);
if (target instanceof PsiReferenceExpression ||
target instanceof PsiMethodCallExpression ||
target != null && propagateThroughExpression(target, DfTypes.LONG) != null) {
return target;
}
return null;
}
static @Nullable AnalysisStartingPoint fromCondition(@Nullable PsiExpression cond) {
cond = PsiUtil.skipParenthesizedExprDown(cond);
if (cond == null) return null;
if (cond instanceof PsiPolyadicExpression) {
IElementType tokenType = ((PsiPolyadicExpression)cond).getOperationTokenType();
if (tokenType.equals(JavaTokenType.ANDAND)) {
AnalysisStartingPoint analysis = null;
for (PsiExpression operand : ((PsiPolyadicExpression)cond).getOperands()) {
AnalysisStartingPoint next = fromCondition(operand);
if (next == null) return null;
if (analysis == null) {
analysis = next;
}
else {
analysis = analysis.tryMeet(next);
if (analysis == null) return null;
}
}
return analysis;
}
if (tokenType.equals(JavaTokenType.OROR)) {
AnalysisStartingPoint analysis = null;
for (PsiExpression operand : ((PsiPolyadicExpression)cond).getOperands()) {
AnalysisStartingPoint next = fromCondition(operand);
if (next == null) return null;
if (analysis == null) {
analysis = next;
}
else {
analysis = analysis.tryJoin(next);
if (analysis == null) return null;
}
}
return analysis;
}
}
if (cond instanceof PsiBinaryExpression) {
PsiBinaryExpression binop = (PsiBinaryExpression)cond;
PsiExpression left = PsiUtil.skipParenthesizedExprDown(binop.getLOperand());
PsiExpression right = PsiUtil.skipParenthesizedExprDown(binop.getROperand());
AnalysisStartingPoint analysis = fromBinOp(left, binop.getOperationTokenType(), right);
if (analysis != null) return analysis;
return fromBinOp(right, binop.getOperationTokenType(), left);
}
if (cond instanceof PsiInstanceOfExpression) {
PsiTypeElement checkType = ((PsiInstanceOfExpression)cond).getCheckType();
if (checkType == null) return null;
PsiExpression anchor = extractAnchor(((PsiInstanceOfExpression)cond).getOperand());
if (anchor != null) {
DfType typedObject = DfTypes.typedObject(checkType.getType(), Nullability.NOT_NULL);
return new AnalysisStartingPoint(typedObject, anchor);
}
}
if (cond instanceof PsiMethodCallExpression) {
PsiMethodCallExpression call = (PsiMethodCallExpression)cond;
if (MethodCallUtils.isEqualsCall(call)) {
PsiExpression qualifier = PsiUtil.skipParenthesizedExprDown(call.getMethodExpression().getQualifierExpression());
PsiExpression argument = PsiUtil.skipParenthesizedExprDown(ArrayUtil.getFirstElement(call.getArgumentList().getExpressions()));
if (qualifier != null && argument != null) {
DfType type = fromConstant(qualifier);
PsiExpression anchor = extractAnchor(argument);
if (type == null) {
type = fromConstant(argument);
anchor = extractAnchor(qualifier);
}
if (type != null && anchor != null) {
PsiType anchorType = anchor.getType();
if (anchorType == null || DfTypes.typedObject(anchorType, Nullability.NOT_NULL).meet(type) == DfTypes.BOTTOM) return null;
return new AnalysisStartingPoint(type, anchor);
}
}
}
}
if (BoolUtils.isNegation(cond)) {
AnalysisStartingPoint negatedAnalysis = fromCondition(BoolUtils.getNegated(cond));
return tryNegate(negatedAnalysis);
}
PsiExpression anchor = extractAnchor(cond);
if (anchor != null) {
return new AnalysisStartingPoint(DfTypes.TRUE, anchor);
}
return null;
}
static @Nullable AnalysisStartingPoint tryNegate(AnalysisStartingPoint analysis) {
if (analysis == null) return null;
DfType type = analysis.myDfType.tryNegate();
if (type == null) return null;
NullabilityProblemKind.NullabilityProblem<?> problem = NullabilityProblemKind.fromContext(analysis.myAnchor, Collections.emptyMap());
if (problem != null && CommonClassNames.JAVA_LANG_NULL_POINTER_EXCEPTION.equals(problem.thrownException())) {
type = type.meet(DfTypes.NOT_NULL_OBJECT);
}
return new AnalysisStartingPoint(type, analysis.myAnchor);
}
static @Nullable DfType fromConstant(@NotNull PsiExpression constant) {
if (constant instanceof PsiClassObjectAccessExpression) {
PsiClassObjectAccessExpression classObject = (PsiClassObjectAccessExpression)constant;
PsiTypeElement operand = classObject.getOperand();
return DfTypes.constant(operand.getType(), classObject.getType());
}
if (constant instanceof PsiReferenceExpression) {
PsiElement target = ((PsiReferenceExpression)constant).resolve();
if (target instanceof PsiEnumConstant) {
return DfTypes.constant(target, Objects.requireNonNull(constant.getType()));
}
}
if (ExpressionUtils.isNullLiteral(constant)) {
return DfTypes.NULL;
}
Object value = ExpressionUtils.computeConstantExpression(constant);
if (value != null) {
return DfTypes.constant(value, Objects.requireNonNull(constant.getType()));
}
return null;
}
private static @Nullable AnalysisStartingPoint fromBinOp(@Nullable PsiExpression target,
@NotNull IElementType type,
@Nullable PsiExpression constant) {
if (constant == null) return null;
DfType constantType = fromConstant(constant);
if (constantType == null) {
return null;
}
PsiExpression anchor = extractAnchor(target);
if (anchor != null) {
PsiType anchorType = anchor.getType();
if (anchorType == null || TypeUtils.isJavaLangString(anchorType)) return null;
if (anchorType.equals(PsiType.BYTE) || anchorType.equals(PsiType.CHAR) || anchorType.equals(PsiType.SHORT)) {
anchorType = PsiType.INT;
}
if (constantType == DfTypes.NULL || DfTypes.typedObject(anchorType, Nullability.NOT_NULL).meet(constantType) != DfTypes.BOTTOM) {
if (type.equals(JavaTokenType.EQEQ)) {
return new AnalysisStartingPoint(constantType, anchor);
}
if (type.equals(JavaTokenType.NE)) {
return tryNegate(new AnalysisStartingPoint(constantType, anchor));
}
}
}
RelationType relationType = RelationType.fromElementType(type);
if (relationType != null) {
LongRangeSet set = DfLongType.extractRange(constantType).fromRelation(relationType);
if (anchor == null) return null;
PsiType anchorType = anchor.getType();
if (PsiType.LONG.equals(anchorType)) {
return new AnalysisStartingPoint(DfTypes.longRange(set), anchor);
}
if (PsiType.INT.equals(anchorType) ||
PsiType.SHORT.equals(anchorType) ||
PsiType.BYTE.equals(anchorType) ||
PsiType.CHAR.equals(anchorType)) {
set = set.intersect(Objects.requireNonNull(LongRangeSet.fromType(anchorType)));
return new AnalysisStartingPoint(DfTypes.intRangeClamped(set), anchor);
}
}
return null;
}
static AnalysisStartingPoint propagateThroughExpression(@NotNull PsiElement expression, @NotNull DfType origType) {
AnalysisStartingPoint analysis = null;
if (origType == DfTypes.TRUE) {
analysis = fromCondition((PsiExpression)expression);
}
else if (origType == DfTypes.FALSE) {
analysis = tryNegate(fromCondition((PsiExpression)expression));
}
DfIntegralType dfType = ObjectUtils.tryCast(origType, DfIntegralType.class);
if (dfType != null) {
boolean isLong = dfType instanceof DfLongType;
LongRangeSet origRange = dfType.getRange();
LongRangeSet newRange = null;
PsiExpression anchor = null;
if (expression instanceof PsiPrefixExpression) {
anchor = ((PsiPrefixExpression)expression).getOperand();
IElementType type = ((PsiPrefixExpression)expression).getOperationTokenType();
if (type.equals(JavaTokenType.MINUS)) {
newRange = origRange.negate(isLong);
}
else if (type.equals(JavaTokenType.TILDE)) {
newRange = origRange.negate(isLong).minus(LongRangeSet.point(1), isLong);
}
}
if (expression instanceof PsiBinaryExpression) {
PsiBinaryExpression binOp = (PsiBinaryExpression)expression;
IElementType type = binOp.getOperationTokenType();
LongRangeSet leftRange = CommonDataflow.getExpressionRange(binOp.getLOperand());
LongRangeSet rightRange = CommonDataflow.getExpressionRange(binOp.getROperand());
Long left = leftRange == null ? null : leftRange.getConstantValue();
Long right = rightRange == null ? null : rightRange.getConstantValue();
if (type.equals(JavaTokenType.PERC)) {
if (right != null) {
newRange = LongRangeSet.fromRemainder(right, origRange);
anchor = binOp.getLOperand();
}
}
if (type.equals(JavaTokenType.PLUS)) {
if (right != null) {
newRange = origRange.minus(LongRangeSet.point(right), isLong);
anchor = binOp.getLOperand();
}
else if (left != null) {
newRange = origRange.minus(LongRangeSet.point(left), isLong);
anchor = binOp.getROperand();
}
}
if (type.equals(JavaTokenType.MINUS)) {
if (right != null) {
newRange = origRange.plus(LongRangeSet.point(right), isLong);
anchor = binOp.getLOperand();
}
else if (left != null) {
newRange = LongRangeSet.point(left).minus(origRange, isLong);
anchor = binOp.getROperand();
}
}
}
if (newRange != null && anchor != null && !(anchor instanceof PsiLiteralExpression)) {
analysis = new AnalysisStartingPoint(isLong ? DfTypes.longRange(newRange) : DfTypes.intRangeClamped(newRange), anchor);
}
}
return analysis;
}
}
@@ -4,9 +4,7 @@ package com.intellij.slicer;
import com.intellij.analysis.AnalysisScope;
import com.intellij.codeInsight.Nullability;
import com.intellij.codeInspection.dataFlow.*;
import com.intellij.codeInspection.dataFlow.rangeSet.LongRangeSet;
import com.intellij.codeInspection.dataFlow.types.*;
import com.intellij.codeInspection.dataFlow.value.RelationType;
import com.intellij.execution.filters.*;
import com.intellij.java.JavaBundle;
import com.intellij.openapi.actionSystem.AnAction;
@@ -14,11 +12,9 @@ import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.util.ArrayUtil;
import com.intellij.util.containers.ContainerUtil;
import com.siyeh.ig.psiutils.*;
import org.jetbrains.annotations.NotNull;
@@ -38,19 +34,19 @@ public class DataflowExceptionAnalysisProvider implements ExceptionAnalysisProvi
@Override
public @Nullable AnAction getAnalysisAction(@NotNull PsiElement anchor,
@NotNull ExceptionInfo info) {
Analysis analysis = getAnalysis(anchor, info);
AnalysisStartingPoint analysis = getAnalysis(anchor, info);
return createAction(analysis);
}
@Override
public @Nullable AnAction getIntermediateRowAnalysisAction(@NotNull PsiElement anchor) {
Analysis analysis = getIntermediateRowAnalysis(anchor);
AnalysisStartingPoint analysis = getIntermediateRowAnalysis(anchor);
return createAction(analysis);
}
private static @Nullable Analysis getIntermediateRowAnalysis(@NotNull PsiElement anchor) {
private static @Nullable AnalysisStartingPoint getIntermediateRowAnalysis(@NotNull PsiElement anchor) {
if (anchor instanceof PsiExpression) {
return new Analysis(DfTypes.NULL, (PsiExpression)anchor);
return new AnalysisStartingPoint(DfTypes.NULL, (PsiExpression)anchor);
}
if (!(anchor instanceof PsiIdentifier)) return null;
PsiReferenceExpression ref = tryCast(anchor.getParent(), PsiReferenceExpression.class);
@@ -70,19 +66,19 @@ public class DataflowExceptionAnalysisProvider implements ExceptionAnalysisProvi
PsiExpression[] args = call.getArgumentList().getExpressions();
PsiExpression arg = getArgFromContract(args, condition, ContractValue.nullValue(), true);
if (arg != null) {
return Analysis.create(DfTypes.NULL, arg);
return AnalysisStartingPoint.create(DfTypes.NULL, arg);
}
arg = getArgFromContract(args, condition, ContractValue.nullValue(), false);
if (arg != null) {
return Analysis.create(DfTypes.NOT_NULL_OBJECT, arg);
return AnalysisStartingPoint.create(DfTypes.NOT_NULL_OBJECT, arg);
}
arg = getArgFromContract(args, condition, ContractValue.booleanValue(true), true);
if (arg != null) {
return fromCondition(arg);
return AnalysisStartingPoint.fromCondition(arg);
}
arg = getArgFromContract(args, condition, ContractValue.booleanValue(false), true);
if (arg != null) {
return tryNegate(fromCondition(arg));
return AnalysisStartingPoint.tryNegate(AnalysisStartingPoint.fromCondition(arg));
}
return null;
}
@@ -94,8 +90,8 @@ public class DataflowExceptionAnalysisProvider implements ExceptionAnalysisProvi
return args[pos];
}
private @Nullable Analysis getAnalysis(@NotNull PsiElement anchor,
@NotNull ExceptionInfo info) {
private @Nullable AnalysisStartingPoint getAnalysis(@NotNull PsiElement anchor,
@NotNull ExceptionInfo info) {
if (anchor instanceof PsiKeyword && anchor.textMatches(PsiKeyword.NEW)) {
PsiNewExpression exceptionConstructor = tryCast(anchor.getParent(), PsiNewExpression.class);
if (exceptionConstructor != null && !exceptionConstructor.isArrayCreation()) {
@@ -111,19 +107,19 @@ public class DataflowExceptionAnalysisProvider implements ExceptionAnalysisProvi
else if (info instanceof ArrayIndexOutOfBoundsExceptionInfo) {
Integer index = ((ArrayIndexOutOfBoundsExceptionInfo)info).getIndex();
if (index != null && anchor instanceof PsiExpression) {
return Analysis.create(DfTypes.intValue(index), (PsiExpression)anchor);
return AnalysisStartingPoint.create(DfTypes.intValue(index), (PsiExpression)anchor);
}
}
else if (info instanceof ClassCastExceptionInfo) {
return fromClassCastException(anchor, ((ClassCastExceptionInfo)info).getActualClass());
}
else if (info instanceof NullPointerExceptionInfo || info instanceof JetBrainsNotNullInstrumentationExceptionInfo) {
return Analysis.create(DfTypes.NULL, tryCast(anchor, PsiExpression.class));
return AnalysisStartingPoint.create(DfTypes.NULL, tryCast(anchor, PsiExpression.class));
}
else if (info instanceof NegativeArraySizeExceptionInfo) {
Integer size = ((NegativeArraySizeExceptionInfo)info).getSuppliedSize();
if (size != null && size < 0 && anchor instanceof PsiExpression) {
return Analysis.create(DfTypes.intValue(size), (PsiExpression)anchor);
return AnalysisStartingPoint.create(DfTypes.intValue(size), (PsiExpression)anchor);
}
}
else if (info instanceof ArithmeticExceptionInfo) {
@@ -131,27 +127,27 @@ public class DataflowExceptionAnalysisProvider implements ExceptionAnalysisProvi
}
else if (info instanceof ArrayCopyIndexOutOfBoundsExceptionInfo) {
if (anchor instanceof PsiExpression) {
return Analysis.create(DfTypes.intValue(((ArrayCopyIndexOutOfBoundsExceptionInfo)info).getValue()), (PsiExpression)anchor);
return AnalysisStartingPoint.create(DfTypes.intValue(((ArrayCopyIndexOutOfBoundsExceptionInfo)info).getValue()), (PsiExpression)anchor);
}
}
return null;
}
private static Analysis fromArithmeticException(PsiElement anchor) {
private static AnalysisStartingPoint fromArithmeticException(PsiElement anchor) {
if (anchor instanceof PsiExpression) {
PsiExpression divisor = (PsiExpression)anchor;
PsiType type = divisor.getType();
if (PsiType.LONG.equals(type)) {
return Analysis.create(DfTypes.longValue(0), divisor);
return AnalysisStartingPoint.create(DfTypes.longValue(0), divisor);
}
else if (TypeConversionUtil.isIntegralNumberType(type)) {
return Analysis.create(DfTypes.intValue(0), divisor);
return AnalysisStartingPoint.create(DfTypes.intValue(0), divisor);
}
}
return null;
}
private @Nullable static Analysis fromThrowStatement(PsiThrowStatement throwStatement) {
private @Nullable static AnalysisStartingPoint fromThrowStatement(PsiThrowStatement throwStatement) {
PsiElement parent = throwStatement.getParent();
if (parent instanceof PsiCodeBlock) {
PsiElement statement = throwStatement.getPrevSibling();
@@ -163,8 +159,8 @@ public class DataflowExceptionAnalysisProvider implements ExceptionAnalysisProvi
boolean elseExits =
ifStatement.getElseBranch() != null && !ControlFlowUtils.statementMayCompleteNormally(ifStatement.getElseBranch());
if (thenExits != elseExits) {
Analysis analysis = fromCondition(ifStatement.getCondition());
return thenExits ? tryNegate(analysis) : analysis;
AnalysisStartingPoint analysis = AnalysisStartingPoint.fromCondition(ifStatement.getCondition());
return thenExits ? AnalysisStartingPoint.tryNegate(analysis) : analysis;
}
}
if (statement instanceof PsiSwitchLabelStatement) {
@@ -179,7 +175,7 @@ public class DataflowExceptionAnalysisProvider implements ExceptionAnalysisProvi
}
if (parent instanceof PsiIfStatement && PsiTreeUtil.isAncestor(((PsiIfStatement)parent).getThenBranch(), throwStatement, false)) {
PsiExpression cond = PsiUtil.skipParenthesizedExprDown(((PsiIfStatement)parent).getCondition());
return fromCondition(cond);
return AnalysisStartingPoint.fromCondition(cond);
}
if (parent instanceof PsiSwitchLabeledRuleStatement) {
return fromSwitchLabel((PsiSwitchLabeledRuleStatement)parent);
@@ -187,7 +183,7 @@ public class DataflowExceptionAnalysisProvider implements ExceptionAnalysisProvi
return null;
}
private static @Nullable Analysis fromSwitchLabel(PsiSwitchLabelStatementBase label) {
private static @Nullable AnalysisStartingPoint fromSwitchLabel(PsiSwitchLabelStatementBase label) {
PsiSwitchBlock block = label.getEnclosingSwitchBlock();
if (block == null) return null;
boolean hasDefault = false;
@@ -231,13 +227,13 @@ public class DataflowExceptionAnalysisProvider implements ExceptionAnalysisProvi
labels = allLabels;
}
PsiExpression selector = block.getExpression();
Analysis result = null;
AnalysisStartingPoint result = null;
for (PsiExpression labelValue : labels) {
DfType type = fromConstant(labelValue);
DfType type = AnalysisStartingPoint.fromConstant(labelValue);
if (type == null) return null;
Analysis next = Analysis.create(type, selector);
AnalysisStartingPoint next = AnalysisStartingPoint.create(type, selector);
if (hasDefault) {
next = tryNegate(next);
next = AnalysisStartingPoint.tryNegate(next);
}
if (next == null) return null;
if (result == null) {
@@ -250,36 +246,36 @@ public class DataflowExceptionAnalysisProvider implements ExceptionAnalysisProvi
return result;
}
private @Nullable Analysis fromClassCastException(@NotNull PsiElement anchor, @Nullable String actualClass) {
private @Nullable AnalysisStartingPoint fromClassCastException(@NotNull PsiElement anchor, @Nullable String actualClass) {
if (!(anchor instanceof PsiTypeElement)) return null;
PsiTypeCastExpression castExpression = tryCast(anchor.getParent(), PsiTypeCastExpression.class);
if (castExpression == null) return null;
PsiExpression ref = extractAnchor(castExpression.getOperand());
PsiExpression ref = AnalysisStartingPoint.extractAnchor(castExpression.getOperand());
if (ref == null) return null;
if (actualClass != null) {
// TODO: support arrays, primitive arrays, inner classes
PsiClass[] classes = JavaPsiFacade.getInstance(myProject).findClasses(actualClass, GlobalSearchScope.allScope(myProject));
if (classes.length == 1) {
return new Analysis(
return new AnalysisStartingPoint(
DfTypes.typedObject(JavaPsiFacade.getElementFactory(myProject).createType(classes[0]), Nullability.NOT_NULL), ref);
}
}
PsiType castType = castExpression.getType();
if (castType != null) {
return tryNegate(new Analysis(DfTypes.typedObject(castType, Nullability.NULLABLE), ref));
return AnalysisStartingPoint.tryNegate(new AnalysisStartingPoint(DfTypes.typedObject(castType, Nullability.NULLABLE), ref));
}
return null;
}
@Nullable
private static Analysis fromAssertionError(@NotNull PsiElement anchor) {
private static AnalysisStartingPoint fromAssertionError(@NotNull PsiElement anchor) {
if (anchor instanceof PsiAssertStatement) {
return tryNegate(fromCondition(((PsiAssertStatement)anchor).getAssertCondition()));
return AnalysisStartingPoint.tryNegate(AnalysisStartingPoint.fromCondition(((PsiAssertStatement)anchor).getAssertCondition()));
}
return null;
}
private @Nullable AnAction createAction(@Nullable Analysis analysis) {
private @Nullable AnAction createAction(@Nullable AnalysisStartingPoint analysis) {
if (analysis == null) return null;
String text = JavaDfaSliceValueFilter.getPresentationText(analysis.myDfType, analysis.myAnchor.getType());
if (text.isEmpty()) return null;
@@ -295,218 +291,4 @@ public class DataflowExceptionAnalysisProvider implements ExceptionAnalysisProvi
}
};
}
private static @Nullable Analysis fromCondition(@Nullable PsiExpression cond) {
cond = PsiUtil.skipParenthesizedExprDown(cond);
if (cond == null) return null;
if (cond instanceof PsiPolyadicExpression) {
IElementType tokenType = ((PsiPolyadicExpression)cond).getOperationTokenType();
if (tokenType.equals(JavaTokenType.ANDAND)) {
Analysis analysis = null;
for (PsiExpression operand : ((PsiPolyadicExpression)cond).getOperands()) {
Analysis next = fromCondition(operand);
if (next == null) return null;
if (analysis == null) {
analysis = next;
}
else {
analysis = analysis.tryMeet(next);
if (analysis == null) return null;
}
}
return analysis;
}
if (tokenType.equals(JavaTokenType.OROR)) {
Analysis analysis = null;
for (PsiExpression operand : ((PsiPolyadicExpression)cond).getOperands()) {
Analysis next = fromCondition(operand);
if (next == null) return null;
if (analysis == null) {
analysis = next;
}
else {
analysis = analysis.tryJoin(next);
if (analysis == null) return null;
}
}
return analysis;
}
}
if (cond instanceof PsiBinaryExpression) {
PsiBinaryExpression binop = (PsiBinaryExpression)cond;
PsiExpression left = PsiUtil.skipParenthesizedExprDown(binop.getLOperand());
PsiExpression right = PsiUtil.skipParenthesizedExprDown(binop.getROperand());
Analysis analysis = fromBinOp(left, binop.getOperationTokenType(), right);
if (analysis != null) return analysis;
return fromBinOp(right, binop.getOperationTokenType(), left);
}
if (cond instanceof PsiInstanceOfExpression) {
PsiTypeElement checkType = ((PsiInstanceOfExpression)cond).getCheckType();
if (checkType == null) return null;
PsiExpression anchor = extractAnchor(((PsiInstanceOfExpression)cond).getOperand());
if (anchor != null) {
DfType typedObject = DfTypes.typedObject(checkType.getType(), Nullability.NOT_NULL);
return new Analysis(typedObject, anchor);
}
}
if (cond instanceof PsiMethodCallExpression) {
PsiMethodCallExpression call = (PsiMethodCallExpression)cond;
if (MethodCallUtils.isEqualsCall(call)) {
PsiExpression qualifier = PsiUtil.skipParenthesizedExprDown(call.getMethodExpression().getQualifierExpression());
PsiExpression argument = PsiUtil.skipParenthesizedExprDown(ArrayUtil.getFirstElement(call.getArgumentList().getExpressions()));
if (qualifier != null && argument != null) {
DfType type = fromConstant(qualifier);
PsiExpression anchor = extractAnchor(argument);
if (type == null) {
type = fromConstant(argument);
anchor = extractAnchor(qualifier);
}
if (type != null && anchor != null) {
PsiType anchorType = anchor.getType();
if (anchorType == null || DfTypes.typedObject(anchorType, Nullability.NOT_NULL).meet(type) == DfTypes.BOTTOM) return null;
return new Analysis(type, anchor);
}
}
}
}
if (BoolUtils.isNegation(cond)) {
Analysis negatedAnalysis = fromCondition(BoolUtils.getNegated(cond));
return tryNegate(negatedAnalysis);
}
PsiExpression anchor = extractAnchor(cond);
if (anchor != null) {
return new Analysis(DfTypes.TRUE, anchor);
}
return null;
}
private static @Nullable Analysis tryNegate(Analysis analysis) {
if (analysis == null) return null;
DfType type = analysis.myDfType.tryNegate();
if (type == null) return null;
NullabilityProblemKind.NullabilityProblem<?> problem = NullabilityProblemKind.fromContext(analysis.myAnchor, Collections.emptyMap());
if (problem != null && CommonClassNames.JAVA_LANG_NULL_POINTER_EXCEPTION.equals(problem.thrownException())) {
type = type.meet(DfTypes.NOT_NULL_OBJECT);
}
return new Analysis(type, analysis.myAnchor);
}
private static @Nullable DfType fromConstant(@NotNull PsiExpression constant) {
if (constant instanceof PsiClassObjectAccessExpression) {
PsiClassObjectAccessExpression classObject = (PsiClassObjectAccessExpression)constant;
PsiTypeElement operand = classObject.getOperand();
return DfTypes.constant(operand.getType(), classObject.getType());
}
if (constant instanceof PsiReferenceExpression) {
PsiElement target = ((PsiReferenceExpression)constant).resolve();
if (target instanceof PsiEnumConstant) {
return DfTypes.constant(target, Objects.requireNonNull(constant.getType()));
}
}
if (ExpressionUtils.isNullLiteral(constant)) {
return DfTypes.NULL;
}
Object value = ExpressionUtils.computeConstantExpression(constant);
if (value != null) {
return DfTypes.constant(value, Objects.requireNonNull(constant.getType()));
}
return null;
}
private static @Nullable Analysis fromBinOp(@Nullable PsiExpression target,
@NotNull IElementType type,
@Nullable PsiExpression constant) {
if (constant == null) return null;
DfType constantType = fromConstant(constant);
if (constantType == null) {
return null;
}
PsiExpression anchor = extractAnchor(target);
if (anchor != null) {
PsiType anchorType = anchor.getType();
if (anchorType == null || TypeUtils.isJavaLangString(anchorType)) return null;
if (anchorType.equals(PsiType.BYTE) || anchorType.equals(PsiType.CHAR) || anchorType.equals(PsiType.SHORT)) {
anchorType = PsiType.INT;
}
if (constantType == DfTypes.NULL || DfTypes.typedObject(anchorType, Nullability.NOT_NULL).meet(constantType) != DfTypes.BOTTOM) {
if (type.equals(JavaTokenType.EQEQ)) {
return new Analysis(constantType, anchor);
}
if (type.equals(JavaTokenType.NE)) {
return tryNegate(new Analysis(constantType, anchor));
}
}
}
RelationType relationType = RelationType.fromElementType(type);
if (relationType != null) {
LongRangeSet set = DfLongType.extractRange(constantType).fromRelation(relationType);
if (anchor == null) {
if (target instanceof PsiBinaryExpression) {
PsiBinaryExpression binOp = (PsiBinaryExpression)target;
IElementType tokenType = binOp.getOperationTokenType();
if (tokenType.equals(JavaTokenType.PERC)) {
anchor = extractAnchor(binOp.getLOperand());
if (anchor != null) {
Object divisor = ExpressionUtils.computeConstantExpression(binOp.getROperand());
if (!(divisor instanceof Integer) && !(divisor instanceof Long)) return null;
set = LongRangeSet.fromRemainder(((Number)divisor).longValue(), set);
}
}
}
if (anchor == null) return null;
}
PsiType anchorType = anchor.getType();
if (PsiType.LONG.equals(anchorType)) {
return new Analysis(DfTypes.longRange(set), anchor);
}
if (PsiType.INT.equals(anchorType) ||
PsiType.SHORT.equals(anchorType) ||
PsiType.BYTE.equals(anchorType) ||
PsiType.CHAR.equals(anchorType)) {
set = set.intersect(Objects.requireNonNull(LongRangeSet.fromType(anchorType)));
return new Analysis(DfTypes.intRangeClamped(set), anchor);
}
}
return null;
}
@Nullable
private static PsiExpression extractAnchor(@Nullable PsiExpression target) {
target = PsiUtil.skipParenthesizedExprDown(target);
if (target instanceof PsiReferenceExpression || target instanceof PsiMethodCallExpression) {
return target;
}
return null;
}
private static class Analysis {
final DfType myDfType;
final PsiExpression myAnchor;
private Analysis(DfType type, PsiExpression anchor) {
myDfType = type;
myAnchor = anchor;
}
private @Nullable Analysis tryMeet(@NotNull Analysis next) {
if (!EquivalenceChecker.getCanonicalPsiEquivalence().expressionsAreEquivalent(this.myAnchor, next.myAnchor)) return null;
DfType meet = this.myDfType.meet(next.myDfType);
if (meet == DfTypes.BOTTOM) return null;
return new Analysis(meet, this.myAnchor);
}
private @Nullable Analysis tryJoin(@NotNull Analysis next) {
if (!EquivalenceChecker.getCanonicalPsiEquivalence().expressionsAreEquivalent(this.myAnchor, next.myAnchor)) return null;
DfType meet = this.myDfType.join(next.myDfType);
if (meet == DfTypes.TOP) return null;
return new Analysis(meet, this.myAnchor);
}
private static @Nullable Analysis create(@NotNull DfType type, @Nullable PsiExpression anchor) {
anchor = extractAnchor(anchor);
if (anchor == null) return null;
if (DfTypes.typedObject(anchor.getType(), Nullability.UNKNOWN).meet(type) == DfTypes.BOTTOM) return null;
return new Analysis(type, anchor);
}
}
}
@@ -18,16 +18,21 @@ import java.util.Objects;
public class JavaDfaSliceValueFilter implements SliceValueFilter {
private final @Nullable JavaDfaSliceValueFilter myNextFilter;
private final @NotNull DfType myDfType;
private JavaDfaSliceValueFilter(@Nullable JavaDfaSliceValueFilter nextFilter, @NotNull DfType type) {
myNextFilter = nextFilter;
myDfType = type;
}
public JavaDfaSliceValueFilter(@NotNull DfType type) {
this(null, type);
}
@NotNull DfType getDfType() {
return myDfType;
}
JavaDfaSliceValueFilter wrap() {
return new JavaDfaSliceValueFilter(this, DfTypes.TOP);
@@ -38,6 +38,7 @@ import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.refactoring.util.RefactoringChangeUtil;
import com.intellij.util.ArrayUtilRt;
import com.intellij.util.CommonProcessors;
import com.intellij.util.ObjectUtils;
import com.intellij.util.Processor;
import com.intellij.util.containers.ContainerUtil;
import gnu.trove.THashMap;
@@ -46,10 +47,7 @@ import org.intellij.lang.annotations.Flow;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import java.util.*;
/**
* @author cdr
@@ -184,6 +182,14 @@ class SliceUtil {
return processUsagesFlownDownTo(rExpression, processor, builder);
}
}
JavaDfaSliceValueFilter filter = ObjectUtils.tryCast(builder.getParent().params.valueFilter, JavaDfaSliceValueFilter.class);
if (filter != null && expression instanceof PsiExpression) {
AnalysisStartingPoint analysis = AnalysisStartingPoint.propagateThroughExpression(expression, filter.getDfType());
if (analysis != null) {
return builder.withFilter(new JavaDfaSliceValueFilter(analysis.myDfType)).process(analysis.myAnchor, processor);
}
}
if (builder.hasNesting()) {
// consider container creation
PsiElement initializer = expression instanceof PsiNewExpression ? ((PsiNewExpression)expression).getArrayInitializer() : expression;
@@ -0,0 +1,13 @@
class Test {
void test() {
foo(<flown1111>"xyz");
foo(123);
}
void foo(Object <flown111>obj) {
boolean b = <flown1><flown11>obj instanceof String;
if (<caret>b) {
}
}
}
@@ -0,0 +1,13 @@
class Test {
void test() {
foo("xyz");
foo(<flown1111>123);
}
void foo(Object <flown111>obj) {
boolean b = <flown1><flown11>obj instanceof String;
if (<caret>b) {
}
}
}
@@ -0,0 +1,27 @@
import java.util.Random;
class Test {
public static void main(String[] args) {
int[] data = new int[10];
int i = <flown1><flown11>getIndex() % data.length;
System.out.println(data[<caret>i]);
}
private static int abs(int <flown1111111>x) {
return <flown1111>x < 0 ? <flown11111>-<flown111111>x : x;
}
private static int getIndex() {
return <flown111>abs(<flown11111111>getCode());
}
private static int getCode() {
switch (new Random().nextInt() % 4) {
case 0: return 10;
case 1: return Integer.MAX_VALUE;
case 2: return -10;
case 3: return <flown111111111>0x80000000;
default: return 12345;
}
}
}
@@ -62,7 +62,7 @@ public class DataflowExceptionAnalysisProviderTest extends LightJavaCodeInsightT
public void testAssertDivisibility() {
doTest("java.lang.AssertionError",
"Find why 'i' could be odd",
"Find why 'i % 2' could be != 0",
"class X {static void test(int i) {assert i % 2 == 0;}}");
}
@@ -96,4 +96,7 @@ public class SliceBackwardTest extends SliceTestCase {
public void testFilterIntRangeArray() throws Exception { doTest(">=0");}
public void testFilterNull() throws Exception { doTest("null");}
public void testNarrowFilter() throws Exception { doTest();}
public void testFilterPropagateMath() throws Exception { doTest("-8");}
public void testFilterPropagateBoolean() throws Exception { doTest("true");}
public void testFilterPropagateBoolean2() throws Exception { doTest("false");}
}