4444import com .sun .tools .javac .code .Attribute ;
4545import com .sun .tools .javac .code .Flags ;
4646import com .sun .tools .javac .code .Symbol ;
47- import com .sun .tools .javac .code .TargetType ;
4847import com .sun .tools .javac .code .Type ;
49- import com .sun .tools .javac .code .TypeAnnotationPosition ;
50- import com .sun .tools .javac .code .TypeAnnotationPosition .TypePathEntry ;
5148import com .sun .tools .javac .code .TypeTag ;
5249import com .sun .tools .javac .code .Types ;
5350import com .sun .tools .javac .tree .JCTree ;
6259import java .util .stream .Stream ;
6360import javax .lang .model .element .AnnotationMirror ;
6461import javax .lang .model .element .AnnotationValue ;
65- import javax .lang .model .element .ElementKind ;
6662import javax .lang .model .element .ExecutableElement ;
6763import javax .lang .model .type .TypeKind ;
6864import org .checkerframework .nullaway .javacutil .AnnotationUtils ;
@@ -195,35 +191,6 @@ public static Stream<? extends AnnotationMirror> getAllAnnotations(Symbol symbol
195191 return Stream .concat (symbol .getAnnotationMirrors ().stream (), typeUseAnnotations );
196192 }
197193
198- /**
199- * Check if any direct annotation a symbol matches a given predicate. Includes code for backward
200- * compatibility with older JDKs where javac did not place type-use annotations on Symbols from
201- * bytecodes.
202- *
203- * @param symbol the symbol
204- * @param predicate the predicate to match annotation names against
205- * @return true if any annotation on the symbol matches the predicate, false otherwise
206- */
207- public static boolean hasAnyAnnotationMatchingBackCompat (
208- Symbol symbol , Predicate <String > predicate ) {
209- if (hasAnyAnnotationMatching (symbol , predicate )) {
210- return true ;
211- }
212- // to handle bytecodes, also check direct type-use annotations stored in attributes
213- Symbol typeAnnotationOwner =
214- symbol .getKind ().equals (ElementKind .PARAMETER ) ? symbol .owner : symbol ;
215- for (Attribute .TypeCompound typeCompound : typeAnnotationOwner .getRawTypeAttributes ()) {
216- if (!targetTypeMatches (symbol , typeCompound .position )
217- || !isDirectTypeUseAnnotation (typeCompound , symbol )) {
218- continue ;
219- }
220- if (predicate .test (typeCompound .getAnnotationType ().toString ())) {
221- return true ;
222- }
223- }
224- return false ;
225- }
226-
227194 /**
228195 * Check if any direct annotation a symbol matches a given predicate.
229196 *
@@ -238,14 +205,24 @@ public static boolean hasAnyAnnotationMatching(Symbol symbol, Predicate<String>
238205 return true ;
239206 }
240207 }
241- // check for type use annotations. For MethodSymbols, look on the return type
242- Type annotatedType =
243- symbol instanceof Symbol .MethodSymbol methodSymbol
244- ? methodSymbol .getReturnType ()
245- : symbol .type ;
246- for (AnnotationMirror annotationMirror : annotatedType .getAnnotationMirrors ()) {
247- if (predicate .test (annotationMirror .getAnnotationType ().toString ())) {
248- return true ;
208+ // Check for type-use annotations. For methods, look on the return type and its enclosing types,
209+ // since NullAway treats an annotation before the outer class of a nested return type as
210+ // applying to the full return type.
211+ if (symbol instanceof Symbol .MethodSymbol methodSymbol ) {
212+ for (Type currentType = methodSymbol .getReturnType ();
213+ currentType != null && !currentType .hasTag (TypeTag .NONE );
214+ currentType = currentType .getEnclosingType ()) {
215+ for (AnnotationMirror annotationMirror : currentType .getAnnotationMirrors ()) {
216+ if (predicate .test (annotationMirror .getAnnotationType ().toString ())) {
217+ return true ;
218+ }
219+ }
220+ }
221+ } else {
222+ for (AnnotationMirror annotationMirror : symbol .type .getAnnotationMirrors ()) {
223+ if (predicate .test (annotationMirror .getAnnotationType ().toString ())) {
224+ return true ;
225+ }
249226 }
250227 }
251228 return false ;
@@ -354,18 +331,11 @@ public static boolean hasAnyAnnotationMatching(Symbol symbol, Predicate<String>
354331 public static Stream <? extends AnnotationMirror > getAllAnnotationsForParameter (
355332 Symbol .MethodSymbol symbol , int paramInd ) {
356333 Symbol .VarSymbol varSymbol = symbol .getParameters ().get (paramInd );
357- // On modern javac versions, type-use annotations are attached directly to the parameter's
358- // type. Keep reading raw attributes as well for backward compatibility with older javac
359- // behavior.
360- Stream <? extends AnnotationMirror > typeUseAnnotations =
334+ Type parameterType = symbol .type .getParameterTypes ().get (paramInd );
335+ Stream <Attribute .TypeCompound > typeUseAnnotations =
361336 Stream .concat (
362- varSymbol .type .getAnnotationMirrors ().stream (),
363- symbol .getRawTypeAttributes ().stream ()
364- .filter (
365- t ->
366- t .position .type .equals (TargetType .METHOD_FORMAL_PARAMETER )
367- && t .position .parameter_index == paramInd
368- && NullabilityUtil .isDirectTypeUseAnnotation (t , symbol )))
337+ getTypeUseAnnotationsIncludingEnclosingTypes (varSymbol .type ),
338+ getTypeUseAnnotationsIncludingEnclosingTypes (parameterType ))
369339 .distinct ();
370340 return Stream .concat (varSymbol .getAnnotationMirrors ().stream (), typeUseAnnotations );
371341 }
@@ -375,137 +345,30 @@ public static Stream<? extends AnnotationMirror> getAllAnnotationsForParameter(
375345 * arguments, wildcards, etc.)
376346 */
377347 public static Stream <Attribute .TypeCompound > getTypeUseAnnotations (Symbol symbol ) {
378- return getTypeUseAnnotations (symbol , /* onlyDirect= */ true );
379- }
380-
381- /**
382- * Gets the type use annotations on a symbol
383- *
384- * @param symbol the symbol
385- * @param onlyDirect if true, only return annotations that are directly on the type, not on
386- * components of the type (type arguments, wildcards, array contents, etc.)
387- * @return the type use annotations on the symbol
388- */
389- private static Stream <Attribute .TypeCompound > getTypeUseAnnotations (
390- Symbol symbol , boolean onlyDirect ) {
391- // Adapted from Error Prone's MoreAnnotations class:
392- // https://github.com/google/error-prone/blob/5f71110374e63f3c35b661f538295fa15b5c1db2/check_api/src/main/java/com/google/errorprone/util/MoreAnnotations.java#L84-L91
393- Symbol typeAnnotationOwner =
394- symbol .getKind ().equals (ElementKind .PARAMETER ) ? symbol .owner : symbol ;
395- Stream <Attribute .TypeCompound > rawTypeAttributes =
396- typeAnnotationOwner .getRawTypeAttributes ().stream ();
348+ Type annotatedType =
349+ symbol instanceof Symbol .MethodSymbol methodSymbol
350+ ? methodSymbol .getReturnType ()
351+ : symbol .type ;
397352 if (symbol instanceof Symbol .MethodSymbol ) {
398- // for methods, we want annotations on the return type
399- return rawTypeAttributes .filter (
400- (t ) ->
401- t .position .type .equals (TargetType .METHOD_RETURN )
402- && (!onlyDirect || isDirectTypeUseAnnotation (t , symbol )));
403- } else {
404- // filter for annotations directly on the type
405- return rawTypeAttributes .filter (
406- t ->
407- targetTypeMatches (symbol , t .position )
408- && (!onlyDirect || isDirectTypeUseAnnotation (t , symbol )));
409- }
410- }
411-
412- // Adapted from Error Prone MoreAnnotations:
413- // https://github.com/google/error-prone/blob/5f71110374e63f3c35b661f538295fa15b5c1db2/check_api/src/main/java/com/google/errorprone/util/MoreAnnotations.java#L128
414- private static boolean targetTypeMatches (Symbol sym , TypeAnnotationPosition position ) {
415- switch (sym .getKind ()) {
416- case LOCAL_VARIABLE -> {
417- return position .type == TargetType .LOCAL_VARIABLE ;
418- }
419- case FIELD , ENUM_CONSTANT -> {
420- // treated like a field
421- return position .type == TargetType .FIELD ;
422- }
423- case CONSTRUCTOR , METHOD -> {
424- return position .type == TargetType .METHOD_RETURN ;
425- }
426- case PARAMETER -> {
427- if (position .type .equals (TargetType .METHOD_FORMAL_PARAMETER )) {
428- int parameterIndex = position .parameter_index ;
429- if (position .onLambda != null ) {
430- com .sun .tools .javac .util .List <JCTree .JCVariableDecl > lambdaParams =
431- position .onLambda .params ;
432- return parameterIndex >= 0
433- && parameterIndex < lambdaParams .size ()
434- && lambdaParams .get (parameterIndex ).sym .equals (sym );
435- } else {
436- return ((Symbol .MethodSymbol ) sym .owner ).getParameters ().indexOf (sym ) == parameterIndex ;
437- }
438- } else {
439- return false ;
440- }
441- }
442- case CLASS , ENUM , RECORD -> {
443- // treated like a class
444- // There are no type annotations on the top-level type of the class/enum/record being
445- // declared,
446- // only on other types in the signature (e.g. `class Foo extends Bar<@A Baz> {}`).
447- return false ;
448- }
449- default -> {
450- throw new AssertionError ("unsupported element kind " + sym .getKind () + " symbol " + sym );
451- }
353+ return getTypeUseAnnotationsIncludingEnclosingTypes (annotatedType );
452354 }
355+ return annotatedType .getAnnotationMirrors ().stream ();
453356 }
454357
455358 /**
456- * Check whether a type-use annotation should be treated as applying directly to the top-level
457- * type
359+ * Gets the type-use annotations directly on {@code type} or any of its enclosing types.
458360 *
459- * <p>For example {@code @Nullable List<T> lst} is a direct type use annotation of {@code lst},
460- * but {@code List<@Nullable T> lst} is not.
461- *
462- * @param t the annotation and its position in the type
463- * @param symbol the symbol for the annotated element
464- * @return {@code true} if the annotation should be treated as applying directly to the top-level
465- * type, false otherwise
361+ * <p>javac models an annotation written before the outer class in a nested type on that enclosing
362+ * type. For method return and parameter nullability, NullAway treats such an annotation as
363+ * applying to the full nested type.
466364 */
467- private static boolean isDirectTypeUseAnnotation (Attribute .TypeCompound t , Symbol symbol ) {
468- // location is a list of TypePathEntry objects, indicating whether the annotation is
469- // on an array, inner type, wildcard, or type argument. If it's empty, then the
470- // annotation is directly on the type.
471- // Annotations on array dimensions, wildcards, or type arguments do not apply to the top-level
472- // type. For nested classes, an annotation applies directly only when placed on the innermost
473- // type.
474- int innerTypeCount = 0 ;
475- for (TypePathEntry entry : t .position .location ) {
476- switch (entry .tag ) {
477- case INNER_TYPE -> {
478- innerTypeCount ++;
479- }
480- case ARRAY -> {
481- return false ;
482- }
483- default -> {
484- // Wildcard or type argument!
485- return false ;
486- }
487- }
488- }
489- // For non-nested classes annotations apply to the innermost type.
490- if (!isTypeOfNestedClass (symbol .type )) {
491- return true ;
492- }
493- // For nested classes the annotation is only valid if it is on the innermost type.
494- return innerTypeCount == getNestingDepth (symbol .type ) - 1 ;
495- }
496-
497- private static int getNestingDepth (Type type ) {
498- int depth = 0 ;
499- for (Type curr = type ;
500- curr != null && !curr .hasTag (TypeTag .NONE );
501- curr = curr .getEnclosingType ()) {
502- depth ++;
503- }
504- return depth ;
505- }
506-
507- private static boolean isTypeOfNestedClass (Type type ) {
508- return type .tsym != null && type .tsym .owner instanceof Symbol .ClassSymbol ;
365+ private static Stream <Attribute .TypeCompound > getTypeUseAnnotationsIncludingEnclosingTypes (
366+ Type type ) {
367+ Stream <Attribute .TypeCompound > annotations = type .getAnnotationMirrors ().stream ();
368+ Type enclosingType = type .getEnclosingType ();
369+ return enclosingType == null || enclosingType .hasTag (TypeTag .NONE )
370+ ? annotations
371+ : Stream .concat (annotations , getTypeUseAnnotationsIncludingEnclosingTypes (enclosingType ));
509372 }
510373
511374 /**
@@ -661,19 +524,17 @@ private static boolean checkArrayElementAnnotations(
661524 Config config ,
662525 BiPredicate <String , Config > typeUseCheck ,
663526 BiPredicate <Symbol , Config > declarationCheck ) {
664- if (getTypeUseAnnotations (arraySymbol , /* onlyDirect= */ false )
665- .anyMatch (
666- t -> {
667- // the location list should be of length 1 and the entry tag should be ARRAY
668- com .sun .tools .javac .util .List <TypePathEntry > location = t .position .location ;
669- TypePathEntry head = location .head ;
670- boolean singleElementList = head != null && location .tail .isEmpty ();
671- if (singleElementList && head .tag == TypeAnnotationPosition .TypePathEntryKind .ARRAY ) {
672- return typeUseCheck .test (t .type .toString (), config );
673- }
674- return false ;
675- })) {
676- return true ;
527+ Type annotatedType =
528+ arraySymbol instanceof Symbol .MethodSymbol methodSymbol
529+ ? methodSymbol .getReturnType ()
530+ : arraySymbol .type ;
531+ if (annotatedType instanceof Type .ArrayType arrayType ) {
532+ for (AnnotationMirror annotationMirror :
533+ arrayType .getComponentType ().getAnnotationMirrors ()) {
534+ if (typeUseCheck .test (annotationMirror .getAnnotationType ().toString (), config )) {
535+ return true ;
536+ }
537+ }
677538 }
678539 // For varargs symbols we also check for declaration annotations on the parameter
679540 // NOTE this flag check does not work for the varargs parameter of a method defined in bytecodes
0 commit comments