private boolean mayBeNullExpr()

in nullaway/src/main/java/com/uber/nullaway/NullAway.java [2552:2683]


  private boolean mayBeNullExpr(VisitorState state, ExpressionTree expr) {
    expr = 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:
      case NEW_ARRAY:
      case ARRAY_TYPE:
      // Lambdas may return null, but the lambda literal itself should not be null
      case LAMBDA_EXPRESSION:
      // These cannot be null; the compiler would catch it
      case MEMBER_REFERENCE:
      // result of compound assignment cannot be null
      case MULTIPLY_ASSIGNMENT:
      case DIVIDE_ASSIGNMENT:
      case REMAINDER_ASSIGNMENT:
      case PLUS_ASSIGNMENT:
      case MINUS_ASSIGNMENT:
      case LEFT_SHIFT_ASSIGNMENT:
      case RIGHT_SHIFT_ASSIGNMENT:
      case UNSIGNED_RIGHT_SHIFT_ASSIGNMENT:
      case AND_ASSIGNMENT:
      case XOR_ASSIGNMENT:
      case OR_ASSIGNMENT:
      // rest are for auto-boxing
      case PLUS:
      case MINUS:
      case MULTIPLY:
      case DIVIDE:
      case REMAINDER:
      case CONDITIONAL_AND:
      case CONDITIONAL_OR:
      case BITWISE_COMPLEMENT:
      case LOGICAL_COMPLEMENT:
      case INSTANCE_OF:
      case PREFIX_INCREMENT:
      case PREFIX_DECREMENT:
      case POSTFIX_DECREMENT:
      case POSTFIX_INCREMENT:
      case EQUAL_TO:
      case NOT_EQUAL_TO:
      case GREATER_THAN:
      case GREATER_THAN_EQUAL:
      case LESS_THAN:
      case LESS_THAN_EQUAL:
      case UNARY_MINUS:
      case UNARY_PLUS:
      case AND:
      case OR:
      case XOR:
      case LEFT_SHIFT:
      case RIGHT_SHIFT:
      case UNSIGNED_RIGHT_SHIFT:
        // clearly not null
        return false;
      default:
        break;
    }
    // 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);
          }
        }
        break;
      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);
        break;
      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;
        }
        break;
      case METHOD_INVOCATION:
        if (!(exprSymbol instanceof Symbol.MethodSymbol)) {
          throw new IllegalStateException(
              "unexpected symbol "
                  + exprSymbol
                  + " for method invocation "
                  + state.getSourceForNode(expr));
        }
        exprMayBeNull =
            mayBeNullMethodCall(
                (Symbol.MethodSymbol) exprSymbol, (MethodInvocationTree) expr, state);
        break;
      case CONDITIONAL_EXPRESSION:
      case ASSIGNMENT:
        exprMayBeNull = true;
        break;
      default:
        // match switch expressions by comparing strings, so the code compiles on JDK versions < 12
        if (expr.getKind().name().equals("SWITCH_EXPRESSION")) {
          exprMayBeNull = true;
        } else {
          throw new RuntimeException(
              "whoops, better handle " + expr.getKind() + " " + state.getSourceForNode(expr));
        }
    }
    exprMayBeNull = handler.onOverrideMayBeNullExpr(this, expr, exprSymbol, state, exprMayBeNull);
    return exprMayBeNull && nullnessFromDataflow(state, expr);
  }