mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IDEA-240403 Analyze dataflow to here: perform backpropagation through simple expressions
GitOrigin-RevId: 6c7d2e9f30121748c624a20ab14cbcf7dd7c71ec
This commit is contained in:
committed by
intellij-monorepo-bot
parent
646b343400
commit
7d4c5ea4d8
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -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");}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user