1 /*
<lambda>null2  * Copyright (C) 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.tools.metalava.model.testsuite.typeitem
18 
19 import com.android.tools.metalava.model.ClassTypeItem
20 import com.android.tools.metalava.model.Codebase
21 import com.android.tools.metalava.model.TypeModifiers
22 import com.android.tools.metalava.model.TypeNullability.NONNULL
23 import com.android.tools.metalava.model.TypeNullability.PLATFORM
24 import com.android.tools.metalava.model.isNullnessAnnotation
25 import com.android.tools.metalava.model.noOpAnnotationManager
26 import com.android.tools.metalava.model.provider.InputFormat
27 import com.android.tools.metalava.model.testsuite.BaseModelTest
28 import com.android.tools.metalava.model.testsuite.assertHasNonNullNullability
29 import com.android.tools.metalava.model.testsuite.assertHasNullableNullability
30 import com.android.tools.metalava.model.testsuite.assertHasPlatformNullability
31 import com.android.tools.metalava.model.testsuite.assertHasUndefinedNullability
32 import com.android.tools.metalava.model.testsuite.runNullabilityTest
33 import com.android.tools.metalava.testing.KnownSourceFiles
34 import com.android.tools.metalava.testing.java
35 import com.android.tools.metalava.testing.kotlin
36 import com.google.common.truth.Truth.assertThat
37 import org.junit.Test
38 
39 class CommonTypeModifiersTest : BaseModelTest() {
40 
41     @Test
42     fun `Test annotation on basic types`() {
43         runCodebaseTest(
44             java(
45                 """
46                     package test.pkg;
47                     public class Foo {
48                         public @A int foo1() {}
49                         public @A String foo2() {}
50                         public <T> @A T foo3() {}
51                     }
52                     @java.lang.annotation.Target(java.lang.annotation.ElementType.TYPE_USE)
53                     public @interface A {}
54                 """
55             ),
56             kotlin(
57                 """
58                     package test.pkg
59                     class Foo {
60                         fun foo1(): @A Int {}
61                         fun foo2(): @A String {}
62                         fun <T> foo3(): @A T {}
63                     }
64                     @Target(AnnotationTarget.TYPE)
65                     annotation class A
66                 """
67             ),
68             signature(
69                 """
70                     // Signature format: 2.0
71                     package test.pkg {
72                       public class Foo {
73                         method public @test.pkg.A int foo1();
74                         method public @test.pkg.A String foo2();
75                         method public <T> @test.pkg.A T foo3();
76                       }
77                     }
78                 """
79             ),
80             testFixture =
81                 TestFixture(
82                     // Use the noOpAnnotationManager to avoid annotation name normalizing as the
83                     // annotation names are important for this test.
84                     annotationManager = noOpAnnotationManager,
85                 ),
86         ) {
87             val methods = codebase.assertClass("test.pkg.Foo").methods()
88             assertThat(methods).hasSize(3)
89 
90             // @test.pkg.A int
91             val primitiveMethod = methods[0]
92             val primitive = primitiveMethod.returnType()
93             primitive.assertPrimitiveTypeItem {
94                 assertThat(annotationNames()).containsExactly("test.pkg.A")
95             }
96             assertThat(primitiveMethod.annotationNames()).isEmpty()
97 
98             // @test.pkg.A String
99             val stringMethod = methods[1]
100             val string = stringMethod.returnType()
101             string.assertClassTypeItem {
102                 assertThat(annotationNames()).containsExactly("test.pkg.A")
103             }
104             val stringMethodAnnotations = stringMethod.annotationNames()
105             // The Kotlin version puts a nullability annotation on the method
106             if (stringMethodAnnotations.isNotEmpty()) {
107                 assertThat(stringMethodAnnotations)
108                     .containsExactly("org.jetbrains.annotations.NotNull")
109             }
110 
111             // @test.pkg.A T
112             val variableMethod = methods[2]
113             val variable = variableMethod.returnType()
114             val typeParameter = variableMethod.typeParameterList.single()
115             variable.assertReferencesTypeParameter(typeParameter) {
116                 assertThat(annotationNames()).containsExactly("test.pkg.A")
117             }
118             assertThat(variableMethod.annotationNames()).isEmpty()
119         }
120     }
121 
122     @Test
123     fun `Test type-use annotations with multiple allowed targets`() {
124         runCodebaseTest(
125             java(
126                 """
127                     package test.pkg;
128                     public class Foo {
129                         public @A int foo1() {}
130                         public @A String foo2() {}
131                         public @A <T> T foo3() {}
132                     }
133                     @java.lang.annotation.Target({ java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.TYPE_USE })
134                     public @interface A {}
135                 """
136             ),
137             kotlin(
138                 """
139                     package test.pkg
140                     class Foo {
141                         @A fun foo(): @A Int {}
142                         @A fun foo(): @A String {}
143                         @A fun <T> foo(): @A T {}
144                     }
145                     @Target(AnnotationTarget.FUNCTION, AnnotationTarget.TYPE)
146                     annotation class A
147                 """
148             ),
149             signature(
150                 """
151                     // Signature format: 2.0
152                     package test.pkg {
153                       public class Foo {
154                         method @test.pkg.A public @test.pkg.A int foo1();
155                         method @test.pkg.A public @test.pkg.A String foo2();
156                         method @test.pkg.A public <T> @test.pkg.A T foo3();
157                       }
158                     }
159                 """
160                     .trimIndent()
161             )
162         ) {
163             val methods = codebase.assertClass("test.pkg.Foo").methods()
164             assertThat(methods).hasSize(3)
165 
166             // @test.pkg.A int
167             val primitiveMethod = methods[0]
168             val primitive = primitiveMethod.returnType()
169             primitive.assertPrimitiveTypeItem {
170                 assertThat(annotationNames()).containsExactly("test.pkg.A")
171             }
172             assertThat(primitiveMethod.annotationNames()).containsExactly("test.pkg.A")
173 
174             // @test.pkg.A String
175             val stringMethod = methods[1]
176             val string = stringMethod.returnType()
177             string.assertClassTypeItem {
178                 assertThat(annotationNames()).containsExactly("test.pkg.A")
179             }
180             // The Kotlin version puts a nullability annotation on the method
181             val stringMethodAnnotations =
182                 stringMethod.annotationNames().filter { !isNullnessAnnotation(it.orEmpty()) }
183             assertThat(stringMethodAnnotations).containsExactly("test.pkg.A")
184 
185             // @test.pkg.A T
186             val variableMethod = methods[2]
187             val variable = variableMethod.returnType()
188             val typeParameter = variableMethod.typeParameterList.single()
189             variable.assertReferencesTypeParameter(typeParameter) {
190                 assertThat(annotationNames()).containsExactly("test.pkg.A")
191             }
192             assertThat(variableMethod.annotationNames()).containsExactly("test.pkg.A")
193         }
194     }
195 
196     @Test
197     fun `Test kotlin type-use annotations with multiple allowed targets on non-type target`() {
198         runCodebaseTest(
199             kotlin(
200                 """
201                     package test.pkg
202                     class Foo {
203                         // @A can be applied to a function or type.
204                         // Because of the positioning, it should apply to the function here.
205                         @A fun foo(): Int {}
206                         @A fun foo(): String {}
207                         @A fun <T> foo(): T {}
208                     }
209                     @Target(AnnotationTarget.FUNCTION, AnnotationTarget.TYPE)
210                     annotation class A
211                 """
212             ),
213             testFixture =
214                 TestFixture(
215                     // Use the noOpAnnotationManager to avoid annotation name normalizing as the
216                     // annotation names are important for this test.
217                     annotationManager = noOpAnnotationManager,
218                 ),
219         ) {
220             val methods = codebase.assertClass("test.pkg.Foo").methods()
221             assertThat(methods).hasSize(3)
222 
223             val primitiveMethod = methods[0]
224             val primitive = primitiveMethod.returnType()
225             primitive.assertPrimitiveTypeItem { assertThat(annotationNames()).isEmpty() }
226             assertThat(primitiveMethod.annotationNames()).containsExactly("test.pkg.A")
227 
228             val stringMethod = methods[1]
229             val string = stringMethod.returnType()
230             string.assertClassTypeItem { assertThat(annotationNames()).isEmpty() }
231             assertThat(stringMethod.annotationNames())
232                 .containsExactly("org.jetbrains.annotations.NotNull", "test.pkg.A")
233 
234             val variableMethod = methods[2]
235             val variable = variableMethod.returnType()
236             val typeParameter = variableMethod.typeParameterList.single()
237             variable.assertReferencesTypeParameter(typeParameter) {
238                 assertThat(annotationNames()).isEmpty()
239             }
240             assertThat(variableMethod.annotationNames()).containsExactly("test.pkg.A")
241         }
242     }
243 
244     @Test
245     fun `Test filtering of annotations based on target usages`() {
246         runCodebaseTest(
247             java(
248                 """
249                 package test.pkg;
250                 public class Foo {
251                     public @A String bar(@A int arg) {}
252                     public @A String baz;
253                 }
254 
255                 @java.lang.annotation.Target({ java.lang.annotation.ElementType.TYPE_USE, java.lang.annotation.ElementType.PARAMETER })
256                 public @interface A {}
257             """
258                     .trimIndent()
259             )
260         ) {
261             val fooClass = codebase.assertClass("test.pkg.Foo")
262 
263             // @A is TYPE_USE and PARAMETER, so it should not appear on the method
264             val method = fooClass.methods().single()
265             assertThat(method.annotationNames()).isEmpty()
266             val methodReturn = method.returnType()
267             assertThat(methodReturn.annotationNames()).containsExactly("test.pkg.A")
268 
269             // @A is TYPE_USE and PARAMETER, so it should appear on the parameter as well as type
270             val methodParam = method.parameters().single()
271             assertThat(methodParam.annotationNames()).containsExactly("test.pkg.A")
272             val methodParamType = methodParam.type()
273             assertThat(methodParamType.annotationNames()).containsExactly("test.pkg.A")
274 
275             // @A is TYPE_USE and PARAMETER, so it should not appear on the field
276             val field = fooClass.fields().single()
277             assertThat(field.annotationNames()).isEmpty()
278             val fieldType = field.type()
279             assertThat(fieldType.annotationNames()).containsExactly("test.pkg.A")
280         }
281     }
282 
283     @Test
284     fun `Test annotations on qualified class type`() {
285         runCodebaseTest(
286             java(
287                 """
288                     package test.pkg;
289                     public class Foo {
290                         public [email protected] Foo foo() {}
291                     }
292                 """
293             ),
294             signature(
295                 """
296                     // Signature format: 2.0
297                     package test.pkg {
298                       public class Foo {
299                         method public [email protected] Foo foo();
300                       }
301                     }
302                 """
303                     .trimIndent()
304             )
305         ) {
306             val method = codebase.assertClass("test.pkg.Foo").methods().single()
307             assertThat(method.annotationNames()).isEmpty()
308 
309             val returnType = method.returnType()
310             returnType.assertClassTypeItem {
311                 assertThat(qualifiedName).isEqualTo("test.pkg.Foo")
312                 assertThat(annotationNames()).containsExactly("test.pkg.A")
313             }
314         }
315     }
316 
317     @Test
318     fun `Test annotations on class type parameters`() {
319         runCodebaseTest(
320             java(
321                 """
322                     package test.pkg;
323 
324                     public class Foo {
325                         public [email protected] Map<[email protected] @test.pkg.C String, [email protected] String> foo() {}
326                     }
327                 """
328             ),
329             signature(
330                 """
331                     // Signature format: 2.0
332                     package test.pkg {
333                       public class Foo {
334                         method public [email protected] Map<[email protected] @test.pkg.C String, [email protected] String> foo();
335                       }
336                     }
337                 """
338                     .trimIndent()
339             )
340         ) {
341             val method = codebase.assertClass("test.pkg.Foo").methods().single()
342             assertThat(method.annotationNames()).isEmpty()
343 
344             val mapType = method.returnType()
345             mapType.assertClassTypeItem {
346                 assertThat(annotationNames()).containsExactly("test.pkg.A")
347                 assertThat(arguments).hasSize(2)
348 
349                 // [email protected] @test.pkg.C String
350                 val string1 = arguments[0]
351                 assertThat(string1.isString()).isTrue()
352                 assertThat(string1.annotationNames()).containsExactly("test.pkg.B", "test.pkg.C")
353 
354                 // [email protected] String
355                 val string2 = arguments[1]
356                 assertThat(string2.isString()).isTrue()
357                 assertThat(string2.annotationNames()).containsExactly("test.pkg.D")
358             }
359         }
360     }
361 
362     @Test
363     fun `Test annotations on array type and component type`() {
364         runCodebaseTest(
365             java(
366                 """
367                     package test.pkg;
368                     public class Foo {
369                         public [email protected] @test.pkg.B Foo @test.pkg.B @test.pkg.C [] foo() {}
370                     }
371                 """
372             ),
373             signature(
374                 """
375                     // Signature format: 2.0
376                     package test.pkg {
377                       public class Foo {
378                         method public [email protected] @test.pkg.B Foo @test.pkg.B @test.pkg.C [] foo();
379                       }
380                     }
381                 """
382                     .trimIndent()
383             )
384         ) {
385             val method = codebase.assertClass("test.pkg.Foo").methods().single()
386             assertThat(method.annotationNames()).isEmpty()
387 
388             val returnType = method.returnType()
389             returnType.assertArrayTypeItem {
390                 assertThat(annotationNames()).containsExactly("test.pkg.B", "test.pkg.C")
391 
392                 componentType.assertClassTypeItem {
393                     assertThat(qualifiedName).isEqualTo("test.pkg.Foo")
394                     assertThat(annotationNames()).containsExactly("test.pkg.A", "test.pkg.B")
395                 }
396             }
397         }
398     }
399 
400     @Test
401     fun `Test leading annotation on array type`() {
402         runCodebaseTest(
403             java(
404                 """
405                     package test.pkg;
406                     public class Foo {
407                         public <T> @test.pkg.A T[] foo() {}
408                     }
409                 """
410             ),
411             signature(
412                 """
413                     // Signature format: 5.0
414                     // - kotlin-name-type-order=yes
415                     // - include-type-use-annotations=yes
416                     package test.pkg {
417                       public class Foo {
418                         method public <T> foo(): @test.pkg.A T[];
419                       }
420                     }
421                 """
422                     .trimIndent()
423             )
424         ) {
425             val method = codebase.assertClass("test.pkg.Foo").methods().single()
426             val methodTypeParam = method.typeParameterList.single()
427             val returnType = method.returnType()
428             returnType.assertArrayTypeItem {
429                 componentType.assertReferencesTypeParameter(methodTypeParam) {
430                     assertThat(annotationNames()).containsExactly("test.pkg.A")
431                 }
432             }
433         }
434     }
435 
436     @Test
437     fun `Test annotations on multidimensional array`() {
438         runCodebaseTest(
439             java(
440                 """
441                     package test.pkg;
442                     public class Foo {
443                         public [email protected] Foo @test.pkg.B [] @test.pkg.C [] @test.pkg.D [] foo() {}
444                     }
445                 """
446                     .trimIndent()
447             ),
448             signature(
449                 """
450                     // Signature format: 2.0
451                     package test.pkg {
452                       public class Foo {
453                         method public [email protected] Foo @test.pkg.B [] @test.pkg.C [] @test.pkg.D [] foo();
454                       }
455                     }
456                 """
457                     .trimIndent()
458             )
459         ) {
460             val method = codebase.assertClass("test.pkg.Foo").methods().single()
461             assertThat(method.annotationNames()).isEmpty()
462 
463             val returnType = method.returnType()
464             // Outer array
465             returnType.assertArrayTypeItem {
466                 assertThat(annotationNames()).containsExactly("test.pkg.B")
467 
468                 // Middle array
469                 componentType.assertArrayTypeItem {
470                     assertThat(annotationNames()).containsExactly("test.pkg.C")
471 
472                     // Inner array
473                     componentType.assertArrayTypeItem {
474                         assertThat(annotationNames()).containsExactly("test.pkg.D")
475 
476                         // Component type
477                         componentType.assertClassTypeItem {
478                             assertThat(qualifiedName).isEqualTo("test.pkg.Foo")
479                             assertThat(annotationNames()).containsExactly("test.pkg.A")
480                         }
481                     }
482                 }
483             }
484         }
485     }
486 
487     @Test
488     fun `Test annotations on multidimensional vararg array`() {
489         runCodebaseTest(
490             java(
491                 """
492                     package test.pkg;
493                     public class Foo {
494                         public void foo([email protected] Foo @test.pkg.B [] @test.pkg.C [] @test.pkg.D ... arg) {}
495                     }
496                 """
497                     .trimIndent()
498             ),
499             signature(
500                 """
501                     // Signature format: 4.0
502                     package test.pkg {
503                       public class Foo {
504                         method public void foo([email protected] Foo @test.pkg.B [] @test.pkg.C [] @test.pkg.D ...);
505                       }
506                     }
507                 """
508                     .trimIndent()
509             )
510         ) {
511             val type =
512                 codebase.assertClass("test.pkg.Foo").methods().single().parameters().single().type()
513             type.assertArrayTypeItem {
514                 assertThat(isVarargs).isTrue()
515                 assertThat(annotationNames()).containsExactly("test.pkg.B")
516 
517                 // Middle array
518                 componentType.assertArrayTypeItem {
519                     assertThat(annotationNames()).containsExactly("test.pkg.C")
520 
521                     // Inner array
522                     componentType.assertArrayTypeItem {
523                         assertThat(annotationNames()).containsExactly("test.pkg.D")
524 
525                         // Component type
526                         componentType.assertClassTypeItem {
527                             assertThat(qualifiedName).isEqualTo("test.pkg.Foo")
528                             assertThat(annotationNames()).containsExactly("test.pkg.A")
529                         }
530                     }
531                 }
532             }
533         }
534     }
535 
536     @Test
537     fun `Test inner parameterized types with annotations`() {
538         runCodebaseTest(
539             java(
540                 """
541                     package test.pkg;
542                     public class Outer<O> {
543                         public class Inner<I> {
544                         }
545 
546                         public <P1, P2> [email protected] Outer<@test.pkg.B P1>[email protected] Inner<@test.pkg.D P2> foo() {
547                             return new Outer<P1>.Inner<P2>();
548                         }
549                     }
550                 """
551             ),
552             signature(
553                 """
554                     // Signature format: 3.0
555                     package test.pkg {
556                       public class Outer<O> {
557                         ctor public Outer();
558                         method public <P1, P2> [email protected] Outer<@test.pkg.B P1!>[email protected] Inner<@test.pkg.D P2!>! foo();
559                       }
560                       public class Outer.Inner<I> {
561                         ctor public Outer.Inner();
562                       }
563                     }
564                 """
565                     .trimIndent()
566             )
567         ) {
568             val method = codebase.assertClass("test.pkg.Outer").methods().single()
569             val methodTypeParameters = method.typeParameterList
570             assertThat(methodTypeParameters).hasSize(2)
571             val p1 = methodTypeParameters[0]
572             val p2 = methodTypeParameters[1]
573 
574             // Outer<P1>.Inner<P2>
575             val returnType = method.returnType()
576             returnType.assertClassTypeItem {
577                 assertThat(qualifiedName).isEqualTo("test.pkg.Outer.Inner")
578                 assertThat(arguments).hasSize(1)
579                 assertThat(annotationNames()).containsExactly("test.pkg.C")
580 
581                 val innerTypeArgument = arguments.single()
582                 innerTypeArgument.assertReferencesTypeParameter(p2) {
583                     assertThat(name).isEqualTo("P2")
584                     assertThat(annotationNames()).containsExactly("test.pkg.D")
585                 }
586 
587                 outerClassType.assertNotNullTypeItem {
588                     assertThat(qualifiedName).isEqualTo("test.pkg.Outer")
589                     assertThat(outerClassType).isNull()
590                     assertThat(arguments).hasSize(1)
591                     assertThat(annotationNames()).containsExactly("test.pkg.A")
592 
593                     val outerClassArgument = arguments.single()
594                     outerClassArgument.assertReferencesTypeParameter(p1) {
595                         assertThat(name).isEqualTo("P1")
596                         assertThat(annotationNames()).containsExactly("test.pkg.B")
597                     }
598                 }
599             }
600         }
601     }
602 
603     @Test
604     fun `Test interface types`() {
605         runCodebaseTest(
606             java(
607                 """
608                     package test.pkg;
609                     public class Foo implements [email protected] Bar, test.pkg.Baz {}
610                 """
611             ),
612             signature(
613                 """
614                     // Signature format: 4.0
615                     package test.pkg {
616                       public class Foo implements [email protected] Bar, test.pkg.Baz {
617                       }
618                     }
619                 """
620                     .trimIndent()
621             )
622         ) {
623             val foo = codebase.assertClass("test.pkg.Foo")
624             val interfaces = foo.interfaceTypes()
625             assertThat(interfaces).hasSize(2)
626 
627             val bar = interfaces[0]
628             assertThat(bar.qualifiedName).isEqualTo("test.pkg.Bar")
629             val annotations = bar.modifiers.annotations
630             assertThat(annotations).hasSize(1)
631             assertThat(annotations.single().qualifiedName).isEqualTo("test.pkg.A")
632 
633             val baz = interfaces[1]
634             assertThat(baz.qualifiedName).isEqualTo("test.pkg.Baz")
635         }
636     }
637 
638     @Test
639     fun `Test super class type`() {
640         runCodebaseTest(
641             java(
642                 """
643                     package test.pkg;
644                     public class Foo extends [email protected] Bar {}
645                     class Bar {}
646                     @interface A {}
647                 """
648             ),
649             signature(
650                 """
651                     // Signature format: 4.0
652                     package test.pkg {
653                       public class Foo extends [email protected] Bar {
654                       }
655                       public class Bar {
656                       }
657                       public @interface A {
658                       }
659                     }
660                 """
661                     .trimIndent()
662             )
663         ) {
664             val foo = codebase.assertClass("test.pkg.Foo")
665             val superClass = foo.superClassType()
666             assertThat(superClass).isNotNull()
667             superClass.assertClassTypeItem {
668                 assertThat(qualifiedName).isEqualTo("test.pkg.Bar")
669                 assertThat(annotationNames()).containsExactly("test.pkg.A")
670             }
671         }
672     }
673 
674     @Test
675     fun `Test super class and interface types of interface`() {
676         runCodebaseTest(
677             java(
678                 """
679                     package test.pkg;
680                     public interface Foo extends [email protected] Bar, [email protected] Baz<@test.pkg.C String>, test.pkg.Biz {}
681                 """
682             ),
683             signature(
684                 """
685                     // Signature format: 4.0
686                     package test.pkg {
687                       public interface Foo extends [email protected] Bar [email protected] Baz<@test.pkg.C String> test.pkg.Biz {
688                       }
689                     }
690                 """
691                     .trimIndent()
692             )
693         ) {
694             val foo = codebase.assertClass("test.pkg.Foo")
695             assertThat(foo.superClassType()).isNull()
696 
697             val interfaces = foo.interfaceTypes()
698             assertThat(interfaces).hasSize(3)
699 
700             val bar = interfaces[0]
701             assertThat(bar.qualifiedName).isEqualTo("test.pkg.Bar")
702             assertThat(bar.annotationNames()).containsExactly("test.pkg.A")
703 
704             val baz = interfaces[1]
705             assertThat(baz.qualifiedName).isEqualTo("test.pkg.Baz")
706             assertThat(baz.arguments).hasSize(1)
707             assertThat(baz.annotationNames()).containsExactly("test.pkg.B")
708 
709             val bazTypeArgument = baz.arguments.single()
710             assertThat(bazTypeArgument.isString()).isTrue()
711             assertThat(bazTypeArgument.annotationNames()).containsExactly("test.pkg.C")
712 
713             val biz = interfaces[2]
714             assertThat(biz.qualifiedName).isEqualTo("test.pkg.Biz")
715             assertThat(biz.annotationNames()).isEmpty()
716         }
717     }
718 
719     @Test
720     fun `Test annotated array types in multiple contexts`() {
721         runCodebaseTest(
722             java(
723                 """
724                     package test.pkg;
725                     public class Foo {
726                         public test.pkg.Foo @test.pkg.A [] method(test.pkg.Foo @test.pkg.A [] arg) {}
727                         public test.pkg.Foo @test.pkg.A [] field;
728                     }
729                 """
730             ),
731             signature(
732                 """
733                     // Signature format: 4.0
734                     package test.pkg {
735                       public class Foo {
736                         method public test.pkg.Foo @test.pkg.A [] method(test.pkg.Foo @test.pkg.A []);
737                         field public test.pkg.Foo @test.pkg.A [] field;
738                         property public test.pkg.Foo @test.pkg.A [] prop;
739                       }
740                     }
741                 """
742                     .trimIndent()
743             )
744         ) {
745             val foo = codebase.assertClass("test.pkg.Foo")
746             val method = foo.methods().single()
747             val returnType = method.returnType()
748             val paramType = method.parameters().single().type()
749             val fieldType = foo.fields().single().type()
750             // Properties can't be defined in java, this is only present for signature type
751             val propertyType = foo.properties().singleOrNull()?.type()
752 
753             // Do full check for one type, then verify the others are equal
754             returnType.assertArrayTypeItem {
755                 assertThat(annotationNames()).containsExactly("test.pkg.A")
756 
757                 componentType.assertClassTypeItem {
758                     assertThat(qualifiedName).isEqualTo("test.pkg.Foo")
759                     assertThat(annotationNames()).isEmpty()
760                 }
761             }
762 
763             assertThat(returnType).isEqualTo(paramType)
764             assertThat(returnType).isEqualTo(fieldType)
765             if (propertyType != null) {
766                 assertThat(returnType).isEqualTo(propertyType)
767             }
768         }
769     }
770 
771     @Test
772     fun `Test annotations with spaces in the annotation string`() {
773         runCodebaseTest(
774             signature(
775                 """
776                     // Signature format: 5.0
777                     // - include-type-use-annotations=yes
778                     // - kotlin-name-type-order=yes
779                     package test.pkg {
780                       public class Foo extends [email protected](a=1, b=2, c=3) Bar implements [email protected](a=1, b=2, c=3) Baz [email protected](a=1, b=2, c=3) Biz {
781                         method public <T> foo(_: @test.pkg.A(a=1, b=2, c=3) T @test.pkg.A(a=1, b=2, c=3) []): [email protected](a=1, b=2, c=3) List<[email protected](a=1, b=2, c=3) String>;
782                       }
783                     }
784                 """
785                     .trimIndent()
786             )
787         ) {
788             // Check the modifiers contain one annotation, `@test.pkg.A(a=1, b=2, c=3)`
789             val testModifiers = { modifiers: TypeModifiers ->
790                 assertThat(modifiers.annotations).hasSize(1)
791                 val annotation = modifiers.annotations.single()
792                 assertThat(annotation.qualifiedName).isEqualTo("test.pkg.A")
793                 val attributes = annotation.attributes
794                 assertThat(attributes.toString()).isEqualTo("[a=1, b=2, c=3]")
795             }
796             val fooClass = codebase.assertClass("test.pkg.Foo")
797 
798             val superClass = fooClass.superClassType()
799             superClass.assertNotNullTypeItem {
800                 assertThat(qualifiedName).isEqualTo("test.pkg.Bar")
801                 testModifiers(modifiers)
802             }
803 
804             val interfaces = fooClass.interfaceTypes()
805             val bazInterface = interfaces[0]
806             assertThat(bazInterface.qualifiedName).isEqualTo("test.pkg.Baz")
807             testModifiers(bazInterface.modifiers)
808             val bizInterface = interfaces[1]
809             assertThat(bizInterface.qualifiedName).isEqualTo("test.pkg.Biz")
810             testModifiers(bizInterface.modifiers)
811 
812             val fooMethod = fooClass.methods().single()
813             val typeParam = fooMethod.typeParameterList.single()
814 
815             val parameterType = fooMethod.parameters().single().type()
816             parameterType.assertArrayTypeItem {
817                 testModifiers(modifiers)
818                 componentType.assertReferencesTypeParameter(typeParam) { testModifiers(modifiers) }
819             }
820 
821             val stringList = fooMethod.returnType()
822             stringList.assertClassTypeItem {
823                 assertThat(qualifiedName).isEqualTo("java.util.List")
824                 testModifiers(modifiers)
825 
826                 val string = arguments.single()
827                 assertThat(string.isString()).isTrue()
828                 testModifiers(string.modifiers)
829             }
830         }
831     }
832 
833     @Test
834     fun `Test nullability of primitives`() {
835         runNullabilityTest(
836             java(
837                 """
838                     package test.pkg;
839                     public class Foo {
840                         public int foo() {}
841                     }
842                 """
843                     .trimIndent()
844             ),
845             signature(
846                 """
847                     // Signature format: 5.0
848                     // - include-type-use-annotations=yes
849                     // - kotlin-name-type-order=yes
850                     // - kotlin-style-nulls=no
851                     package test.pkg {
852                       public class Foo {
853                         method public foo(): int;
854                       }
855                     }
856                 """
857                     .trimIndent()
858             ),
859             kotlin(
860                 """
861                     package test.pkg
862                     class Foo {
863                         fun foo(): Int {}
864                     }
865                 """
866                     .trimIndent()
867             ),
868             signature(
869                 """
870                     // Signature format: 5.0
871                     // - include-type-use-annotations=yes
872                     // - kotlin-name-type-order=yes
873                     package test.pkg {
874                       public class Foo {
875                         method public foo(): int;
876                       }
877                     }
878                 """
879                     .trimIndent()
880             )
881         ) {
882             val primitive = codebase.assertClass("test.pkg.Foo").methods().single().returnType()
883             // Primitives are always non-null without an annotation needed
884             primitive.assertHasNonNullNullability(expectAnnotation = false)
885         }
886     }
887 
888     @Test
889     fun `Test nullability of simple classes`() {
890         runNullabilityTest(
891             java(
892                 """
893                     package test.pkg;
894                     import libcore.util.NonNull;
895                     import libcore.util.Nullable;
896                     public class Foo {
897                         public String platformString() {}
898                         public @Nullable String nullableString() {}
899                         public @NonNull String nonNullString() {}
900                     }
901                 """
902                     .trimIndent()
903             ),
904             signature(
905                 """
906                     // Signature format: 5.0
907                     // - include-type-use-annotations=yes
908                     // - kotlin-name-type-order=yes
909                     // - kotlin-style-nulls=no
910                     package test.pkg {
911                       public class Foo {
912                         method public platformString(): String;
913                         method public nullableString(): @libcore.util.Nullable String;
914                         method public nonNullString(): @libcore.util.NonNull String;
915                       }
916                     }
917                 """
918                     .trimIndent()
919             ),
920             kotlin(
921                 """
922                     package test.pkg
923                     class Foo {
924                         fun nullableString(): String? {}
925                         fun nonNullString(): String {}
926                     }
927                 """
928                     .trimIndent()
929             ),
930             signature(
931                 """
932                     // Signature format: 5.0
933                     // - include-type-use-annotations=yes
934                     // - kotlin-name-type-order=yes
935                     package test.pkg {
936                       public class Foo {
937                         method public platformString(): String!;
938                         method public nullableString(): String?;
939                         method public nonNullString(): String;
940                       }
941                     }
942                 """
943                     .trimIndent()
944             )
945         ) {
946             val fooClass = codebase.assertClass("test.pkg.Foo")
947 
948             // Platform nullability isn't possible from Kotlin
949             if (inputFormat != InputFormat.KOTLIN) {
950                 val platformString = fooClass.assertMethod("platformString", "").returnType()
951                 assertThat(platformString.modifiers.nullability).isEqualTo(PLATFORM)
952             }
953 
954             val nullableString = fooClass.assertMethod("nullableString", "").returnType()
955             nullableString.assertHasNullableNullability(nullabilityFromAnnotations)
956 
957             val nonNullString = fooClass.assertMethod("nonNullString", "").returnType()
958             nonNullString.assertHasNonNullNullability(nullabilityFromAnnotations)
959         }
960     }
961 
962     @Test
963     fun `Test nullability of arrays`() {
964         runNullabilityTest(
965             java(
966                 """
967                     package test.pkg;
968                     import libcore.util.NonNull;
969                     import libcore.util.Nullable;
970                     public class Foo {
971                         public String[] platformStringPlatformArray() {}
972                         public java.lang.@NonNull String[] nonNullStringPlatformArray() {}
973                         public String @Nullable [] platformStringNullableArray() {}
974                         public java.lang.@Nullable String @Nullable [] nullableStringNullableArray() {}
975                         public java.lang.@Nullable String @NonNull [] nullableStringNonNullArray() {}
976                     }
977                 """
978                     .trimIndent()
979             ),
980             signature(
981                 """
982                     // Signature format: 5.0
983                     // - include-type-use-annotations=yes
984                     // - kotlin-name-type-order=yes
985                     // - kotlin-style-nulls=no
986                     package test.pkg {
987                       public class Foo {
988                         method public nonNullStringPlatformArray(): @NonNull String[];
989                         method public nullableStringNonNullArray(): @Nullable String @NonNull [];
990                         method public nullableStringNullableArray(): @Nullable String @Nullable [];
991                         method public platformStringNullableArray(): String @Nullable [];
992                         method public platformStringPlatformArray(): String[];
993                       }
994                     }
995                 """
996                     .trimIndent()
997             ),
998             kotlin(
999                 """
1000                     package test.pkg
1001                     class Foo {
1002                         fun nullableStringNullableArray(): Array<String?>? {}
1003                         fun nullableStringNonNullArray(): Array<String?> {}
1004                     }
1005                 """
1006                     .trimIndent()
1007             ),
1008             signature(
1009                 """
1010                     // Signature format: 5.0
1011                     // - include-type-use-annotations=yes
1012                     // - kotlin-name-type-order=yes
1013                     package test.pkg {
1014                       public class Foo {
1015                         method public nonNullStringPlatformArray(): String[]!;
1016                         method public nullableStringNonNullArray(): String?[];
1017                         method public nullableStringNullableArray(): String?[]?;
1018                         method public platformStringNullableArray(): String![]?;
1019                         method public platformStringPlatformArray(): String![]!;
1020                       }
1021                     }
1022                 """
1023                     .trimIndent()
1024             )
1025         ) {
1026             val fooClass = codebase.assertClass("test.pkg.Foo")
1027 
1028             // Platform nullability isn't possible from Kotlin
1029             if (inputFormat != InputFormat.KOTLIN) {
1030                 val platformStringPlatformArray =
1031                     fooClass.assertMethod("platformStringPlatformArray", "").returnType()
1032                 platformStringPlatformArray.assertArrayTypeItem {
1033                     assertHasPlatformNullability()
1034                     componentType.assertHasPlatformNullability()
1035                 }
1036             }
1037 
1038             // Platform nullability isn't possible from Kotlin
1039             if (inputFormat != InputFormat.KOTLIN) {
1040                 val platformStringNullableArray =
1041                     fooClass.assertMethod("platformStringNullableArray", "").returnType()
1042                 platformStringNullableArray.assertArrayTypeItem {
1043                     assertHasNullableNullability(nullabilityFromAnnotations)
1044                     componentType.assertHasPlatformNullability()
1045                 }
1046             }
1047 
1048             // Platform nullability isn't possible from Kotlin
1049             if (inputFormat != InputFormat.KOTLIN) {
1050                 val nonNullStringPlatformArray =
1051                     fooClass.assertMethod("nonNullStringPlatformArray", "").returnType()
1052                 nonNullStringPlatformArray.assertArrayTypeItem {
1053                     assertHasPlatformNullability()
1054                     componentType.assertHasNonNullNullability(nullabilityFromAnnotations)
1055                 }
1056             }
1057 
1058             val nullableStringNonNullArray =
1059                 fooClass.assertMethod("nullableStringNonNullArray", "").returnType()
1060             nullableStringNonNullArray.assertArrayTypeItem {
1061                 assertHasNonNullNullability(nullabilityFromAnnotations)
1062                 componentType.assertHasNullableNullability(nullabilityFromAnnotations)
1063             }
1064 
1065             val nullableStringNullableArray =
1066                 fooClass.assertMethod("nullableStringNullableArray", "").returnType()
1067             nullableStringNullableArray.assertArrayTypeItem {
1068                 assertHasNullableNullability(nullabilityFromAnnotations)
1069                 componentType.assertHasNullableNullability(nullabilityFromAnnotations)
1070             }
1071         }
1072     }
1073 
1074     @Test
1075     fun `Test nullability of multi-dimensional arrays`() {
1076         runNullabilityTest(
1077             java(
1078                 """
1079                     package test.pkg;
1080                     import libcore.util.NonNull;
1081                     import libcore.util.Nullable;
1082                     public class Foo {
1083                         public java.lang.@Nullable String @NonNull [] @Nullable [] @NonNull [] foo() {}
1084                     }
1085                 """
1086                     .trimIndent()
1087             ),
1088             signature(
1089                 """
1090                     // Signature format: 5.0
1091                     // - include-type-use-annotations=yes
1092                     // - kotlin-name-type-order=yes
1093                     // - kotlin-style-nulls=no
1094                     package test.pkg {
1095                       public class Foo {
1096                         ctor public Foo();
1097                         method public foo(): @Nullable String @NonNull [] @Nullable [] @NonNull [];
1098                       }
1099                     }
1100                 """
1101                     .trimIndent()
1102             ),
1103             kotlin(
1104                 """
1105                     package test.pkg
1106                     class Foo {
1107                         fun foo(): Array<Array<Array<String?>>?>
1108                     }
1109                 """
1110                     .trimIndent()
1111             ),
1112             signature(
1113                 """
1114                     // Signature format: 5.0
1115                     // - include-type-use-annotations=yes
1116                     // - kotlin-name-type-order=yes
1117                     package test.pkg {
1118                       public class Foo {
1119                         ctor public Foo();
1120                         method public foo(): String?[][]?[];
1121                       }
1122                     }
1123                 """
1124                     .trimIndent()
1125             )
1126         ) {
1127             val fooClass = codebase.assertClass("test.pkg.Foo")
1128 
1129             val array3d = fooClass.methods().single().returnType()
1130             array3d.assertArrayTypeItem {
1131                 assertHasNonNullNullability(nullabilityFromAnnotations)
1132 
1133                 componentType.assertArrayTypeItem {
1134                     assertHasNullableNullability(nullabilityFromAnnotations)
1135 
1136                     componentType.assertArrayTypeItem {
1137                         assertHasNonNullNullability(nullabilityFromAnnotations)
1138                         componentType.assertHasNullableNullability(nullabilityFromAnnotations)
1139                     }
1140                 }
1141             }
1142         }
1143     }
1144 
1145     @Test
1146     fun `Test nullability of varargs`() {
1147         runNullabilityTest(
1148             java(
1149                 """
1150                     package test.pkg;
1151                     import libcore.util.NonNull;
1152                     import libcore.util.Nullable;
1153                     public class Foo {
1154                         public void platformStringPlatformVararg(String... arg) {}
1155                         public void nullableStringPlatformVararg(java.lang.@Nullable String... arg) {}
1156                         public void platformStringNullableVararg(String @Nullable ... arg) {}
1157                         public void nullableStringNullableVararg(java.lang.@Nullable String @Nullable ... arg) {}
1158                         public void nullableStringNonNullVararg(java.lang.@Nullable String @NonNull ... arg) {}
1159                     }
1160                 """
1161                     .trimIndent()
1162             ),
1163             signature(
1164                 """
1165                     // Signature format: 5.0
1166                     // - include-type-use-annotations=yes
1167                     // - kotlin-name-type-order=yes
1168                     // - kotlin-style-nulls=no
1169                     package test.pkg {
1170                       public class Foo {
1171                         method public platformStringPlatformVararg(arg: String...): void;
1172                         method public nullableStringPlatformVararg(arg: @Nullable String...): void;
1173                         method public platformStringNullableVararg(arg: String @Nullable ...): void;
1174                         method public nullableStringNullableVararg(arg: @Nullable String @Nullable ...): void;
1175                         method public nullableStringNonNullVararg(arg: @Nullable String @NonNull ...): void;
1176                       }
1177                     }
1178                 """
1179                     .trimIndent()
1180             ),
1181             kotlin(
1182                 """
1183                     package test.pkg
1184                     class Foo {
1185                         // Platform nullability isn't possible
1186                         // Nullable varargs aren't possible
1187                         fun nullableStringNonNullVararg(vararg arg: String?) = Unit
1188                     }
1189                 """
1190                     .trimIndent()
1191             ),
1192             signature(
1193                 """
1194                     // Signature format: 5.0
1195                     // - include-type-use-annotations=yes
1196                     // - kotlin-name-type-order=yes
1197                     package test.pkg {
1198                       public class Foo {
1199                         method public platformStringPlatformVararg(arg: String!...!): void;
1200                         method public nullableStringPlatformVararg(arg: String?...!): void;
1201                         method public platformStringNullableVararg(arg: String!...?): void;
1202                         method public nullableStringNullableVararg(arg: String?...?): void;
1203                         method public nullableStringNonNullVararg(arg: String?...): void;
1204                       }
1205                     }
1206                 """
1207                     .trimIndent()
1208             )
1209         ) {
1210             val fooClass = codebase.assertClass("test.pkg.Foo")
1211 
1212             if (inputFormat != InputFormat.KOTLIN) {
1213                 val platformStringPlatformVararg =
1214                     fooClass
1215                         .assertMethod("platformStringPlatformVararg", "java.lang.String[]")
1216                         .parameters()
1217                         .single()
1218                         .type()
1219                 platformStringPlatformVararg.assertArrayTypeItem {
1220                     assertHasPlatformNullability()
1221                     componentType.assertHasPlatformNullability()
1222                 }
1223             }
1224 
1225             if (inputFormat != InputFormat.KOTLIN) {
1226                 val nullableStringPlatformVararg =
1227                     fooClass
1228                         .assertMethod("nullableStringPlatformVararg", "java.lang.String[]")
1229                         .parameters()
1230                         .single()
1231                         .type()
1232                 nullableStringPlatformVararg.assertArrayTypeItem {
1233                     assertHasPlatformNullability()
1234                     componentType.assertHasNullableNullability(nullabilityFromAnnotations)
1235                 }
1236             }
1237 
1238             if (inputFormat != InputFormat.KOTLIN) {
1239                 val platformStringNullableVararg =
1240                     fooClass
1241                         .assertMethod("platformStringNullableVararg", "java.lang.String[]")
1242                         .parameters()
1243                         .single()
1244                         .type()
1245                 platformStringNullableVararg.assertArrayTypeItem {
1246                     assertHasNullableNullability(nullabilityFromAnnotations)
1247                     componentType.assertHasPlatformNullability()
1248                 }
1249             }
1250 
1251             if (inputFormat != InputFormat.KOTLIN) {
1252                 val nullableStringNullableVararg =
1253                     fooClass
1254                         .assertMethod("nullableStringNullableVararg", "java.lang.String[]")
1255                         .parameters()
1256                         .single()
1257                         .type()
1258                 nullableStringNullableVararg.assertArrayTypeItem {
1259                     assertHasNullableNullability(nullabilityFromAnnotations)
1260                     componentType.assertHasNullableNullability(nullabilityFromAnnotations)
1261                 }
1262             }
1263 
1264             // The only version that exists for Kotlin
1265             val nullableStringNonNullVararg =
1266                 fooClass
1267                     .assertMethod("nullableStringNonNullVararg", "java.lang.String[]")
1268                     .parameters()
1269                     .single()
1270                     .type()
1271             nullableStringNonNullVararg.assertHasNonNullNullability(nullabilityFromAnnotations)
1272             nullableStringNonNullVararg.assertArrayTypeItem {
1273                 componentType.assertHasNullableNullability(nullabilityFromAnnotations)
1274             }
1275         }
1276     }
1277 
1278     @Test
1279     fun `Test nullability of classes with parameters`() {
1280         runNullabilityTest(
1281             java(
1282                 """
1283                     package test.pkg;
1284                     import java.util.List;
1285                     import java.util.Map;
1286                     import libcore.util.NonNull;
1287                     import libcore.util.Nullable;
1288                     public class Foo {
1289                         public @Nullable List<String> nullableListPlatformString() {}
1290                         public @NonNull List<@Nullable String> nonNullListNullableString() {}
1291                         public @Nullable Map<@NonNull Integer, @Nullable String> nullableMap() {}
1292                     }
1293                 """
1294                     .trimIndent()
1295             ),
1296             signature(
1297                 """
1298                     // Signature format: 5.0
1299                     // - include-type-use-annotations=yes
1300                     // - kotlin-name-type-order=yes
1301                     // - kotlin-style-nulls=no
1302                     package test.pkg {
1303                       public class Foo {
1304                         method public nullableListPlatformString(): java.util.@Nullable List<java.lang.String>;
1305                         method public nonNullListNullableString(): java.util.@NonNull List<java.lang.@Nullable String>;
1306                         method public nullableMap(): java.util.@Nullable Map<java.lang.@NonNull Integer, java.lang.@Nullable String>;
1307                       }
1308                     }
1309                 """
1310                     .trimIndent()
1311             ),
1312             kotlin(
1313                 """
1314                     package test.pkg
1315                     class Foo {
1316                         fun nonNullListNullableString(): List<String?> {}
1317                         fun nullableMap(): Map<Int, String?>? {}
1318                     }
1319                 """
1320                     .trimIndent()
1321             ),
1322             signature(
1323                 """
1324                     // Signature format: 5.0
1325                     // - include-type-use-annotations=yes
1326                     // - kotlin-name-type-order=yes
1327                     package test.pkg {
1328                       public class Foo {
1329                         method public nullableListPlatformString(): java.util.List<java.lang.String!>?;
1330                         method public nonNullListNullableString(): java.util.List<java.lang.String?>;
1331                         method public nullableMap(): java.util.Map<java.lang.Integer, java.lang.String?>?;
1332                       }
1333                     }
1334                 """
1335                     .trimIndent()
1336             )
1337         ) {
1338             val fooClass = codebase.assertClass("test.pkg.Foo")
1339 
1340             // Platform type doesn't exist in Kotlin
1341             if (inputFormat != InputFormat.KOTLIN) {
1342                 val nullableListPlatformString =
1343                     fooClass.assertMethod("nullableListPlatformString", "").returnType()
1344                 nullableListPlatformString.assertClassTypeItem {
1345                     assertHasNullableNullability(nullabilityFromAnnotations)
1346                     arguments.single().assertHasPlatformNullability()
1347                 }
1348             }
1349 
1350             val nonNullListNullableString =
1351                 fooClass.assertMethod("nonNullListNullableString", "").returnType()
1352             nonNullListNullableString.assertClassTypeItem {
1353                 assertHasNonNullNullability(nullabilityFromAnnotations)
1354                 arguments.single().assertHasNullableNullability(nullabilityFromAnnotations)
1355             }
1356 
1357             val nullableMap = fooClass.assertMethod("nullableMap", "").returnType()
1358             nullableMap.assertClassTypeItem {
1359                 assertHasNullableNullability(nullabilityFromAnnotations)
1360                 // Non-null Integer
1361                 arguments[0].assertHasNonNullNullability(nullabilityFromAnnotations)
1362                 // Nullable String
1363                 arguments[1].assertHasNullableNullability(nullabilityFromAnnotations)
1364             }
1365         }
1366     }
1367 
1368     @Test
1369     fun `Test nullability of outer classes`() {
1370         runNullabilityTest(
1371             java(
1372                 """
1373                     package test.pkg;
1374                     import libcore.util.NonNull;
1375                     import libcore.util.Nullable;
1376                     public class Foo {
1377                         public Outer<@Nullable String>.@Nullable Inner<@NonNull String> foo();
1378                     }
1379                     public class Outer<P1> {
1380                         public class Inner<P2> {}
1381                     }
1382                 """
1383                     .trimIndent()
1384             ),
1385             signature(
1386                 """
1387                     // Signature format: 5.0
1388                     // - include-type-use-annotations=yes
1389                     // - kotlin-name-type-order=yes
1390                     // - kotlin-style-nulls=no
1391                     package test.pkg {
1392                       public class Foo {
1393                         method public foo(): test.pkg.Outer<[email protected] String>[email protected] Inner<[email protected] String>;
1394                       }
1395                     }
1396                 """
1397                     .trimIndent()
1398             ),
1399             kotlin(
1400                 """
1401                     package test.pkg
1402                     class Foo {
1403                         fun foo(): Outer<String?>.Inner<String>? {}
1404                     }
1405                     class Outer<P1> {
1406                         inner class Inner<P2>
1407                     }
1408                 """
1409                     .trimIndent()
1410             ),
1411             signature(
1412                 """
1413                     // Signature format: 5.0
1414                     // - include-type-use-annotations=yes
1415                     // - kotlin-name-type-order=yes
1416                     package test.pkg {
1417                       public class Foo {
1418                         method public foo(): test.pkg.Outer<java.lang.String?>.Inner<java.lang.String>?;
1419                       }
1420                     }
1421                 """
1422                     .trimIndent()
1423             ),
1424         ) {
1425             val innerClass = codebase.assertClass("test.pkg.Foo").methods().single().returnType()
1426             innerClass.assertClassTypeItem {
1427                 assertHasNullableNullability(nullabilityFromAnnotations)
1428                 arguments.single().assertHasNonNullNullability(nullabilityFromAnnotations)
1429 
1430                 // Outer class types can't be null and don't need to be annotated.
1431                 outerClassType.assertNotNullTypeItem {
1432                     assertHasNonNullNullability(expectAnnotation = false)
1433                     arguments.single().assertHasNullableNullability(nullabilityFromAnnotations)
1434                 }
1435             }
1436         }
1437     }
1438 
1439     @Test
1440     fun `Test nullability of wildcards`() {
1441         runNullabilityTest(
1442             java(
1443                 """
1444                     package test.pkg;
1445                     import libcore.util.NonNull;
1446                     import libcore.util.Nullable;
1447                     import java.util.List;
1448                     public class Foo<T> {
1449                         public @NonNull Foo<? extends @Nullable String> extendsBound() {}
1450                         public @NonNull Foo<? super @NonNull String> superBound() {}
1451                         public @NonNull Foo<?> unbounded() {}
1452                     }
1453                 """
1454                     .trimIndent()
1455             ),
1456             signature(
1457                 """
1458                     // Signature format: 5.0
1459                     // - kotlin-name-type-order=yes
1460                     // - include-type-use-annotations=yes
1461                     // - kotlin-style-nulls=no
1462                     package test.pkg {
1463                       public class Foo<T> {
1464                         method public extendsBound(): test.pkg.@NonNull Foo<? extends java.lang.@Nullable String>;
1465                         method public superBound(): test.pkg.@NonNull Foo<? super java.lang.@NonNull String>;
1466                         method public unbounded(): test.pkg.@NonNull Foo<?>;
1467                       }
1468                     }
1469                 """
1470                     .trimIndent()
1471             ),
1472             kotlin(
1473                 """
1474                     package test.pkg
1475                     class Foo<T> {
1476                         fun extendsBound(): Foo<out String?> {}
1477                         fun superBound(): Foo<in String> {}
1478                         fun unbounded(): Foo<*> {}
1479                     }
1480                 """
1481                     .trimIndent()
1482             ),
1483             signature(
1484                 """
1485                     // Signature format: 5.0
1486                     // - kotlin-name-type-order=yes
1487                     // - include-type-use-annotations=yes
1488                     package test.pkg {
1489                       public class Foo<T> {
1490                         method public extendsBound(): test.pkg.Foo<? extends java.lang.String?>;
1491                         method public superBound(): test.pkg.Foo<? super java.lang.String>;
1492                         method public unbounded(): test.pkg.Foo<?>;
1493                       }
1494                     }
1495                 """
1496                     .trimIndent()
1497             )
1498         ) {
1499             val fooClass = codebase.assertClass("test.pkg.Foo")
1500 
1501             val extendsBoundReturnType = fooClass.assertMethod("extendsBound", "").returnType()
1502             extendsBoundReturnType.assertClassTypeItem {
1503                 assertHasNonNullNullability(nullabilityFromAnnotations)
1504 
1505                 val argumentType = arguments.single()
1506                 argumentType.assertWildcardItem {
1507                     assertHasUndefinedNullability()
1508                     extendsBound.assertNotNullTypeItem {
1509                         assertHasNullableNullability(nullabilityFromAnnotations)
1510                     }
1511                 }
1512             }
1513 
1514             val superBoundReturnType = fooClass.assertMethod("superBound", "").returnType()
1515             superBoundReturnType.assertClassTypeItem {
1516                 assertHasNonNullNullability(nullabilityFromAnnotations)
1517 
1518                 val argumentType = arguments.single()
1519                 argumentType.assertWildcardItem {
1520                     assertHasUndefinedNullability()
1521                     superBound.assertNotNullTypeItem {
1522                         assertHasNonNullNullability(nullabilityFromAnnotations)
1523                     }
1524                 }
1525             }
1526 
1527             val unboundedReturnType = fooClass.assertMethod("unbounded", "").returnType()
1528             unboundedReturnType.assertClassTypeItem {
1529                 assertHasNonNullNullability(nullabilityFromAnnotations)
1530 
1531                 val argumentType = arguments.single()
1532                 argumentType.assertHasUndefinedNullability()
1533             }
1534         }
1535     }
1536 
1537     @Test
1538     fun `Test resetting nullability`() {
1539         // Mutating modifiers isn't supported for a text codebase due to type caching.
1540         val javaSource =
1541             inputSet(
1542                 java(
1543                     """
1544                         package test.pkg;
1545                         import libcore.util.Nullable;
1546                         public class Foo {
1547                             public java.lang.@Nullable String foo() {}
1548                         }
1549                     """
1550                         .trimIndent()
1551                 ),
1552                 KnownSourceFiles.libcoreNullableSource
1553             )
1554         val kotlinSource =
1555             kotlin(
1556                 """
1557                     package test.pkg
1558                     class Foo {
1559                         fun foo(): String? {}
1560                     }
1561                 """
1562                     .trimIndent()
1563             )
1564         val nullabilityTest = { codebase: Codebase, annotations: Boolean ->
1565             val stringType = codebase.assertClass("test.pkg.Foo").methods().single().returnType()
1566             // The type is originally nullable
1567             stringType.assertHasNullableNullability(annotations)
1568 
1569             // Set to platform
1570             val platformStringType = stringType.substitute(PLATFORM)
1571             platformStringType.assertHasPlatformNullability()
1572             // The annotation was not removed
1573             if (annotations) {
1574                 assertThat(platformStringType.annotationNames().single()).endsWith("Nullable")
1575             }
1576 
1577             // Set to non-null
1578             val nonNullStringType = stringType.substitute(NONNULL)
1579             assertThat(nonNullStringType.modifiers.nullability).isEqualTo(NONNULL)
1580             // The nullable annotation was not removed, a nonnull annotation was not added
1581             if (annotations) {
1582                 assertThat(nonNullStringType.annotationNames().single()).endsWith("Nullable")
1583             }
1584         }
1585 
1586         runCodebaseTest(javaSource) { nullabilityTest(codebase, true) }
1587         runCodebaseTest(kotlinSource) { nullabilityTest(codebase, false) }
1588     }
1589 
1590     @Test
1591     fun `Test nullability set through item annotations`() {
1592         runCodebaseTest(
1593             inputSet(
1594                 java(
1595                     """
1596                         package test.pkg;
1597                         import org.jetbrains.annotations.Nullable;
1598                         public class Foo {
1599                             public @Nullable String foo() {}
1600                         }
1601                     """
1602                         .trimIndent()
1603                 ),
1604                 java(
1605                     """
1606                         package org.jetbrains.annotations;
1607                         import java.lang.annotation.ElementType;
1608                         import java.lang.annotation.Target;
1609                         @Target({ ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER })
1610                         public @interface Nullable {}
1611                     """
1612                         .trimIndent()
1613                 )
1614             ),
1615             inputSet(
1616                 signature(
1617                     """
1618                         // Signature format: 5.0
1619                         // - kotlin-name-type-order=yes
1620                         // - include-type-use-annotations=yes
1621                         // - kotlin-style-nulls=no
1622                         package test.pkg {
1623                           public class Foo {
1624                             method @Nullable public foo(): String;
1625                           }
1626                         }
1627                     """
1628                         .trimIndent()
1629                 )
1630             )
1631         ) {
1632             val strType = codebase.assertClass("test.pkg.Foo").methods().single().returnType()
1633             // The annotation is on the item, not the type.
1634             strType.assertHasNullableNullability(expectAnnotation = false)
1635         }
1636     }
1637 
1638     @Test
1639     fun `Test implicit nullability of constants`() {
1640         runCodebaseTest(
1641             java(
1642                 """
1643                 package test.pkg;
1644                 public class Foo {
1645                     public final String nonNullStringConstant = "non null value";
1646                     public final String nullStringConstant = null;
1647                     public String nonConstantString = "non null value";
1648                 }
1649             """
1650                     .trimIndent()
1651             ),
1652             signature(
1653                 """
1654                 // Signature format: 2.0
1655                 package test.pkg {
1656                   public class Foo {
1657                     field public final String nonNullStringConstant = "non null value";
1658                     field public final String nullStringConstant;
1659                     field public String nonConstantString;
1660                   }
1661                 }
1662             """
1663                     .trimIndent()
1664             )
1665         ) {
1666             val fooClass = codebase.assertClass("test.pkg.Foo")
1667 
1668             val nonNullConstantType =
1669                 fooClass.fields().single { it.name() == "nonNullStringConstant" }.type()
1670             // Nullability not set through an annotation.
1671             nonNullConstantType.assertHasNonNullNullability(expectAnnotation = false)
1672 
1673             val nullConstantType =
1674                 fooClass.fields().single { it.name() == "nullStringConstant" }.type()
1675             nullConstantType.assertHasPlatformNullability()
1676 
1677             val nonConstantType =
1678                 fooClass.fields().single { it.name() == "nullStringConstant" }.type()
1679             nonConstantType.assertHasPlatformNullability()
1680         }
1681     }
1682 
1683     @Test
1684     fun `Test implicit nullability of constructor returns`() {
1685         runNullabilityTest(
1686             java(
1687                 """
1688                 package test.pkg;
1689                 public class Foo {}
1690             """
1691                     .trimIndent()
1692             ),
1693             signature(
1694                 """
1695                 // Signature format: 2.0
1696                 package test.pkg {
1697                   public class Foo {
1698                     ctor public Foo();
1699                   }
1700                 }
1701             """
1702                     .trimIndent()
1703             ),
1704             kotlin(
1705                 """
1706                 package test.pkg
1707                 class Foo
1708             """
1709                     .trimIndent()
1710             ),
1711             signature(
1712                 """
1713                 // Signature format: 5.0
1714                 package test.pkg {
1715                   public class Foo {
1716                     ctor public Foo();
1717                   }
1718                 }
1719             """
1720                     .trimIndent()
1721             )
1722         ) {
1723             val ctorReturn =
1724                 codebase.assertClass("test.pkg.Foo").constructors().single().returnType()
1725             // Constructor returns are always non-null without needing an annotation
1726             ctorReturn.assertHasNonNullNullability(expectAnnotation = false)
1727         }
1728     }
1729 
1730     @Test
1731     fun `Test implicit nullability of equals parameter`() {
1732         runCodebaseTest(
1733             java(
1734                 """
1735                     package test.pkg;
1736                     public class Foo {
1737                         @Override
1738                         public boolean equals(Object other) {}
1739                     }
1740                 """
1741                     .trimIndent()
1742             ),
1743             signature(
1744                 """
1745                     // Signature format: 5.0
1746                     // - kotlin-name-type-order=yes
1747                     // - include-type-use-annotations=yes
1748                     // - kotlin-style-nulls=no
1749                     package test.pkg {
1750                       public class Foo {
1751                         method public equals(other: Object): boolean;
1752                       }
1753                     }
1754                 """
1755                     .trimIndent()
1756             )
1757         ) {
1758             val equals = codebase.assertClass("test.pkg.Foo").methods().single()
1759             val objType = equals.parameters().single().type()
1760             // equals must accept null
1761             objType.assertHasNullableNullability(expectAnnotation = false)
1762         }
1763     }
1764 
1765     @Test
1766     fun `Test implicit nullability of toString`() {
1767         runCodebaseTest(
1768             java(
1769                 """
1770                     package test.pkg;
1771                     public class Foo {
1772                         @Override
1773                         public String toString() {}
1774                     }
1775                 """
1776                     .trimIndent()
1777             ),
1778             signature(
1779                 """
1780                     // Signature format: 5.0
1781                     // - kotlin-name-type-order=yes
1782                     // - include-type-use-annotations=yes
1783                     // - kotlin-style-nulls=no
1784                     package test.pkg {
1785                       public class Foo {
1786                         method public toString(): String;
1787                       }
1788                     }
1789                 """
1790                     .trimIndent()
1791             )
1792         ) {
1793             val strType = codebase.assertClass("test.pkg.Foo").methods().single().returnType()
1794             // toString must not return null
1795             strType.assertHasNonNullNullability(expectAnnotation = false)
1796         }
1797     }
1798 
1799     @Test
1800     fun `Test implicit nullability of annotation members`() {
1801         runCodebaseTest(
1802             java(
1803                 """
1804                     package test.pkg;
1805                     public @interface Foo {
1806                         String[] values();
1807                     }
1808                 """
1809                     .trimIndent()
1810             ),
1811             kotlin(
1812                 """
1813                     package test.pkg
1814                     annotation class Foo {
1815                         fun values(): Array<String>
1816                     }
1817                 """
1818                     .trimIndent()
1819             ),
1820             signature(
1821                 """
1822                     // Signature format: 5.0
1823                     // - kotlin-name-type-order=yes
1824                     // - include-type-use-annotations=yes
1825                     // - kotlin-style-nulls=no
1826                     package test.pkg {
1827                       public @interface Foo {
1828                         method public values(): String[];
1829                       }
1830                     }
1831                 """
1832                     .trimIndent()
1833             )
1834         ) {
1835             val strArray = codebase.assertClass("test.pkg.Foo").methods().single().returnType()
1836             strArray.assertArrayTypeItem {
1837                 assertHasNonNullNullability(expectAnnotation = false)
1838                 componentType.assertHasNonNullNullability(false)
1839             }
1840         }
1841     }
1842 
1843     @Test
1844     fun `Test nullness of Kotlin enum members`() {
1845         runCodebaseTest(
1846             kotlin(
1847                 """
1848                     package test.pkg
1849                     enum class Foo {
1850                         A
1851                     }
1852                 """
1853                     .trimIndent()
1854             )
1855         ) {
1856             val fooEnum = codebase.assertClass("test.pkg.Foo")
1857 
1858             // enum_constant public static final A: test.pkg.Foo;
1859             val enumConstant = fooEnum.fields().single()
1860             assertThat(enumConstant.isEnumConstant()).isTrue()
1861             enumConstant.type().assertHasNonNullNullability(expectAnnotation = false)
1862         }
1863     }
1864 
1865     @Test
1866     fun `Test nullness of companion object`() {
1867         runCodebaseTest(
1868             kotlin(
1869                 """
1870                     package test.pkg
1871                     class Foo {
1872                         companion object
1873                     }
1874                 """
1875                     .trimIndent()
1876             )
1877         ) {
1878             val fooClass = codebase.assertClass("test.pkg.Foo")
1879             val companionType = fooClass.fields().single().type()
1880             companionType.assertHasNonNullNullability(expectAnnotation = false)
1881         }
1882     }
1883 
1884     @Test
1885     fun `Test nullness of Kotlin lambda type`() {
1886         runCodebaseTest(
1887             kotlin(
1888                 """
1889                     package test.pkg
1890                     class Foo {
1891                         fun noParamToString(): () -> String {}
1892                         fun oneParamToString(): (String?) -> String {}
1893                         fun twoParamToString(): (String, Int?) -> String? {}
1894                         fun oneParamToUnit(): (String) -> Unit {}
1895                     }
1896                 """
1897                     .trimIndent()
1898             )
1899         ) {
1900             val fooClass = codebase.assertClass("test.pkg.Foo")
1901             // () -> String
1902             val noParamToString = fooClass.assertMethod("noParamToString", "").returnType()
1903             noParamToString.assertClassTypeItem {
1904                 assertHasNonNullNullability(expectAnnotation = false)
1905                 assertThat(arguments).hasSize(1)
1906                 arguments.single().assertHasNonNullNullability(expectAnnotation = false)
1907             }
1908 
1909             // (String?) -> String
1910             val oneParamToString = fooClass.assertMethod("oneParamToString", "").returnType()
1911             oneParamToString.assertClassTypeItem {
1912                 assertHasNonNullNullability(expectAnnotation = false)
1913                 assertThat(arguments).hasSize(2)
1914                 arguments[0].assertHasNullableNullability(expectAnnotation = false)
1915                 arguments[1].assertHasNonNullNullability(expectAnnotation = false)
1916             }
1917 
1918             // (String, Int?) -> String?
1919             val twoParamToString = fooClass.assertMethod("twoParamToString", "").returnType()
1920             twoParamToString.assertClassTypeItem {
1921                 assertHasNonNullNullability(expectAnnotation = false)
1922                 assertThat(arguments).hasSize(3)
1923                 arguments[0].assertHasNonNullNullability(expectAnnotation = false)
1924                 arguments[1].assertHasNullableNullability(expectAnnotation = false)
1925                 arguments[2].assertHasNullableNullability(expectAnnotation = false)
1926             }
1927 
1928             // (String) -> Unit
1929             val oneParamToUnit = fooClass.assertMethod("oneParamToUnit", "").returnType()
1930             oneParamToUnit.assertClassTypeItem {
1931                 assertHasNonNullNullability(expectAnnotation = false)
1932                 assertThat(arguments).hasSize(2)
1933                 arguments[0].assertHasNonNullNullability(expectAnnotation = false)
1934                 arguments[1].assertHasNonNullNullability(expectAnnotation = false)
1935             }
1936         }
1937     }
1938 
1939     @Test
1940     fun `Test inherited nullability of unbounded Kotlin type variables - usage is not null`() {
1941         runCodebaseTest(
1942             kotlin(
1943                 """
1944                     package test.pkg
1945                     class Foo<T> {
1946                         fun foo(): T {}
1947                     }
1948                 """
1949                     .trimIndent()
1950             )
1951         ) {
1952             // T is unbounded, so it has an implicit `Any?` bound, making it possibly nullable, but
1953             // not necessarily. That means the usage of the variable without any nullable suffix
1954             // doesn't have a nullability on its own, it depends on what type is used as the
1955             // parameter.
1956             val tVar = codebase.assertClass("test.pkg.Foo").methods().single().returnType()
1957             tVar.assertHasUndefinedNullability()
1958         }
1959     }
1960 
1961     @Test
1962     fun `Test inherited nullability of unbounded Kotlin type variables - usage is nullable`() {
1963         runCodebaseTest(
1964             kotlin(
1965                 """
1966                     package test.pkg
1967                     class Foo<T> {
1968                         fun foo(): T? {}
1969                     }
1970                 """
1971                     .trimIndent()
1972             )
1973         ) {
1974             // T is unbounded, so it has an implicit `Any?` bound, making it possibly nullable, but
1975             // not necessarily. That means the usage of the variable without any nullable suffix
1976             // doesn't have a nullability on its own, it depends on what type is used as the
1977             // parameter. However, when it has a nullable suffix then it is nullable.
1978             val tVar = codebase.assertClass("test.pkg.Foo").methods().single().returnType()
1979             tVar.assertHasNullableNullability()
1980         }
1981     }
1982 
1983     @Test
1984     fun `Test inherited nullability of bounded Kotlin type variables - bound is not nullable`() {
1985         runCodebaseTest(
1986             kotlin(
1987                 """
1988                     package test.pkg
1989                     class Foo<T : Any> {
1990                         fun foo(): T {}
1991                     }
1992                 """
1993                     .trimIndent()
1994             )
1995         ) {
1996             // T is bounded by `Any` so it cannot be nullable which means that the variable on its
1997             // own is not nullable.
1998             val tVar = codebase.assertClass("test.pkg.Foo").methods().single().returnType()
1999             tVar.assertHasNonNullNullability()
2000         }
2001     }
2002 
2003     @Test
2004     fun `Test inherited nullability of bounded Kotlin type variables - bound is nullable`() {
2005         runCodebaseTest(
2006             kotlin(
2007                 """
2008                     package test.pkg
2009                     class Foo<T : Number?> {
2010                         fun foo(): T {}
2011                     }
2012                 """
2013                     .trimIndent()
2014             )
2015         ) {
2016             // T is bounded by `Number?`, making it possibly nullable, but not necessarily. That
2017             // means the usage of the variable without any nullable suffix doesn't have a
2018             // nullability on its own, it depends on what type is used as the parameter.
2019             val tVar = codebase.assertClass("test.pkg.Foo").methods().single().returnType()
2020             tVar.assertHasUndefinedNullability()
2021         }
2022     }
2023 
2024     @Test
2025     fun `Test nullability of Kotlin properties and accessors`() {
2026         runCodebaseTest(
2027             kotlin(
2028                 """
2029                     package test.pkg
2030                     class Foo {
2031                         var nullableString: String?
2032                         var nonNullListNullableString: List<String?>
2033                     }
2034                 """
2035                     .trimIndent()
2036             )
2037         ) {
2038             val fooClass = codebase.assertClass("test.pkg.Foo")
2039 
2040             val nullableStringProp = fooClass.properties().single { it.name() == "nullableString" }
2041             nullableStringProp.type().assertHasNullableNullability(expectAnnotation = false)
2042             nullableStringProp.getter!!
2043                 .returnType()
2044                 .assertHasNullableNullability(expectAnnotation = false)
2045             nullableStringProp.setter!!
2046                 .parameters()
2047                 .single()
2048                 .type()
2049                 .assertHasNullableNullability(expectAnnotation = false)
2050 
2051             val nonNullListProp =
2052                 fooClass.properties().single { it.name() == "nonNullListNullableString" }
2053             val propType = nonNullListProp.type()
2054             val getterType = nonNullListProp.getter!!.returnType()
2055             val setterType = nonNullListProp.setter!!.parameters().single().type()
2056             propType.assertClassTypeItem {
2057                 assertHasNonNullNullability(expectAnnotation = false)
2058                 arguments.single().assertHasNullableNullability(expectAnnotation = false)
2059             }
2060             getterType.assertClassTypeItem {
2061                 assertHasNonNullNullability(expectAnnotation = false)
2062                 arguments.single().assertHasNullableNullability(expectAnnotation = false)
2063             }
2064             setterType.assertClassTypeItem {
2065                 assertHasNonNullNullability(expectAnnotation = false)
2066                 arguments.single().assertHasNullableNullability(expectAnnotation = false)
2067             }
2068         }
2069     }
2070 
2071     @Test
2072     fun `Test nullability of extension function type`() {
2073         runCodebaseTest(
2074             kotlin(
2075                 """
2076                     package test.pkg
2077                     class Foo {
2078                         fun foo(): String?.(Int, Int?) -> String {}
2079                     }
2080                 """
2081                     .trimIndent()
2082             )
2083         ) {
2084             val extensionFunctionType =
2085                 codebase.assertClass("test.pkg.Foo").methods().single().returnType()
2086             extensionFunctionType.assertClassTypeItem {
2087                 assertHasNonNullNullability(expectAnnotation = false)
2088                 val receiverType = arguments[0]
2089                 receiverType.assertHasNullableNullability(expectAnnotation = false)
2090                 val typeArgument1 = arguments[1]
2091                 typeArgument1.assertHasNonNullNullability(expectAnnotation = false)
2092                 val typeArgument2 = arguments[2]
2093                 typeArgument2.assertHasNullableNullability(expectAnnotation = false)
2094                 val returnType = arguments[3]
2095                 returnType.assertHasNonNullNullability(expectAnnotation = false)
2096             }
2097         }
2098     }
2099 
2100     @Test
2101     fun `Test nullability of typealias`() {
2102         runCodebaseTest(
2103             kotlin(
2104                 """
2105                     package test.pkg
2106                     class Foo {
2107                         fun foo(): FunctionType?
2108                     }
2109                     typealias FunctionType = (String) -> Int?
2110                 """
2111                     .trimIndent()
2112             )
2113         ) {
2114             val functionType = codebase.assertClass("test.pkg.Foo").methods().single().returnType()
2115             functionType.assertClassTypeItem {
2116                 assertHasNullableNullability(expectAnnotation = false)
2117                 val typeArgument = arguments[0]
2118                 typeArgument.assertHasNonNullNullability(expectAnnotation = false)
2119                 val returnType = arguments[1]
2120                 returnType.assertHasNullableNullability(expectAnnotation = false)
2121             }
2122         }
2123     }
2124 
2125     @Test
2126     fun `Test nullability of super class type`() {
2127         runCodebaseTest(
2128             java(
2129                 """
2130                     package test.pkg;
2131                     public class Foo extends Number {}
2132                 """
2133             ),
2134             kotlin(
2135                 """
2136                     package test.pkg
2137                     class Foo: Number {
2138                     }
2139                 """
2140             ),
2141             signature(
2142                 """
2143                     // Signature format: 2.0
2144                     package test.pkg {
2145                       public class Foo extends Number {
2146                       }
2147                     }
2148                 """
2149             ),
2150         ) {
2151             val superClassType = codebase.assertClass("test.pkg.Foo").superClassType()!!
2152             superClassType.assertHasNonNullNullability(expectAnnotation = false)
2153         }
2154     }
2155 
2156     @Test
2157     fun `Test nullability of super interface type`() {
2158         runCodebaseTest(
2159             java(
2160                 """
2161                     package test.pkg;
2162                     import java.util.Map;
2163                     public abstract class Foo implements Map.Entry<String, String> {}
2164                 """
2165             ),
2166             kotlin(
2167                 """
2168                     package test.pkg
2169                     import java.util.Map
2170                     abstract class Foo: Map.Entry<String, String> {
2171                     }
2172                 """
2173             ),
2174             signature(
2175                 """
2176                     // Signature format: 2.0
2177                     package test.pkg {
2178                       public abstract class Foo implements java.util.Map.Entry<java.lang.String, java.lang.String> {
2179                       }
2180                     }
2181                 """
2182             ),
2183         ) {
2184             val superInterfaceType = codebase.assertClass("test.pkg.Foo").interfaceTypes().single()
2185 
2186             // The outer class type must be non-null.
2187             val outerClassType = superInterfaceType.outerClassType!!
2188             outerClassType.assertHasNonNullNullability(expectAnnotation = false)
2189 
2190             // As must the nested class.
2191             superInterfaceType.assertHasNonNullNullability(expectAnnotation = false)
2192         }
2193     }
2194 
2195     @Test
2196     fun `Test nullability of generic super class and interface type`() {
2197         runCodebaseTest(
2198             java(
2199                 """
2200                     package test.pkg;
2201                     import java.util.List;
2202                     public abstract class Foo<E> extends Number implements List<E> {}
2203                 """
2204             ),
2205             kotlin(
2206                 """
2207                     package test.pkg
2208                     import java.util.List
2209                     abstract class Foo<E>: List<E> {
2210                     }
2211                 """
2212             ),
2213             signature(
2214                 """
2215                     // Signature format: 2.0
2216                     package test.pkg {
2217                       public abstract class Foo<E> extends Number implements java.util.List<E> {
2218                       }
2219                     }
2220                 """
2221             ),
2222         ) {
2223             val fooClass = codebase.assertClass("test.pkg.Foo")
2224 
2225             // The super class type must be non-null.
2226             val superClassType = codebase.assertClass("test.pkg.Foo").superClassType()!!
2227             superClassType.assertHasNonNullNullability(expectAnnotation = false)
2228 
2229             // The super interface types must be non-null.
2230             val superInterfaceType = fooClass.interfaceTypes().single()
2231             superInterfaceType.assertHasNonNullNullability(expectAnnotation = false)
2232         }
2233     }
2234 
2235     @Test
2236     fun `Test nullability of class type parameter from constructor`() {
2237         runCodebaseTest(
2238             java(
2239                 """
2240                     package test.pkg;
2241                     public class Foo<F> {
2242                         public class Bar<B> {}
2243                     }
2244                 """
2245                     .trimIndent()
2246             ),
2247             kotlin(
2248                 """
2249                     package test.pkg
2250                     class Foo<F> {
2251                         inner class Bar<B>
2252                     }
2253                 """
2254                     .trimIndent()
2255             ),
2256             signature(
2257                 """
2258                     // Signature format: 5.0
2259                     package test.pkg {
2260                       public class Foo<F> {
2261                         ctor public Foo();
2262                       }
2263                       public class Foo.Bar<B> {
2264                         ctor public Foo.Bar();
2265                       }
2266                     }
2267                 """
2268                     .trimIndent()
2269             ),
2270         ) {
2271             val foo = codebase.assertClass("test.pkg.Foo").constructors().single().returnType()
2272             foo.assertHasNonNullNullability()
2273             val f = foo.arguments.single()
2274             f.assertHasUndefinedNullability()
2275 
2276             val bar = codebase.assertClass("test.pkg.Foo.Bar").constructors().single().returnType()
2277             bar.assertHasNonNullNullability()
2278             val b = bar.arguments.single()
2279             b.assertHasUndefinedNullability()
2280             val outerFoo = bar.outerClassType!!
2281             outerFoo.assertHasNonNullNullability()
2282             val outerF = outerFoo.arguments.single()
2283             outerF.assertHasUndefinedNullability()
2284         }
2285     }
2286 
2287     @Test
2288     fun `Test nullness of unbounded kotlin wildcard`() {
2289         runCodebaseTest(
2290             kotlin(
2291                 """
2292                     package test.pkg
2293                     class Foo {
2294                         fun foo(): List<*>
2295                     }
2296                 """
2297                     .trimIndent()
2298             )
2299         ) {
2300             val fooMethod = codebase.assertClass("test.pkg.Foo").methods().single()
2301             val wildcardType = (fooMethod.returnType() as ClassTypeItem).arguments.single()
2302 
2303             wildcardType.assertHasUndefinedNullability()
2304             wildcardType.assertWildcardItem {
2305                 extendsBound.assertNotNullTypeItem { assertHasNullableNullability() }
2306             }
2307         }
2308     }
2309 }
2310