34
34
import com .google .common .reflect .Reflection ;
35
35
import com .google .common .reflect .TypeToken ;
36
36
import java .lang .annotation .Annotation ;
37
+ import java .lang .reflect .AnnotatedType ;
37
38
import java .lang .reflect .Constructor ;
38
39
import java .lang .reflect .InvocationTargetException ;
39
40
import java .lang .reflect .Member ;
40
41
import java .lang .reflect .Method ;
41
42
import java .lang .reflect .Modifier ;
42
43
import java .lang .reflect .ParameterizedType ;
43
44
import java .lang .reflect .Type ;
45
+ import java .lang .reflect .TypeVariable ;
44
46
import java .util .Arrays ;
45
47
import java .util .List ;
46
48
import java .util .concurrent .ConcurrentMap ;
49
+ import java .util .function .Function ;
47
50
import junit .framework .Assert ;
48
51
import junit .framework .AssertionFailedError ;
49
52
import org .checkerframework .checker .nullness .qual .Nullable ;
52
55
* A test utility that verifies that your methods and constructors throw {@link
53
56
* NullPointerException} or {@link UnsupportedOperationException} whenever null is passed to a
54
57
* parameter whose declaration or type isn't annotated with an annotation with the simple name
55
- * {@code Nullable}, {@lcode CheckForNull}, {@link NullableType}, or {@link NullableDecl}.
58
+ * {@code Nullable}, {@code CheckForNull}, {@link NullableType}, or {@link NullableDecl}.
56
59
*
57
60
* <p>The tested methods and constructors are invoked -- each time with one parameter being null and
58
61
* the rest not null -- and the test fails if no expected exception is thrown. {@code
@@ -483,7 +486,22 @@ static boolean isNullable(Invokable<?, ?> invokable) {
483
486
484
487
static boolean isNullable (Parameter param ) {
485
488
return isNullable (param .getAnnotatedType ().getAnnotations ())
486
- || isNullable (param .getAnnotations ());
489
+ || isNullable (param .getAnnotations ())
490
+ || isNullableTypeVariable (param .getAnnotatedType ().getType ());
491
+ }
492
+
493
+ private static boolean isNullableTypeVariable (Type type ) {
494
+ if (!(type instanceof TypeVariable )) {
495
+ return false ;
496
+ }
497
+ TypeVariable <?> var = (TypeVariable <?>) type ;
498
+ AnnotatedType [] bounds = GET_ANNOTATED_BOUNDS .apply (var );
499
+ for (AnnotatedType bound : bounds ) {
500
+ if (isNullable (bound .getAnnotations ()) || isNullableTypeVariable (bound .getType ())) {
501
+ return true ;
502
+ }
503
+ }
504
+ return false ;
487
505
}
488
506
489
507
private static boolean isNullable (Annotation [] annotations ) {
@@ -495,6 +513,27 @@ private static boolean isNullable(Annotation[] annotations) {
495
513
return false ;
496
514
}
497
515
516
+ // This is currently required because of j2objc restrictions.
517
+ private static final Function <TypeVariable <?>, AnnotatedType []> GET_ANNOTATED_BOUNDS =
518
+ initGetAnnotatedBounds ();
519
+
520
+ private static Function <TypeVariable <?>, AnnotatedType []> initGetAnnotatedBounds () {
521
+ AnnotatedType [] noBounds = new AnnotatedType [0 ];
522
+ Method getAnnotatedBounds ;
523
+ try {
524
+ getAnnotatedBounds = TypeVariable .class .getMethod ("getAnnotatedBounds" );
525
+ } catch (ReflectiveOperationException e ) {
526
+ return v -> noBounds ;
527
+ }
528
+ return v -> {
529
+ try {
530
+ return (AnnotatedType []) getAnnotatedBounds .invoke (v );
531
+ } catch (ReflectiveOperationException e ) {
532
+ return noBounds ;
533
+ }
534
+ };
535
+ }
536
+
498
537
private boolean isIgnored (Member member ) {
499
538
return member .isSynthetic () || ignoredMembers .contains (member ) || isEquals (member );
500
539
}
0 commit comments