private boolean mayBeNullExpr()

in nullaway/src/main/java/com/uber/nullaway/NullAway.java [2664:2786]


  private boolean mayBeNullExpr(VisitorState state, ExpressionTree expr) {
    expr = NullabilityUtil.stripParensAndCasts(expr);
    if (ASTHelpers.constValue(expr) != null) {
      // This should include literals such as "true" or a string
      // obviously not null
      return false;
    }
    // return early for expressions that no handler overrides and will not need dataflow analysis
    switch (expr.getKind()) {
      case NULL_LITERAL -> {
        // obviously null
        return true;
      }
      case NEW_CLASS,
          NEW_ARRAY,
          ARRAY_TYPE,
          // Lambdas may return null, but the lambda literal itself should not be null
          LAMBDA_EXPRESSION,
          // These cannot be null; the compiler would catch it
          MEMBER_REFERENCE,
          // result of compound assignment cannot be null
          MULTIPLY_ASSIGNMENT,
          DIVIDE_ASSIGNMENT,
          REMAINDER_ASSIGNMENT,
          PLUS_ASSIGNMENT,
          MINUS_ASSIGNMENT,
          LEFT_SHIFT_ASSIGNMENT,
          RIGHT_SHIFT_ASSIGNMENT,
          UNSIGNED_RIGHT_SHIFT_ASSIGNMENT,
          AND_ASSIGNMENT,
          XOR_ASSIGNMENT,
          OR_ASSIGNMENT,
          // rest are for auto-boxing
          PLUS,
          MINUS,
          MULTIPLY,
          DIVIDE,
          REMAINDER,
          CONDITIONAL_AND,
          CONDITIONAL_OR,
          BITWISE_COMPLEMENT,
          LOGICAL_COMPLEMENT,
          INSTANCE_OF,
          PREFIX_INCREMENT,
          PREFIX_DECREMENT,
          POSTFIX_DECREMENT,
          POSTFIX_INCREMENT,
          EQUAL_TO,
          NOT_EQUAL_TO,
          GREATER_THAN,
          GREATER_THAN_EQUAL,
          LESS_THAN,
          LESS_THAN_EQUAL,
          UNARY_MINUS,
          UNARY_PLUS,
          AND,
          OR,
          XOR,
          LEFT_SHIFT,
          RIGHT_SHIFT,
          UNSIGNED_RIGHT_SHIFT -> {
        // clearly not null
        return false;
      }
      default -> {}
    }
    // the logic here is to avoid doing dataflow analysis whenever possible
    Symbol exprSymbol = ASTHelpers.getSymbol(expr);
    boolean exprMayBeNull;
    switch (expr.getKind()) {
      case ARRAY_ACCESS -> {
        // Outside JSpecify mode, we assume array contents are always non-null
        exprMayBeNull = false;
        if (config.isJSpecifyMode()) {
          // In JSpecify mode, we check if the array element type is nullable
          ArrayAccessTree arrayAccess = (ArrayAccessTree) expr;
          ExpressionTree arrayExpr = arrayAccess.getExpression();
          Symbol arraySymbol = ASTHelpers.getSymbol(arrayExpr);
          if (arraySymbol != null) {
            exprMayBeNull = NullabilityUtil.isArrayElementNullable(arraySymbol, config);
          }
        }
      }
      case MEMBER_SELECT -> {
        if (exprSymbol == null) {
          throw new IllegalStateException(
              "unexpected null symbol for dereference expression " + state.getSourceForNode(expr));
        }
        exprMayBeNull =
            NullabilityUtil.mayBeNullFieldFromType(exprSymbol, config, handler, codeAnnotationInfo);
      }
      case IDENTIFIER -> {
        if (exprSymbol == null) {
          throw new IllegalStateException(
              "unexpected null symbol for identifier " + state.getSourceForNode(expr));
        }
        if (exprSymbol.getKind() == ElementKind.FIELD) {
          exprMayBeNull =
              NullabilityUtil.mayBeNullFieldFromType(
                  exprSymbol, config, handler, codeAnnotationInfo);
        } else {
          // rely on dataflow analysis for local variables
          exprMayBeNull = true;
        }
      }
      case METHOD_INVOCATION -> {
        if (!(exprSymbol instanceof Symbol.MethodSymbol methodSymbol)) {
          throw new IllegalStateException(
              "unexpected symbol "
                  + exprSymbol
                  + " for method invocation "
                  + state.getSourceForNode(expr));
        }
        exprMayBeNull = mayBeNullMethodCall(methodSymbol, (MethodInvocationTree) expr, state);
      }
      case CONDITIONAL_EXPRESSION, ASSIGNMENT, SWITCH_EXPRESSION -> exprMayBeNull = true;
      default ->
          throw new RuntimeException(
              "whoops, better handle " + expr.getKind() + " " + state.getSourceForNode(expr));
    }
    exprMayBeNull = handler.onOverrideMayBeNullExpr(this, expr, exprSymbol, state, exprMayBeNull);
    return exprMayBeNull && nullnessFromDataflow(state, expr);
  }