@Override
  public boolean tryInlineCall(@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", "contains", "containsSame", "containsInstanceOf" -> {
        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;
  }
@Override
  public boolean tryInlineCall(@NotNull CFGBuilder builder, @NotNull PsiMethodCallExpression call)