public void onMatchMethod()

in nullaway/src/main/java/com/uber/nullaway/handlers/contract/ContractCheckHandler.java [62:165]


  public void onMatchMethod(MethodTree tree, MethodAnalysisContext methodAnalysisContext) {
    Symbol.MethodSymbol callee = ASTHelpers.getSymbol(tree);
    Preconditions.checkNotNull(callee);
    // Check to see if this method has an @Contract annotation
    String contractString = ContractUtils.getContractString(callee, config);
    if (contractString != null) {
      // Found a contract, lets parse it.
      String[] clauses = contractString.split(";");
      if (clauses.length != 1) {
        return;
      }

      String clause = clauses[0];
      NullAway analysis = methodAnalysisContext.analysis();
      VisitorState state = methodAnalysisContext.state();
      String[] antecedent =
          getAntecedent(clause, tree, analysis, state, callee, tree.getParameters().size());
      String consequent = getConsequent(clause, tree, analysis, state, callee);

      boolean supported = true;

      for (int i = 0; i < antecedent.length; ++i) {
        String valueConstraint = antecedent[i].trim();
        if (!(valueConstraint.equals("_")
            || valueConstraint.equals("!null")
            || valueConstraint.equals("null"))) {
          supported = false;
        }
      }

      if (!consequent.equals("!null")) {
        supported = false;
      }

      if (!supported) {
        return;
      }

      // we scan the method tree for the return nodes and check the contract
      new TreePathScanner<Void, Void>() {
        @Override
        public Void visitReturn(ReturnTree returnTree, Void unused) {

          VisitorState returnState = state.withPath(getCurrentPath());
          Nullness nullness =
              analysis
                  .getNullnessAnalysis(returnState)
                  .getNullnessForContractDataflow(
                      new TreePath(returnState.getPath(), returnTree.getExpression()),
                      returnState.context);

          if (nullness == Nullness.NULLABLE || nullness == Nullness.NULL) {

            String errorMessage;

            // used for error message
            int nonNullAntecedentCount = 0;
            int nonNullAntecedentPosition = -1;

            for (int i = 0; i < antecedent.length; ++i) {
              String valueConstraint = antecedent[i].trim();

              if (valueConstraint.equals("!null")) {
                nonNullAntecedentCount += 1;
                nonNullAntecedentPosition = i;
              }
            }

            if (nonNullAntecedentCount == 1) {

              errorMessage =
                  "Method "
                      + callee.name
                      + " has @Contract("
                      + contractString
                      + "), but this appears to be violated, as a @Nullable value may be returned when parameter "
                      + tree.getParameters().get(nonNullAntecedentPosition).getName()
                      + " is non-null.";
            } else {
              errorMessage =
                  "Method "
                      + callee.name
                      + " has @Contract("
                      + contractString
                      + "), but this appears to be violated, as a @Nullable value may be returned "
                      + "when the contract preconditions are true.";
            }

            returnState.reportMatch(
                analysis
                    .getErrorBuilder()
                    .createErrorDescription(
                        new ErrorMessage(
                            ErrorMessage.MessageTypes.ANNOTATION_VALUE_INVALID, errorMessage),
                        returnTree,
                        analysis.buildDescription(returnTree),
                        returnState,
                        null));
          }
          return super.visitReturn(returnTree, null);
        }
      }.scan(state.getPath(), null);
    }
  }