CauseItem (MemoryStateChange factUse, DfaValue value, LongRangeSet range, @Nls String template) { if (value instanceof DfaTypeValue && factUse.myInstruction.getIndex() == -1) { return null; } if (value instanceof DfaVariableValue) { VariableDescriptor descriptor = ((DfaVariableValue)value).getDescriptor(); if (descriptor instanceof SpecialField && range.equals(JvmPsiRangeSetUtil.indexRange())) { switch (((SpecialField)descriptor)) { case ARRAY_LENGTH -> { return new CauseItem(JavaAnalysisBundle.message("dfa.find.cause.array.length.is.always.non.negative"), factUse); } case STRING_LENGTH -> { return new CauseItem(JavaAnalysisBundle.message("dfa.find.cause.string.length.is.always.non.negative"), factUse); } case COLLECTION_SIZE -> { return new CauseItem(JavaAnalysisBundle.message("dfa.find.cause.collection.size.is.always.non.negative"), factUse); } default -> { } } } } PsiExpression expression = factUse.myTopOfStack == value ? factUse.getExpression() : null; if (expression != null) { PsiType type = expression.getType(); if (expression instanceof PsiLiteralExpression) { return null; // Literal range is quite evident } if (expression instanceof PsiMethodCallExpression call) { PsiMethod method = call.resolveMethod(); if (method != null) { LongRangeSet fromAnnotation = JvmPsiRangeSetUtil.fromPsiElement(method); if (fromAnnotation.equals(range)) { return new CauseItem(JavaAnalysisBundle.message("dfa.find.cause.range.is.specified.by.annotation", method.getName(), range), call.getMethodExpression().getReferenceNameElement()); } } } if (expression instanceof PsiTypeCastExpression && type instanceof PsiPrimitiveType && TypeConversionUtil.isNumericType(type)) { PsiExpression operand = ((PsiTypeCastExpression)expression).getOperand(); MemoryStateChange operandPush = factUse.findExpressionPush(operand); if (operandPush != null) { DfaValue castedValue = operandPush.myTopOfStack; FactDefinition<LongRangeSet> operandInfo = operandPush.findFact(castedValue, FactExtractor.range()); LongRangeSet operandRange = operandInfo.myFact; LongRangeSet result = JvmPsiRangeSetUtil.castTo(operandRange, (PsiPrimitiveType)type); if (range.equals(result)) { CauseItem cause = new CauseItem(new RangeDfaProblemType( JavaAnalysisBundle.message("dfa.find.cause.result.of.primitive.cast.template", type.getCanonicalText()), range, null), expression); if (!operandRange.equals(JvmPsiRangeSetUtil.typeRange(operand.getType()))) { cause.addChildren(findRangeCause(operandPush, castedValue, operandRange, JavaAnalysisBundle.message("dfa.find.cause.numeric.cast.operand.template"))); } return cause; } } } if (range.equals(JvmPsiRangeSetUtil.typeRange(type))) { return null; // Range is any value of given type: no need to explain (except narrowing cast) } if (PsiTypes.longType().equals(type) || PsiTypes.intType().equals(type)) { if (expression instanceof PsiBinaryExpression binOp) { LongRangeType lrType = PsiTypes.longType().equals(type) ? LongRangeType.INT64 : LongRangeType.INT32; PsiExpression left = PsiUtil.skipParenthesizedExprDown(binOp.getLOperand()); PsiExpression right = PsiUtil.skipParenthesizedExprDown(binOp.getROperand()); MemoryStateChange leftPush = factUse.findExpressionPush(left); MemoryStateChange rightPush = factUse.findExpressionPush(right); if (leftPush != null && rightPush != null) { DfaValue leftVal = leftPush.myTopOfStack; FactDefinition<LongRangeSet> leftSet = leftPush.findFact(leftVal, FactExtractor.range()); DfaValue rightVal = rightPush.myTopOfStack; FactDefinition<LongRangeSet> rightSet = rightPush.findFact(rightVal, FactExtractor.range()); LongRangeSet fromType = Objects.requireNonNull(JvmPsiRangeSetUtil.typeRange(type)); LongRangeSet leftRange = leftSet.myFact.meet(fromType); LongRangeSet rightRange = rightSet.myFact.meet(fromType); LongRangeBinOp op = JvmPsiRangeSetUtil.binOpFromToken(binOp.getOperationTokenType()); if (op != null) { LongRangeSet result = op.eval(leftRange, rightRange, lrType); if (range.equals(result)) { String sign = binOp.getOperationSign().getText(); CauseItem cause = new CauseItem(new RangeDfaProblemType( JavaAnalysisBundle.message("dfa.find.cause.result.of.numeric.operation.template", sign.equals("%") ? "%%" : sign), range, ObjectUtils.tryCast(type, PsiPrimitiveType.class)), factUse); CauseItem leftCause = null, rightCause = null; if (!leftRange.equals(fromType)) { leftCause = findRangeCause(leftPush, leftVal, leftRange, JavaAnalysisBundle.message("dfa.find.cause.left.operand.range.template")); } if (!rightRange.equals(fromType)) { rightCause = findRangeCause(rightPush, rightVal, rightRange, JavaAnalysisBundle.message("dfa.find.cause.right.operand.range.template")); } cause.addChildren(leftCause, rightCause); return cause; } } } } } } PsiPrimitiveType type = expression != null ? ObjectUtils.tryCast(expression.getType(), PsiPrimitiveType.class) : null; CauseItem item = new CauseItem(new RangeDfaProblemType(template, range, type), factUse); FactDefinition<LongRangeSet> info = factUse.findFact(value, FactExtractor.range()); while ((!info.myFact.equals(range))) { if (info.myChange == null) break; MemoryStateChange previous = info.myChange.getPrevious(); if (previous == null) break; info = previous.findFact(value, FactExtractor.range()); } MemoryStateChange factDef = range.equals(info.myFact) ? info.myChange : null; if (factDef != null) { if (factDef.myInstruction instanceof AssignInstruction && factDef.myTopOfStack == value) { PsiExpression rExpression = ((AssignInstruction)factDef.myInstruction).getRExpression(); if (rExpression != null) { MemoryStateChange rValuePush = factDef.findSubExpressionPush(rExpression); if (rValuePush != null) { CauseItem assignmentItem = createAssignmentCause((AssignInstruction)factDef.myInstruction, value); assignmentItem.addChildren(findRangeCause(rValuePush, rValuePush.myTopOfStack, range, JavaAnalysisBundle.message("dfa.find.cause.numeric.range.generic.template"))); item.addChildren(assignmentItem); return item; } } } PsiExpression defExpression = factDef.getExpression(); if (defExpression != null) { item.addChildren(new CauseItem(JavaAnalysisBundle.message("dfa.find.cause.range.is.known.from.place"), defExpression)); } } return item; }