1010import com .google .errorprone .util .ASTHelpers ;
1111import com .sun .source .tree .AnnotatedTypeTree ;
1212import com .sun .source .tree .AnnotationTree ;
13+ import com .sun .source .tree .ArrayTypeTree ;
1314import com .sun .source .tree .AssignmentTree ;
1415import com .sun .source .tree .ConditionalExpressionTree ;
1516import com .sun .source .tree .ExpressionTree ;
2021import com .sun .source .tree .ParameterizedTypeTree ;
2122import com .sun .source .tree .Tree ;
2223import com .sun .source .tree .VariableTree ;
24+ import com .sun .source .util .SimpleTreeVisitor ;
2325import com .sun .tools .javac .code .Attribute ;
2426import com .sun .tools .javac .code .BoundKind ;
2527import com .sun .tools .javac .code .Symbol ;
@@ -354,57 +356,10 @@ public static void checkTypeParameterNullnessForFunctionReturnType(
354356 * @param state the visitor state
355357 */
356358 private static boolean compareNullabilityAnnotations (
357- Type .ClassType lhsType , Type .ClassType rhsType , VisitorState state ) {
358- Types types = state .getTypes ();
359- // The base type of rhsType may be a subtype of lhsType's base type. In such cases, we must
360- // compare lhsType against the supertype of rhsType with a matching base type.
361- rhsType = (Type .ClassType ) types .asSuper (rhsType , lhsType .tsym );
362- // This is impossible, considering the fact that standard Java subtyping succeeds before running
363- // NullAway
364- if (rhsType == null ) {
365- throw new RuntimeException ("Did not find supertype of " + rhsType + " matching " + lhsType );
366- }
367- List <Type > lhsTypeArguments = lhsType .getTypeArguments ();
368- List <Type > rhsTypeArguments = rhsType .getTypeArguments ();
369- // This is impossible, considering the fact that standard Java subtyping succeeds before running
370- // NullAway
371- if (lhsTypeArguments .size () != rhsTypeArguments .size ()) {
372- throw new RuntimeException (
373- "Number of types arguments in " + rhsType + " does not match " + lhsType );
374- }
375- for (int i = 0 ; i < lhsTypeArguments .size (); i ++) {
376- Type lhsTypeArgument = lhsTypeArguments .get (i );
377- Type rhsTypeArgument = rhsTypeArguments .get (i );
378- boolean isLHSNullableAnnotated = false ;
379- List <Attribute .TypeCompound > lhsAnnotations = lhsTypeArgument .getAnnotationMirrors ();
380- // To ensure that we are checking only jspecify nullable annotations
381- for (Attribute .TypeCompound annotation : lhsAnnotations ) {
382- if (annotation .getAnnotationType ().toString ().equals (NULLABLE_NAME )) {
383- isLHSNullableAnnotated = true ;
384- break ;
385- }
386- }
387- boolean isRHSNullableAnnotated = false ;
388- List <Attribute .TypeCompound > rhsAnnotations = rhsTypeArgument .getAnnotationMirrors ();
389- // To ensure that we are checking only jspecify nullable annotations
390- for (Attribute .TypeCompound annotation : rhsAnnotations ) {
391- if (annotation .getAnnotationType ().toString ().equals (NULLABLE_NAME )) {
392- isRHSNullableAnnotated = true ;
393- break ;
394- }
395- }
396- if (isLHSNullableAnnotated != isRHSNullableAnnotated ) {
397- return false ;
398- }
399- // nested generics
400- if (lhsTypeArgument .getTypeArguments ().length () > 0 ) {
401- if (!compareNullabilityAnnotations (
402- (Type .ClassType ) lhsTypeArgument , (Type .ClassType ) rhsTypeArgument , state )) {
403- return false ;
404- }
405- }
406- }
407- return true ;
359+ Type lhsType , Type rhsType , VisitorState state ) {
360+ // it is fair to assume rhyType should be the same as lhsType as the Java compiler has passed
361+ // before NullAway.
362+ return lhsType .accept (new CompareNullabilityVisitor (state ), rhsType );
408363 }
409364
410365 /**
@@ -418,56 +373,7 @@ private static boolean compareNullabilityAnnotations(
418373 */
419374 private static Type .ClassType typeWithPreservedAnnotations (
420375 ParameterizedTypeTree tree , VisitorState state ) {
421- Type .ClassType type = (Type .ClassType ) ASTHelpers .getType (tree );
422- Preconditions .checkNotNull (type );
423- Type nullableType = NULLABLE_TYPE_SUPPLIER .get (state );
424- List <? extends Tree > typeArguments = tree .getTypeArguments ();
425- List <Type > newTypeArgs = new ArrayList <>();
426- for (int i = 0 ; i < typeArguments .size (); i ++) {
427- AnnotatedTypeTree annotatedType = null ;
428- Tree curTypeArg = typeArguments .get (i );
429- // If the type argument has an annotation, it will either be an AnnotatedTypeTree, or a
430- // ParameterizedTypeTree in the case of a nested generic type
431- if (curTypeArg instanceof AnnotatedTypeTree ) {
432- annotatedType = (AnnotatedTypeTree ) curTypeArg ;
433- } else if (curTypeArg instanceof ParameterizedTypeTree
434- && ((ParameterizedTypeTree ) curTypeArg ).getType () instanceof AnnotatedTypeTree ) {
435- annotatedType = (AnnotatedTypeTree ) ((ParameterizedTypeTree ) curTypeArg ).getType ();
436- }
437- List <? extends AnnotationTree > annotations =
438- annotatedType != null ? annotatedType .getAnnotations () : Collections .emptyList ();
439- boolean hasNullableAnnotation = false ;
440- for (AnnotationTree annotation : annotations ) {
441- if (ASTHelpers .isSameType (
442- nullableType , ASTHelpers .getType (annotation .getAnnotationType ()), state )) {
443- hasNullableAnnotation = true ;
444- break ;
445- }
446- }
447- // construct a TypeMetadata object containing a nullability annotation if needed
448- com .sun .tools .javac .util .List <Attribute .TypeCompound > nullableAnnotationCompound =
449- hasNullableAnnotation
450- ? com .sun .tools .javac .util .List .from (
451- Collections .singletonList (
452- new Attribute .TypeCompound (
453- nullableType , com .sun .tools .javac .util .List .nil (), null )))
454- : com .sun .tools .javac .util .List .nil ();
455- TypeMetadata typeMetadata =
456- new TypeMetadata (new TypeMetadata .Annotations (nullableAnnotationCompound ));
457- Type currentTypeArgType = castToNonNull (ASTHelpers .getType (curTypeArg ));
458- if (currentTypeArgType .getTypeArguments ().size () > 0 ) {
459- // nested generic type; recursively preserve its nullability type argument annotations
460- currentTypeArgType =
461- typeWithPreservedAnnotations ((ParameterizedTypeTree ) curTypeArg , state );
462- }
463- Type .ClassType newTypeArgType =
464- (Type .ClassType ) currentTypeArgType .cloneWithMetadata (typeMetadata );
465- newTypeArgs .add (newTypeArgType );
466- }
467- Type .ClassType finalType =
468- new Type .ClassType (
469- type .getEnclosingType (), com .sun .tools .javac .util .List .from (newTypeArgs ), type .tsym );
470- return finalType ;
376+ return (Type .ClassType ) tree .accept (new PreservedAnnotationTreeVisitor (state ), null );
471377 }
472378
473379 /**
@@ -577,6 +483,156 @@ public static void compareGenericTypeParameterNullabilityForCall(
577483 }
578484 }
579485
486+ /**
487+ * Visitor that is called from compareNullabilityAnnotations which recursively compares the
488+ * Nullability annotations for the nested generic type arguments. Compares the Type it is called
489+ * upon, i.e. the LHS type and the Type passed as an argument, i.e. The RHS type.
490+ */
491+ public static class CompareNullabilityVisitor extends Types .DefaultTypeVisitor <Boolean , Type > {
492+ private final VisitorState state ;
493+
494+ CompareNullabilityVisitor (VisitorState state ) {
495+ this .state = state ;
496+ }
497+
498+ @ Override
499+ public Boolean visitClassType (Type .ClassType lhsType , Type rhsType ) {
500+ Types types = state .getTypes ();
501+ // The base type of rhsType may be a subtype of lhsType's base type. In such cases, we must
502+ // compare lhsType against the supertype of rhsType with a matching base type.
503+ rhsType = (Type .ClassType ) types .asSuper (rhsType , lhsType .tsym );
504+ // This is impossible, considering the fact that standard Java subtyping succeeds before
505+ // running NullAway
506+ if (rhsType == null ) {
507+ throw new RuntimeException ("Did not find supertype of " + rhsType + " matching " + lhsType );
508+ }
509+ List <Type > lhsTypeArguments = lhsType .getTypeArguments ();
510+ List <Type > rhsTypeArguments = rhsType .getTypeArguments ();
511+ // This is impossible, considering the fact that standard Java subtyping succeeds before
512+ // running NullAway
513+ if (lhsTypeArguments .size () != rhsTypeArguments .size ()) {
514+ throw new RuntimeException (
515+ "Number of types arguments in " + rhsType + " does not match " + lhsType );
516+ }
517+ for (int i = 0 ; i < lhsTypeArguments .size (); i ++) {
518+ Type lhsTypeArgument = lhsTypeArguments .get (i );
519+ Type rhsTypeArgument = rhsTypeArguments .get (i );
520+ boolean isLHSNullableAnnotated = false ;
521+ List <Attribute .TypeCompound > lhsAnnotations = lhsTypeArgument .getAnnotationMirrors ();
522+ // To ensure that we are checking only jspecify nullable annotations
523+ for (Attribute .TypeCompound annotation : lhsAnnotations ) {
524+ if (annotation .getAnnotationType ().toString ().equals (NULLABLE_NAME )) {
525+ isLHSNullableAnnotated = true ;
526+ break ;
527+ }
528+ }
529+ boolean isRHSNullableAnnotated = false ;
530+ List <Attribute .TypeCompound > rhsAnnotations = rhsTypeArgument .getAnnotationMirrors ();
531+ // To ensure that we are checking only jspecify nullable annotations
532+ for (Attribute .TypeCompound annotation : rhsAnnotations ) {
533+ if (annotation .getAnnotationType ().toString ().equals (NULLABLE_NAME )) {
534+ isRHSNullableAnnotated = true ;
535+ break ;
536+ }
537+ }
538+ if (isLHSNullableAnnotated != isRHSNullableAnnotated ) {
539+ return false ;
540+ }
541+ // nested generics
542+ if (!lhsTypeArgument .accept (this , rhsTypeArgument )) {
543+ return false ;
544+ }
545+ }
546+ return true ;
547+ }
548+
549+ @ Override
550+ public Boolean visitArrayType (Type .ArrayType lhsType , Type rhsType ) {
551+ Type .ArrayType arrRhsType = (Type .ArrayType ) rhsType ;
552+ return lhsType .getComponentType ().accept (this , arrRhsType .getComponentType ());
553+ }
554+
555+ @ Override
556+ public Boolean visitType (Type t , Type type ) {
557+ return true ;
558+ }
559+ }
560+
561+ /**
562+ * Visitor For getting the preserved Annotation Type for the nested generic type arguments within
563+ * the ParameterizedTypeTree originally passed to TypeWithPreservedAnnotations method, since these
564+ * nested arguments may not always be ParameterizedTypeTrees and may be of different types for
565+ * e.g. ArrayTypeTree.
566+ */
567+ public static class PreservedAnnotationTreeVisitor extends SimpleTreeVisitor <Type , Void > {
568+
569+ private final VisitorState state ;
570+
571+ PreservedAnnotationTreeVisitor (VisitorState state ) {
572+ this .state = state ;
573+ }
574+
575+ @ Override
576+ public Type visitArrayType (ArrayTypeTree tree , Void p ) {
577+ Type elemType = tree .getType ().accept (this , null );
578+ return new Type .ArrayType (elemType , castToNonNull (ASTHelpers .getType (tree )).tsym );
579+ }
580+
581+ @ Override
582+ public Type visitParameterizedType (ParameterizedTypeTree tree , Void p ) {
583+ Type .ClassType type = (Type .ClassType ) ASTHelpers .getType (tree );
584+ Preconditions .checkNotNull (type );
585+ Type nullableType = NULLABLE_TYPE_SUPPLIER .get (state );
586+ List <? extends Tree > typeArguments = tree .getTypeArguments ();
587+ List <Type > newTypeArgs = new ArrayList <>();
588+ for (int i = 0 ; i < typeArguments .size (); i ++) {
589+ AnnotatedTypeTree annotatedType = null ;
590+ Tree curTypeArg = typeArguments .get (i );
591+ // If the type argument has an annotation, it will either be an AnnotatedTypeTree, or a
592+ // ParameterizedTypeTree in the case of a nested generic type
593+ if (curTypeArg instanceof AnnotatedTypeTree ) {
594+ annotatedType = (AnnotatedTypeTree ) curTypeArg ;
595+ } else if (curTypeArg instanceof ParameterizedTypeTree
596+ && ((ParameterizedTypeTree ) curTypeArg ).getType () instanceof AnnotatedTypeTree ) {
597+ annotatedType = (AnnotatedTypeTree ) ((ParameterizedTypeTree ) curTypeArg ).getType ();
598+ }
599+ List <? extends AnnotationTree > annotations =
600+ annotatedType != null ? annotatedType .getAnnotations () : Collections .emptyList ();
601+ boolean hasNullableAnnotation = false ;
602+ for (AnnotationTree annotation : annotations ) {
603+ if (ASTHelpers .isSameType (
604+ nullableType , ASTHelpers .getType (annotation .getAnnotationType ()), state )) {
605+ hasNullableAnnotation = true ;
606+ break ;
607+ }
608+ }
609+ // construct a TypeMetadata object containing a nullability annotation if needed
610+ com .sun .tools .javac .util .List <Attribute .TypeCompound > nullableAnnotationCompound =
611+ hasNullableAnnotation
612+ ? com .sun .tools .javac .util .List .from (
613+ Collections .singletonList (
614+ new Attribute .TypeCompound (
615+ nullableType , com .sun .tools .javac .util .List .nil (), null )))
616+ : com .sun .tools .javac .util .List .nil ();
617+ TypeMetadata typeMetadata =
618+ new TypeMetadata (new TypeMetadata .Annotations (nullableAnnotationCompound ));
619+ Type currentTypeArgType = curTypeArg .accept (this , null );
620+ Type newTypeArgType = currentTypeArgType .cloneWithMetadata (typeMetadata );
621+ newTypeArgs .add (newTypeArgType );
622+ }
623+ Type .ClassType finalType =
624+ new Type .ClassType (
625+ type .getEnclosingType (), com .sun .tools .javac .util .List .from (newTypeArgs ), type .tsym );
626+ return finalType ;
627+ }
628+
629+ /** By default, just use the type computed by javac */
630+ @ Override
631+ protected Type defaultAction (Tree node , Void unused ) {
632+ return castToNonNull (ASTHelpers .getType (node ));
633+ }
634+ }
635+
580636 /**
581637 * Checks that type parameter nullability is consistent between an overriding method and the
582638 * corresponding overridden method.
@@ -908,6 +964,7 @@ public String visitCapturedType(Type.CapturedType t, Void s) {
908964
909965 @ Override
910966 public String visitArrayType (Type .ArrayType t , Void unused ) {
967+ // TODO properly print cases like int @Nullable[]
911968 return t .elemtype .accept (this , null ) + "[]" ;
912969 }
913970
0 commit comments