5555import java .util .stream .Stream ;
5656import javax .lang .model .element .AnnotationMirror ;
5757import javax .lang .model .element .AnnotationValue ;
58+ import javax .lang .model .element .ElementKind ;
5859import javax .lang .model .element .ExecutableElement ;
5960import org .checkerframework .nullaway .javacutil .AnnotationUtils ;
6061import org .jspecify .annotations .Nullable ;
@@ -301,9 +302,27 @@ public static Stream<? extends AnnotationMirror> getAllAnnotationsForParameter(
301302 * Gets the type use annotations on a symbol, ignoring annotations on components of the type (type
302303 * arguments, wildcards, etc.)
303304 */
304- public static Stream <? extends AnnotationMirror > getTypeUseAnnotations (
305- Symbol symbol , Config config ) {
306- Stream <Attribute .TypeCompound > rawTypeAttributes = symbol .getRawTypeAttributes ().stream ();
305+ public static Stream <Attribute .TypeCompound > getTypeUseAnnotations (Symbol symbol , Config config ) {
306+ return getTypeUseAnnotations (symbol , config , /* onlyDirect= */ true );
307+ }
308+
309+ /**
310+ * Gets the type use annotations on a symbol
311+ *
312+ * @param symbol the symbol
313+ * @param config NullAway configuration
314+ * @param onlyDirect if true, only return annotations that are directly on the type, not on
315+ * components of the type (type arguments, wildcards, array contents, etc.)
316+ * @return the type use annotations on the symbol
317+ */
318+ private static Stream <Attribute .TypeCompound > getTypeUseAnnotations (
319+ Symbol symbol , Config config , boolean onlyDirect ) {
320+ // Adapted from Error Prone's MoreAnnotations class:
321+ // https://github.com/google/error-prone/blob/5f71110374e63f3c35b661f538295fa15b5c1db2/check_api/src/main/java/com/google/errorprone/util/MoreAnnotations.java#L84-L91
322+ Symbol typeAnnotationOwner =
323+ symbol .getKind ().equals (ElementKind .PARAMETER ) ? symbol .owner : symbol ;
324+ Stream <Attribute .TypeCompound > rawTypeAttributes =
325+ typeAnnotationOwner .getRawTypeAttributes ().stream ();
307326 if (symbol instanceof Symbol .MethodSymbol ) {
308327 // for methods, we want annotations on the return type
309328 return rawTypeAttributes .filter (
@@ -313,7 +332,45 @@ public static Stream<? extends AnnotationMirror> getTypeUseAnnotations(
313332 } else {
314333 // filter for annotations directly on the type
315334 return rawTypeAttributes .filter (
316- t -> NullabilityUtil .isDirectTypeUseAnnotation (t , symbol , config ));
335+ t ->
336+ targetTypeMatches (symbol , t .position )
337+ && (!onlyDirect || NullabilityUtil .isDirectTypeUseAnnotation (t , symbol , config )));
338+ }
339+ }
340+
341+ // Adapted from Error Prone MoreAnnotations:
342+ // https://github.com/google/error-prone/blob/5f71110374e63f3c35b661f538295fa15b5c1db2/check_api/src/main/java/com/google/errorprone/util/MoreAnnotations.java#L128
343+ private static boolean targetTypeMatches (Symbol sym , TypeAnnotationPosition position ) {
344+ switch (sym .getKind ()) {
345+ case LOCAL_VARIABLE :
346+ return position .type == TargetType .LOCAL_VARIABLE ;
347+ case FIELD :
348+ // treated like a field
349+ case ENUM_CONSTANT :
350+ return position .type == TargetType .FIELD ;
351+ case CONSTRUCTOR :
352+ case METHOD :
353+ return position .type == TargetType .METHOD_RETURN ;
354+ case PARAMETER :
355+ if (position .type .equals (TargetType .METHOD_FORMAL_PARAMETER )) {
356+ int parameterIndex = position .parameter_index ;
357+ if (position .onLambda != null ) {
358+ com .sun .tools .javac .util .List <JCTree .JCVariableDecl > lambdaParams =
359+ position .onLambda .params ;
360+ return parameterIndex < lambdaParams .size ()
361+ && lambdaParams .get (parameterIndex ).sym .equals (sym );
362+ } else {
363+ return ((Symbol .MethodSymbol ) sym .owner ).getParameters ().indexOf (sym ) == parameterIndex ;
364+ }
365+ } else {
366+ return false ;
367+ }
368+ case CLASS :
369+ // There are no type annotations on the top-level type of the class being declared, only
370+ // on other types in the signature (e.g. `class Foo extends Bar<@A Baz> {}`).
371+ return false ;
372+ default :
373+ throw new AssertionError ("unsupported element kind " + sym .getKind () + " symbol " + sym );
317374 }
318375 }
319376
@@ -488,12 +545,28 @@ public static boolean isArrayElementNullable(Symbol arraySymbol, Config config)
488545 }
489546 // For varargs symbols we also consider the elements to be @Nullable if there is a @Nullable
490547 // declaration annotation on the parameter
548+ // NOTE this flag check does not work for the varargs parameter of a method defined in bytecodes
491549 if ((arraySymbol .flags () & Flags .VARARGS ) != 0 ) {
492550 return Nullness .hasNullableDeclarationAnnotation (arraySymbol , config );
493551 }
494552 return false ;
495553 }
496554
555+ /**
556+ * Checks if the given varargs symbol has a {@code @Nullable} annotation for its elements. Works
557+ * for both source and bytecode.
558+ *
559+ * @param varargsSymbol the symbol of the varargs parameter
560+ * @param config NullAway configuration
561+ * @return true if the varargs symbol has a {@code @Nullable} annotation for its elements, false
562+ * otherwise
563+ */
564+ public static boolean nullableVarargsElementsForSourceOrBytecode (
565+ Symbol varargsSymbol , Config config ) {
566+ return isArrayElementNullable (varargsSymbol , config )
567+ || Nullness .hasNullableDeclarationAnnotation (varargsSymbol , config );
568+ }
569+
497570 /**
498571 * Checks if the given array symbol has a {@code @NonNull} annotation for its elements.
499572 *
@@ -503,23 +576,44 @@ public static boolean isArrayElementNullable(Symbol arraySymbol, Config config)
503576 * otherwise
504577 */
505578 public static boolean isArrayElementNonNull (Symbol arraySymbol , Config config ) {
506- for (Attribute .TypeCompound t : arraySymbol .getRawTypeAttributes ()) {
507- for (TypeAnnotationPosition .TypePathEntry entry : t .position .location ) {
508- if (entry .tag == TypeAnnotationPosition .TypePathEntryKind .ARRAY ) {
509- if (Nullness .isNonNullAnnotation (t .type .toString (), config )) {
510- return true ;
511- }
512- }
513- }
579+ if (getTypeUseAnnotations (arraySymbol , config , /* onlyDirect= */ false )
580+ .anyMatch (
581+ t -> {
582+ for (TypeAnnotationPosition .TypePathEntry entry : t .position .location ) {
583+ if (entry .tag == TypeAnnotationPosition .TypePathEntryKind .ARRAY ) {
584+ if (Nullness .isNonNullAnnotation (t .type .toString (), config )) {
585+ return true ;
586+ }
587+ }
588+ }
589+ return false ;
590+ })) {
591+ return true ;
514592 }
515593 // For varargs symbols we also consider the elements to be @NonNull if there is a @NonNull
516594 // declaration annotation on the parameter
595+ // NOTE this flag check does not work for the varargs parameter of a method defined in bytecodes
517596 if ((arraySymbol .flags () & Flags .VARARGS ) != 0 ) {
518597 return Nullness .hasNonNullDeclarationAnnotation (arraySymbol , config );
519598 }
520599 return false ;
521600 }
522601
602+ /**
603+ * Checks if the given varargs symbol has a {@code @NonNull} annotation for its elements. Works
604+ * for both source and bytecode.
605+ *
606+ * @param varargsSymbol the symbol of the varargs parameter
607+ * @param config NullAway configuration
608+ * @return true if the varargs symbol has a {@code @NonNull} annotation for its elements, false
609+ * otherwise
610+ */
611+ public static boolean nonnullVarargsElementsForSourceOrBytecode (
612+ Symbol varargsSymbol , Config config ) {
613+ return isArrayElementNonNull (varargsSymbol , config )
614+ || Nullness .hasNonNullDeclarationAnnotation (varargsSymbol , config );
615+ }
616+
523617 /**
524618 * Does the given symbol have a JetBrains @NotNull declaration annotation? Useful for workarounds
525619 * in light of https://github.com/uber/NullAway/issues/720
0 commit comments