001/*
002 * Copyright (C) 2005 The Guava Authors
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package com.google.common.testing;
018
019import static com.google.common.base.Preconditions.checkArgument;
020import static com.google.common.base.Preconditions.checkNotNull;
021import static java.util.Arrays.stream;
022import static java.util.Objects.requireNonNull;
023import static java.util.stream.Stream.concat;
024
025import com.google.common.annotations.GwtIncompatible;
026import com.google.common.annotations.J2ktIncompatible;
027import com.google.common.base.Converter;
028import com.google.common.collect.ClassToInstanceMap;
029import com.google.common.collect.ImmutableList;
030import com.google.common.collect.ImmutableSet;
031import com.google.common.collect.MutableClassToInstanceMap;
032import com.google.common.reflect.Invokable;
033import com.google.common.reflect.Parameter;
034import com.google.common.reflect.Reflection;
035import com.google.common.reflect.TypeToken;
036import com.google.common.util.concurrent.AbstractFuture;
037import com.google.errorprone.annotations.CanIgnoreReturnValue;
038import java.lang.annotation.Annotation;
039import java.lang.reflect.AnnotatedType;
040import java.lang.reflect.Constructor;
041import java.lang.reflect.InvocationTargetException;
042import java.lang.reflect.Member;
043import java.lang.reflect.Method;
044import java.lang.reflect.Modifier;
045import java.lang.reflect.ParameterizedType;
046import java.lang.reflect.Type;
047import java.lang.reflect.TypeVariable;
048import java.util.ArrayList;
049import java.util.Arrays;
050import java.util.HashMap;
051import java.util.List;
052import java.util.Map;
053import java.util.Objects;
054import junit.framework.Assert;
055import org.jspecify.annotations.NullMarked;
056import org.jspecify.annotations.Nullable;
057
058/**
059 * A test utility that verifies that your methods and constructors throw {@link
060 * NullPointerException} or {@link UnsupportedOperationException} whenever null is passed to a
061 * parameter whose declaration or type isn't annotated with an annotation with the simple name
062 * {@code Nullable}, {@code CheckForNull}, {@code NullableType}, or {@code NullableDecl}.
063 *
064 * <p>The tested methods and constructors are invoked -- each time with one parameter being null and
065 * the rest not null -- and the test fails if no expected exception is thrown. {@code
066 * NullPointerTester} uses best effort to pick non-null default values for many common JDK and Guava
067 * types, and also for interfaces and public classes that have public parameter-less constructors.
068 * When the non-null default value for a particular parameter type cannot be provided by {@code
069 * NullPointerTester}, the caller can provide a custom non-null default value for the parameter type
070 * via {@link #setDefault}.
071 *
072 * @author Kevin Bourrillion
073 * @since 10.0
074 */
075@GwtIncompatible
076@J2ktIncompatible
077@NullMarked
078public final class NullPointerTester {
079
080  private final ClassToInstanceMap<Object> defaults = MutableClassToInstanceMap.create();
081  private final List<Member> ignoredMembers = new ArrayList<>();
082
083  private ExceptionTypePolicy policy = ExceptionTypePolicy.NPE_OR_UOE;
084
085  /*
086   * Requiring desugaring for guava-*testlib* is likely safe, at least for the reflection-based
087   * NullPointerTester. But if you are a user who is reading this because this change caused you
088   * trouble, please let us know: https://github.com/google/guava/issues/new
089   */
090  /** Constructs a new {@code NullPointerTester}. */
091  @IgnoreJRERequirement
092  public NullPointerTester() {
093    try {
094      /*
095       * Converter.apply has a non-nullable parameter type but doesn't throw for null arguments. For
096       * more information, see the comments in that class.
097       *
098       * We already know that that's how it behaves, and subclasses of Converter can't change that
099       * behavior. So there's no sense in making all subclass authors exclude the method from any
100       * NullPointerTester tests that they have.
101       */
102      ignoredMembers.add(Converter.class.getMethod("apply", Object.class));
103    } catch (NoSuchMethodException shouldBeImpossible) {
104      // Fine: If it doesn't exist, then there's no chance that we're going to be asked to test it.
105    }
106
107    /*
108     * These methods "should" call checkNotNull. However, I'm wary of accidentally introducing
109     * anything that might slow down execution on such a hot path. Given that the methods are only
110     * package-private, I feel OK with just not testing them for NPE.
111     *
112     * Note that testing casValue is particularly dangerous because it uses Unsafe under some
113     * versions of Java, and apparently Unsafe can cause SIGSEGV instead of NPE—almost as if it's
114     * not safe.
115     */
116    concat(
117            stream(AbstractFuture.class.getDeclaredMethods()),
118            stream(requireNonNull(AbstractFuture.class.getSuperclass()).getDeclaredMethods()))
119        .filter(
120            m ->
121                m.getName().equals("getDoneValue")
122                    || m.getName().equals("getKnownSuccessfulDoneValue")
123                    || m.getName().equals("casValue")
124                    || m.getName().equals("casListeners")
125                    || m.getName().equals("gasListeners"))
126        .forEach(ignoredMembers::add);
127  }
128
129  /**
130   * Sets a default value that can be used for any parameter of type {@code type}. Returns this
131   * object.
132   */
133  @CanIgnoreReturnValue
134  public <T> NullPointerTester setDefault(Class<T> type, T value) {
135    defaults.putInstance(type, checkNotNull(value));
136    return this;
137  }
138
139  /**
140   * Ignore {@code method} in the tests that follow. Returns this object.
141   *
142   * @since 13.0
143   */
144  @CanIgnoreReturnValue
145  public NullPointerTester ignore(Method method) {
146    ignoredMembers.add(checkNotNull(method));
147    return this;
148  }
149
150  /**
151   * Ignore {@code constructor} in the tests that follow. Returns this object.
152   *
153   * @since 22.0
154   */
155  @CanIgnoreReturnValue
156  public NullPointerTester ignore(Constructor<?> constructor) {
157    ignoredMembers.add(checkNotNull(constructor));
158    return this;
159  }
160
161  /**
162   * Runs {@link #testConstructor} on every constructor in class {@code c} that has at least {@code
163   * minimalVisibility}.
164   */
165  public void testConstructors(Class<?> c, Visibility minimalVisibility) {
166    for (Constructor<?> constructor : c.getDeclaredConstructors()) {
167      if (minimalVisibility.isVisible(constructor) && !isIgnored(constructor)) {
168        testConstructor(constructor);
169      }
170    }
171  }
172
173  /** Runs {@link #testConstructor} on every public constructor in class {@code c}. */
174  public void testAllPublicConstructors(Class<?> c) {
175    testConstructors(c, Visibility.PUBLIC);
176  }
177
178  /**
179   * Runs {@link #testMethod} on every static method of class {@code c} that has at least {@code
180   * minimalVisibility}, including those "inherited" from superclasses of the same package.
181   */
182  public void testStaticMethods(Class<?> c, Visibility minimalVisibility) {
183    for (Method method : minimalVisibility.getStaticMethods(c)) {
184      if (!isIgnored(method)) {
185        testMethod(null, method);
186      }
187    }
188  }
189
190  /**
191   * Runs {@link #testMethod} on every public static method of class {@code c}, including those
192   * "inherited" from superclasses of the same package.
193   */
194  public void testAllPublicStaticMethods(Class<?> c) {
195    testStaticMethods(c, Visibility.PUBLIC);
196  }
197
198  /**
199   * Runs {@link #testMethod} on every instance method of the class of {@code instance} with at
200   * least {@code minimalVisibility}, including those inherited from superclasses of the same
201   * package.
202   */
203  public void testInstanceMethods(Object instance, Visibility minimalVisibility) {
204    for (Method method : getInstanceMethodsToTest(instance.getClass(), minimalVisibility)) {
205      testMethod(instance, method);
206    }
207  }
208
209  ImmutableList<Method> getInstanceMethodsToTest(Class<?> c, Visibility minimalVisibility) {
210    ImmutableList.Builder<Method> builder = ImmutableList.builder();
211    for (Method method : minimalVisibility.getInstanceMethods(c)) {
212      if (!isIgnored(method)) {
213        builder.add(method);
214      }
215    }
216    return builder.build();
217  }
218
219  /**
220   * Runs {@link #testMethod} on every public instance method of the class of {@code instance},
221   * including those inherited from superclasses of the same package.
222   */
223  public void testAllPublicInstanceMethods(Object instance) {
224    testInstanceMethods(instance, Visibility.PUBLIC);
225  }
226
227  /**
228   * Verifies that {@code method} produces a {@link NullPointerException} or {@link
229   * UnsupportedOperationException} whenever <i>any</i> of its non-nullable parameters are null.
230   *
231   * @param instance the instance to invoke {@code method} on, or null if {@code method} is static
232   */
233  public void testMethod(@Nullable Object instance, Method method) {
234    Class<?>[] types = method.getParameterTypes();
235    for (int nullIndex = 0; nullIndex < types.length; nullIndex++) {
236      testMethodParameter(instance, method, nullIndex);
237    }
238  }
239
240  /**
241   * Verifies that {@code ctor} produces a {@link NullPointerException} or {@link
242   * UnsupportedOperationException} whenever <i>any</i> of its non-nullable parameters are null.
243   */
244  public void testConstructor(Constructor<?> ctor) {
245    Class<?> declaringClass = ctor.getDeclaringClass();
246    checkArgument(
247        Modifier.isStatic(declaringClass.getModifiers())
248            || declaringClass.getEnclosingClass() == null,
249        "Cannot test constructor of non-static inner class: %s",
250        declaringClass.getName());
251    Class<?>[] types = ctor.getParameterTypes();
252    for (int nullIndex = 0; nullIndex < types.length; nullIndex++) {
253      testConstructorParameter(ctor, nullIndex);
254    }
255  }
256
257  /**
258   * Verifies that {@code method} produces a {@link NullPointerException} or {@link
259   * UnsupportedOperationException} when the parameter in position {@code paramIndex} is null. If
260   * this parameter is marked nullable, this method does nothing.
261   *
262   * @param instance the instance to invoke {@code method} on, or null if {@code method} is static
263   */
264  public void testMethodParameter(@Nullable Object instance, Method method, int paramIndex) {
265    method.setAccessible(true);
266    testParameter(instance, invokable(instance, method), paramIndex, method.getDeclaringClass());
267  }
268
269  /**
270   * Verifies that {@code ctor} produces a {@link NullPointerException} or {@link
271   * UnsupportedOperationException} when the parameter in position {@code paramIndex} is null. If
272   * this parameter is marked nullable, this method does nothing.
273   */
274  public void testConstructorParameter(Constructor<?> ctor, int paramIndex) {
275    ctor.setAccessible(true);
276    testParameter(null, Invokable.from(ctor), paramIndex, ctor.getDeclaringClass());
277  }
278
279  /** Visibility of any method or constructor. */
280  public enum Visibility {
281    PACKAGE {
282      @Override
283      boolean isVisible(int modifiers) {
284        return !Modifier.isPrivate(modifiers);
285      }
286    },
287
288    PROTECTED {
289      @Override
290      boolean isVisible(int modifiers) {
291        return Modifier.isPublic(modifiers) || Modifier.isProtected(modifiers);
292      }
293    },
294
295    PUBLIC {
296      @Override
297      boolean isVisible(int modifiers) {
298        return Modifier.isPublic(modifiers);
299      }
300    };
301
302    abstract boolean isVisible(int modifiers);
303
304    /** Returns {@code true} if {@code member} is visible under {@code this} visibility. */
305    final boolean isVisible(Member member) {
306      return isVisible(member.getModifiers());
307    }
308
309    final ImmutableList<Method> getStaticMethods(Class<?> cls) {
310      ImmutableList.Builder<Method> builder = ImmutableList.builder();
311      for (Method method : getVisibleMethods(cls)) {
312        if (Invokable.from(method).isStatic()) {
313          builder.add(method);
314        }
315      }
316      return builder.build();
317    }
318
319    // TODO(cpovirk): Disable Animal Sniffer entirely for guava-testlib?
320    @IgnoreJRERequirement // see comment on other @IgnoreJRERequirement annotation above
321    final Iterable<Method> getInstanceMethods(Class<?> cls) {
322      Map<Signature, Method> map = new HashMap<>();
323      for (Method method : getVisibleMethods(cls)) {
324        if (!Invokable.from(method).isStatic()) {
325          map.putIfAbsent(new Signature(method), method);
326        }
327      }
328      return map.values();
329    }
330
331    private ImmutableList<Method> getVisibleMethods(Class<?> cls) {
332      // Don't use cls.getPackage() because it does nasty things like reading
333      // a file.
334      String visiblePackage = Reflection.getPackageName(cls);
335      ImmutableList.Builder<Method> builder = ImmutableList.builder();
336      for (Class<?> type : TypeToken.of(cls).getTypes().rawTypes()) {
337        if (!Reflection.getPackageName(type).equals(visiblePackage)) {
338          break;
339        }
340        for (Method method : type.getDeclaredMethods()) {
341          if (!method.isSynthetic() && isVisible(method)) {
342            builder.add(method);
343          }
344        }
345      }
346      return builder.build();
347    }
348  }
349
350  private static final class Signature {
351    private final String name;
352    private final ImmutableList<Class<?>> parameterTypes;
353
354    Signature(Method method) {
355      this(method.getName(), ImmutableList.copyOf(method.getParameterTypes()));
356    }
357
358    Signature(String name, ImmutableList<Class<?>> parameterTypes) {
359      this.name = name;
360      this.parameterTypes = parameterTypes;
361    }
362
363    @Override
364    public boolean equals(@Nullable Object obj) {
365      if (obj instanceof Signature) {
366        Signature that = (Signature) obj;
367        return name.equals(that.name) && parameterTypes.equals(that.parameterTypes);
368      }
369      return false;
370    }
371
372    @Override
373    public int hashCode() {
374      return Objects.hash(name, parameterTypes);
375    }
376  }
377
378  /**
379   * Verifies that {@code invokable} produces a {@link NullPointerException} or {@link
380   * UnsupportedOperationException} when the parameter in position {@code paramIndex} is null. If
381   * this parameter is marked nullable, this method does nothing.
382   *
383   * @param instance the instance to invoke {@code invokable} on, or null if {@code invokable} is
384   *     static
385   */
386  private void testParameter(
387      @Nullable Object instance, Invokable<?, ?> invokable, int paramIndex, Class<?> testedClass) {
388    if (isPrimitiveOrNullable(invokable.getParameters().get(paramIndex))) {
389      return; // there's nothing to test
390    }
391    @Nullable Object[] params = buildParamList(invokable, paramIndex);
392    try {
393      @SuppressWarnings("unchecked") // We'll get a runtime exception if the type is wrong.
394      Invokable<Object, ?> unsafe = (Invokable<Object, ?>) invokable;
395      unsafe.invoke(instance, params);
396      Assert.fail(
397          "No exception thrown for parameter at index "
398              + paramIndex
399              + " from "
400              + invokable
401              + Arrays.toString(params)
402              + " for "
403              + testedClass);
404    } catch (InvocationTargetException e) {
405      Throwable cause = e.getCause();
406      if (policy.isExpectedType(cause)) {
407        return;
408      }
409      throw new AssertionError(
410          String.format(
411              "wrong exception thrown from %s when passing null to %s parameter at index %s.%n"
412                  + "Full parameters: %s%n"
413                  + "Actual exception message: %s",
414              invokable,
415              invokable.getParameters().get(paramIndex).getType(),
416              paramIndex,
417              Arrays.toString(params),
418              cause),
419          cause);
420    } catch (IllegalAccessException e) {
421      throw new RuntimeException(e);
422    }
423  }
424
425  private @Nullable Object[] buildParamList(
426      Invokable<?, ?> invokable, int indexOfParamToSetToNull) {
427    ImmutableList<Parameter> params = invokable.getParameters();
428    @Nullable Object[] args = new Object[params.size()];
429
430    for (int i = 0; i < args.length; i++) {
431      Parameter param = params.get(i);
432      if (i != indexOfParamToSetToNull) {
433        args[i] = getDefaultValue(param.getType());
434        Assert.assertTrue(
435            "Can't find or create a sample instance for type '"
436                + param.getType()
437                + "'; please provide one using NullPointerTester.setDefault()",
438            args[i] != null || isNullable(param));
439      }
440    }
441    return args;
442  }
443
444  private <T> @Nullable T getDefaultValue(TypeToken<T> type) {
445    // We assume that all defaults are generics-safe, even if they aren't,
446    // we take the risk.
447    @SuppressWarnings("unchecked")
448    T defaultValue = (T) defaults.getInstance(type.getRawType());
449    if (defaultValue != null) {
450      return defaultValue;
451    }
452    @SuppressWarnings("unchecked") // All arbitrary instances are generics-safe
453    T arbitrary = (T) ArbitraryInstances.get(type.getRawType());
454    if (arbitrary != null) {
455      return arbitrary;
456    }
457    if (type.getRawType() == Class.class) {
458      // If parameter is Class<? extends Foo>, we return Foo.class
459      @SuppressWarnings("unchecked")
460      T defaultClass = (T) getFirstTypeParameter(type.getType()).getRawType();
461      return defaultClass;
462    }
463    if (type.getRawType() == TypeToken.class) {
464      // If parameter is TypeToken<? extends Foo>, we return TypeToken<Foo>.
465      @SuppressWarnings("unchecked")
466      T defaultType = (T) getFirstTypeParameter(type.getType());
467      return defaultType;
468    }
469    if (type.getRawType() == Converter.class) {
470      TypeToken<?> convertFromType = type.resolveType(Converter.class.getTypeParameters()[0]);
471      TypeToken<?> convertToType = type.resolveType(Converter.class.getTypeParameters()[1]);
472      @SuppressWarnings("unchecked") // returns default for both F and T
473      T defaultConverter = (T) defaultConverter(convertFromType, convertToType);
474      return defaultConverter;
475    }
476    if (type.getRawType().isInterface()) {
477      return newDefaultReturningProxy(type);
478    }
479    return null;
480  }
481
482  private <F, T> Converter<F, T> defaultConverter(
483      TypeToken<F> convertFromType, TypeToken<T> convertToType) {
484    return new Converter<F, T>() {
485      @Override
486      protected T doForward(F a) {
487        return doConvert(convertToType);
488      }
489
490      @Override
491      protected F doBackward(T b) {
492        return doConvert(convertFromType);
493      }
494
495      private /*static*/ <S> S doConvert(TypeToken<S> type) {
496        return checkNotNull(getDefaultValue(type));
497      }
498    };
499  }
500
501  private static TypeToken<?> getFirstTypeParameter(Type type) {
502    if (type instanceof ParameterizedType) {
503      return TypeToken.of(((ParameterizedType) type).getActualTypeArguments()[0]);
504    } else {
505      return TypeToken.of(Object.class);
506    }
507  }
508
509  private <T> T newDefaultReturningProxy(TypeToken<T> type) {
510    return new DummyProxy() {
511      @Override
512      <R> @Nullable R dummyReturnValue(TypeToken<R> returnType) {
513        return getDefaultValue(returnType);
514      }
515    }.newProxy(type);
516  }
517
518  private static Invokable<?, ?> invokable(@Nullable Object instance, Method method) {
519    if (instance == null) {
520      return Invokable.from(method);
521    } else {
522      return TypeToken.of(instance.getClass()).method(method);
523    }
524  }
525
526  static boolean isPrimitiveOrNullable(Parameter param) {
527    return param.getType().getRawType().isPrimitive() || isNullable(param);
528  }
529
530  private static final ImmutableSet<String> NULLABLE_ANNOTATION_SIMPLE_NAMES =
531      ImmutableSet.of("CheckForNull", "Nullable", "NullableDecl", "NullableType");
532
533  static boolean isNullable(Invokable<?, ?> invokable) {
534    return NULLNESS_ANNOTATION_READER.isNullable(invokable);
535  }
536
537  static boolean isNullable(Parameter param) {
538    return NULLNESS_ANNOTATION_READER.isNullable(param);
539  }
540
541  private static boolean containsNullable(Annotation[] annotations) {
542    for (Annotation annotation : annotations) {
543      if (NULLABLE_ANNOTATION_SIMPLE_NAMES.contains(annotation.annotationType().getSimpleName())) {
544        return true;
545      }
546    }
547    return false;
548  }
549
550  private boolean isIgnored(Member member) {
551    return member.isSynthetic()
552        || ignoredMembers.contains(member)
553        || isEquals(member)
554        /*
555         * We can assume that Kotlin code is performing the null checks that we want, since kotlinc
556         * inserts checks automatically for non-private functions (which are the only kind that we
557         * check).
558         *
559         * We *would* just check such functions redundantly, but kotlinc emits its nullness
560         * annotations in the form of JetBrains annotations, which have only class retention and
561         * thus are invisible at runtime. Thus, we conclude that the parameter types are
562         * *non*-nullable, even when they are declared as `Foo?`.
563         */
564        || hasAutomaticNullChecksFromKotlin(member);
565  }
566
567  private static boolean hasAutomaticNullChecksFromKotlin(Member member) {
568    for (Annotation annotation : member.getDeclaringClass().getAnnotations()) {
569      if (annotation.annotationType().getName().equals("kotlin.Metadata")) {
570        return true;
571      }
572    }
573    return false;
574  }
575
576  /**
577   * Returns true if the given member is a method that overrides {@link Object#equals(Object)}.
578   *
579   * <p>The documentation for {@link Object#equals} says it should accept null, so don't require an
580   * explicit {@code @Nullable} annotation (see <a
581   * href="https://github.com/google/guava/issues/1819">#1819</a>).
582   *
583   * <p>It is not necessary to consider visibility, return type, or type parameter declarations. The
584   * declaration of a method with the same name and formal parameters as {@link Object#equals} that
585   * is not public and boolean-returning, or that declares any type parameters, would be rejected at
586   * compile-time.
587   */
588  private static boolean isEquals(Member member) {
589    if (!(member instanceof Method)) {
590      return false;
591    }
592    Method method = (Method) member;
593    if (!method.getName().contentEquals("equals")) {
594      return false;
595    }
596    Class<?>[] parameters = method.getParameterTypes();
597    if (parameters.length != 1) {
598      return false;
599    }
600    if (!parameters[0].equals(Object.class)) {
601      return false;
602    }
603    return true;
604  }
605
606  /** Strategy for exception type matching used by {@link NullPointerTester}. */
607  private enum ExceptionTypePolicy {
608
609    /**
610     * Exceptions should be {@link NullPointerException} or {@link UnsupportedOperationException}.
611     */
612    NPE_OR_UOE() {
613      @Override
614      boolean isExpectedType(Throwable cause) {
615        return cause instanceof NullPointerException
616            || cause instanceof UnsupportedOperationException;
617      }
618    },
619
620    /**
621     * Exceptions should be {@link NullPointerException}, {@link IllegalArgumentException}, or
622     * {@link UnsupportedOperationException}.
623     */
624    NPE_IAE_OR_UOE() {
625      @Override
626      boolean isExpectedType(Throwable cause) {
627        return cause instanceof NullPointerException
628            || cause instanceof IllegalArgumentException
629            || cause instanceof UnsupportedOperationException;
630      }
631    };
632
633    abstract boolean isExpectedType(Throwable cause);
634  }
635
636  private static boolean annotatedTypeExists() {
637    try {
638      Class.forName("java.lang.reflect.AnnotatedType");
639    } catch (ClassNotFoundException e) {
640      return false;
641    }
642    return true;
643  }
644
645  private static final NullnessAnnotationReader NULLNESS_ANNOTATION_READER =
646      annotatedTypeExists()
647          ? NullnessAnnotationReader.FROM_DECLARATION_AND_TYPE_USE_ANNOTATIONS
648          : NullnessAnnotationReader.FROM_DECLARATION_ANNOTATIONS_ONLY;
649
650  /**
651   * Looks for declaration nullness annotations and, if supported, type-use nullness annotations.
652   *
653   * <p>Under Android VMs, the methods for retrieving type-use annotations don't exist. This means
654   * that {@link NullPointerTester} may misbehave under Android when used on classes that rely on
655   * type-use annotations.
656   *
657   * <p>Under j2objc, the necessary APIs exist, but some (perhaps all) return stub values, like
658   * empty arrays. Presumably {@link NullPointerTester} could likewise misbehave under j2objc, but I
659   * don't know that anyone uses it there, anyway.
660   */
661  private enum NullnessAnnotationReader {
662    FROM_DECLARATION_AND_TYPE_USE_ANNOTATIONS {
663      @Override
664      boolean isNullable(Invokable<?, ?> invokable) {
665        return FROM_DECLARATION_ANNOTATIONS_ONLY.isNullable(invokable)
666            || containsNullable(invokable.getAnnotatedReturnType().getAnnotations());
667        // TODO(cpovirk): Should we also check isNullableTypeVariable?
668      }
669
670      @Override
671      boolean isNullable(Parameter param) {
672        return FROM_DECLARATION_ANNOTATIONS_ONLY.isNullable(param)
673            || containsNullable(param.getAnnotatedType().getAnnotations())
674            || isNullableTypeVariable(param.getAnnotatedType().getType());
675      }
676
677      boolean isNullableTypeVariable(Type type) {
678        if (!(type instanceof TypeVariable)) {
679          return false;
680        }
681        TypeVariable<?> typeVar = (TypeVariable<?>) type;
682        for (AnnotatedType bound : typeVar.getAnnotatedBounds()) {
683          // Until Java 15, the isNullableTypeVariable case here won't help:
684          // https://bugs.openjdk.org/browse/JDK-8202469
685          if (containsNullable(bound.getAnnotations()) || isNullableTypeVariable(bound.getType())) {
686            return true;
687          }
688        }
689        return false;
690      }
691    },
692    FROM_DECLARATION_ANNOTATIONS_ONLY {
693      @Override
694      boolean isNullable(Invokable<?, ?> invokable) {
695        return containsNullable(invokable.getAnnotations());
696      }
697
698      @Override
699      boolean isNullable(Parameter param) {
700        return containsNullable(param.getAnnotations());
701      }
702    };
703
704    abstract boolean isNullable(Invokable<?, ?> invokable);
705
706    abstract boolean isNullable(Parameter param);
707  }
708}