// Test file for nullness parameter and return checks.

import org.checkerframework.checker.nullness.qual.*;

interface Noop {
  void noop();
}

interface FunctionNull<T extends @Nullable Object, R> {
  R apply(T t);
}

interface Supplier<R extends @Nullable Object> {
  R supply();
}

interface BiFunctionNull<T, U, R> {
  R apply(T t, U u);
}

public class LambdaNullness {

  // Annotations in lamba expressions, in static, instance of fields initializers are stored on
  // the last declared constructor.
  //
  // For example, the annotation for @Nullable Integer x on f7's initializer
  // is stored on here because it is the last defined constructor.
  //
  // See TypeFromElement::annotateParam
  LambdaNullness(FunctionNull<String, String> f, Object e) {}

  // No parameters; result is void
  Noop f1 = () -> {};
  // No parameters, expression body
  Supplier<Integer> f2a = () -> 42;

  // No parameters, expression body
  // :: error: (return)
  Supplier<Integer> f2b = () -> null;

  // No parameters, expression body
  Supplier<@Nullable Void> f3 = () -> null;
  // No parameters, block body with return
  Supplier<Integer> f4a =
      () -> {
        return 42;
      };
  // No parameters, block body with return
  Supplier<@Nullable Integer> f4b =
      () -> {
        // :: error: (assignment)
        @NonNull String s = null;

        return null;
      };
  // No parameters, void block body
  Noop f5 =
      () -> {
        System.gc();
      };

  // Complex block body with returns
  Supplier<Integer> f6 =
      () -> {
        if (true) {
          return 12;
        } else {
          int result = 15;
          for (int i = 1; i < 10; i++) {
            result *= i;
          }
          // :: error: (return)
          return null;
        }
      };

  // Single declared-type parameter
  FunctionNull<@Nullable Integer, Integer> f7 = (@Nullable Integer x) -> 1;

  // Single declared-type parameter
  FunctionNull<@Nullable String, String> f9 =
      // :: error: (lambda.param)
      (@NonNull String x) -> {
        return x + "";
      };
  // Single inferred-type parameter
  FunctionNull<@NonNull Integer, Integer> f10 = (x) -> x + 1;
  // Parentheses optional for single
  FunctionNull<@Nullable Integer, Integer> f11 = x -> 1;

  // Multiple declared-type parameters
  BiFunctionNull<Integer, Integer, Integer> f16 =
      (@Nullable Integer x, final Integer y) -> {
        x = null;
        // :: error: (unboxing.of.nullable)
        return x + y;
      };

  // Multiple inferred-type parameters
  BiFunctionNull<String, String, String> f18 = (x, y) -> x + y;

  // Infer based on context.
  FunctionNull<@Nullable String, String> fn =
      (s) -> {
        // :: error: (dereference.of.nullable)
        s.toString();
        return "";
      };
}
