boolean (@NotNull CFGBuilder builder, @NotNull PsiMethodCallExpression call) { if (!ASSERT.matches(call)) return false; List<PsiExpression> intermediateArgs = new ArrayList<>(); PsiMethodCallExpression qualifier = MethodCallUtils.getQualifierMethodCall(call); while (INTERMEDIATE.matches(qualifier)) { intermediateArgs.addAll(0, Arrays.asList(qualifier.getArgumentList().getExpressions())); qualifier = MethodCallUtils.getQualifierMethodCall(qualifier); } if (!ASSERT_THAT.matches(qualifier)) return false; PsiExpression valueToCheck = qualifier.getArgumentList().getExpressions()[0]; builder.pushExpression(valueToCheck); for (PsiExpression arg : intermediateArgs) { builder.pushExpression(arg); } builder.splice(intermediateArgs.size()); PsiExpression[] args = call.getArgumentList().getExpressions(); for (PsiExpression arg : args) { builder.pushExpression(arg); } builder.splice(args.length); PsiType type = valueToCheck.getType(); SpecialField field = SpecialField.fromQualifierType(DfTypes.typedObject(type, Nullability.UNKNOWN)); // Note that `new ContractFailureProblem(call)` cannot be extracted to a variable here, as its identity is necessary // for analysis (see DataFlowInstructionVisitor#myFailingCalls) boolean container = field == SpecialField.COLLECTION_SIZE || field == SpecialField.ARRAY_LENGTH || field == SpecialField.STRING_LENGTH || field == SpecialField.OPTIONAL_VALUE; switch (Objects.requireNonNull(call.getMethodExpression().getReferenceName())) { case "isNotNull", "have", "haveAtLeast", "haveAtLeastOne", "haveAtMost", "haveExactly", "hasOnlyElementsOfType", "hasOnlyElementsOfTypes" -> builder.ensure(RelationType.NE, DfTypes.NULL, new ContractFailureProblem(call), JAVA_LANG_ASSERTION_ERROR); case "isNull" -> builder.ensure(RelationType.EQ, DfTypes.NULL, new ContractFailureProblem(call), JAVA_LANG_ASSERTION_ERROR); case "isPresent", "isNotEmpty", "isNotBlank" -> { builder.ensure(RelationType.NE, DfTypes.NULL, new ContractFailureProblem(call), JAVA_LANG_ASSERTION_ERROR); if (container) { builder.unwrap(field); builder.ensure(RelationType.NE, field == SpecialField.OPTIONAL_VALUE ? DfTypes.NULL : DfTypes.intValue(0), new ContractFailureProblem(call), JAVA_LANG_ASSERTION_ERROR); } } case "isNotPresent", "isEmpty" -> { builder.ensure(RelationType.NE, DfTypes.NULL, new ContractFailureProblem(call), JAVA_LANG_ASSERTION_ERROR); if (container) { builder.unwrap(field); builder.ensure(RelationType.EQ, field == SpecialField.OPTIONAL_VALUE ? DfTypes.NULL : DfTypes.intValue(0), new ContractFailureProblem(call), JAVA_LANG_ASSERTION_ERROR); } } case "isTrue" -> { if (PsiTypes.booleanType().equals(type) || TypeUtils.typeEquals(JAVA_LANG_BOOLEAN, type)) { builder .boxUnbox(valueToCheck, PsiTypes.booleanType()) .ensure(RelationType.EQ, DfTypes.TRUE, new ContractFailureProblem(call), JAVA_LANG_ASSERTION_ERROR); } else if (type instanceof PsiClassType) { builder.ensure(RelationType.NE, DfTypes.NULL, new ContractFailureProblem(call), JAVA_LANG_ASSERTION_ERROR); } } case "isFalse" -> { if (PsiTypes.booleanType().equals(type) || TypeUtils.typeEquals(JAVA_LANG_BOOLEAN, type)) { builder .boxUnbox(valueToCheck, PsiTypes.booleanType()) .ensure(RelationType.EQ, DfTypes.FALSE, new ContractFailureProblem(call), JAVA_LANG_ASSERTION_ERROR); } else if (type instanceof PsiClassType) { builder.ensure(RelationType.NE, DfTypes.NULL, new ContractFailureProblem(call), JAVA_LANG_ASSERTION_ERROR); } } case "hasSize" -> sizeLimit(builder, call, field, RelationType.EQ); case "hasSizeGreaterThan" -> sizeLimit(builder, call, field, RelationType.GT); case "hasSizeLessThan" -> sizeLimit(builder, call, field, RelationType.LT); case "hasSizeGreaterThanOrEqualTo" -> sizeLimit(builder, call, field, RelationType.GE); case "hasSizeLessThanOrEqualTo" -> sizeLimit(builder, call, field, RelationType.LE); case "hasSizeBetween" -> { builder.ensure(RelationType.NE, DfTypes.NULL, new ContractFailureProblem(call), JAVA_LANG_ASSERTION_ERROR); if ((field == SpecialField.COLLECTION_SIZE || field == SpecialField.ARRAY_LENGTH) && args.length == 2) { Integer min = ObjectUtils.tryCast(ExpressionUtils.computeConstantExpression(args[0]), Integer.class); Integer max = ObjectUtils.tryCast(ExpressionUtils.computeConstantExpression(args[1]), Integer.class); builder.unwrap(field); if (min != null) { builder.ensure(RelationType.GE, DfTypes.intValue(min), new ContractFailureProblem(call), JAVA_LANG_ASSERTION_ERROR); } if (max != null) { builder.ensure(RelationType.LE, DfTypes.intValue(max), new ContractFailureProblem(call), JAVA_LANG_ASSERTION_ERROR); } } } } builder.pop().pushUnknown(); return true; }