• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.ArrayTypeItem
20 import com.android.tools.metalava.model.BaseTypeTransformer
21 import com.android.tools.metalava.model.ClassItem
22 import com.android.tools.metalava.model.ClassTypeItem
23 import com.android.tools.metalava.model.MethodItem
24 import com.android.tools.metalava.model.PrimitiveTypeItem
25 import com.android.tools.metalava.model.ReferenceTypeItem
26 import com.android.tools.metalava.model.TypeArgumentTypeItem
27 import com.android.tools.metalava.model.TypeItem
28 import com.android.tools.metalava.model.TypeModifiers
29 import com.android.tools.metalava.model.VariableTypeItem
30 import com.android.tools.metalava.model.WildcardTypeItem
31 import com.android.tools.metalava.model.provider.InputFormat
32 import com.android.tools.metalava.model.testing.testTypeString
33 import com.android.tools.metalava.model.testsuite.BaseModelTest
34 import com.android.tools.metalava.testing.KnownSourceFiles
35 import com.android.tools.metalava.testing.java
36 import com.android.tools.metalava.testing.kotlin
37 import com.google.common.truth.Truth.assertThat
38 import com.google.common.truth.Truth.assertWithMessage
39 import kotlin.test.assertEquals
40 import org.junit.Test
41 
42 class CommonTypeItemTest : BaseModelTest() {
43     @Test
44     fun `Test primitive types`() {
45         runCodebaseTest(
46             java(
47                 """
48                     package test.pkg;
49                     public class Foo {
50                         public void foo(
51                             boolean p0,
52                             byte p1,
53                             char p2,
54                             double p3,
55                             float p4,
56                             int p5,
57                             long p6,
58                             short p7
59                         ) {}
60                     }
61                 """
62             ),
63             kotlin(
64                 """
65                     package test.pkg
66                     class Foo {
67                         fun foo(
68                             p0: Boolean,
69                             p1: Byte,
70                             p2: Char,
71                             p3: Double,
72                             p4: Float,
73                             p5: Int,
74                             p6: Long,
75                             p7: Short
76                         ) = Unit
77                     }
78                 """
79             ),
80             signature(
81                 """
82                     // Signature format: 4.0
83                     package test.pkg {
84                       public class Foo {
85                         ctor public Foo();
86                         method public void foo(boolean, byte, char, double, float, int, long, short);
87                       }
88                     }
89                 """
90                     .trimIndent()
91             )
92         ) {
93             val method = codebase.assertClass("test.pkg.Foo").methods().single()
94 
95             val returnType = method.returnType()
96             assertThat(returnType).isInstanceOf(PrimitiveTypeItem::class.java)
97             assertThat((returnType as PrimitiveTypeItem).kind)
98                 .isEqualTo(PrimitiveTypeItem.Primitive.VOID)
99 
100             val expectedParamTypes =
101                 listOf(
102                     PrimitiveTypeItem.Primitive.BOOLEAN,
103                     PrimitiveTypeItem.Primitive.BYTE,
104                     PrimitiveTypeItem.Primitive.CHAR,
105                     PrimitiveTypeItem.Primitive.DOUBLE,
106                     PrimitiveTypeItem.Primitive.FLOAT,
107                     PrimitiveTypeItem.Primitive.INT,
108                     PrimitiveTypeItem.Primitive.LONG,
109                     PrimitiveTypeItem.Primitive.SHORT
110                 )
111 
112             val params = method.parameters().map { it.type() }
113             assertThat(params).hasSize(expectedParamTypes.size)
114             for ((param, expectedKind) in params.zip(expectedParamTypes)) {
115                 assertThat(param).isInstanceOf(PrimitiveTypeItem::class.java)
116                 assertThat((param as PrimitiveTypeItem).kind).isEqualTo(expectedKind)
117             }
118         }
119     }
120 
121     @Test
122     fun `Test primitive array types`() {
123         runCodebaseTest(
124             java(
125                 """
126                     package test.pkg;
127                     public class Foo {
128                         public void foo(
129                             int[] p0,
130                             char[] p1
131                         ) {}
132                     }
133                 """
134             ),
135             // The Kotlin equivalent can be interpreted with java.lang types instead of primitives
136             signature(
137                 """
138                     // Signature format: 4.0
139                     package test.pkg {
140                       public class Foo {
141                         ctor public Foo();
142                         method public void foo(int[], char[]);
143                       }
144                     }
145                 """
146                     .trimIndent()
147             )
148         ) {
149             val method = codebase.assertClass("test.pkg.Foo").methods().single()
150 
151             val paramTypes = method.parameters().map { it.type() }
152             assertThat(paramTypes).hasSize(2)
153 
154             // int[]
155             val intArray = paramTypes[0]
156             assertThat(intArray).isInstanceOf(ArrayTypeItem::class.java)
157             val int = (intArray as ArrayTypeItem).componentType
158             assertThat(int).isInstanceOf(PrimitiveTypeItem::class.java)
159             assertThat((int as PrimitiveTypeItem).kind).isEqualTo(PrimitiveTypeItem.Primitive.INT)
160             assertThat(intArray.isVarargs).isFalse()
161 
162             // char[]
163             val charArray = paramTypes[1]
164             assertThat(charArray).isInstanceOf(ArrayTypeItem::class.java)
165             val char = (charArray as ArrayTypeItem).componentType
166             assertThat(char).isInstanceOf(PrimitiveTypeItem::class.java)
167             assertThat((char as PrimitiveTypeItem).kind).isEqualTo(PrimitiveTypeItem.Primitive.CHAR)
168             assertThat(charArray.isVarargs).isFalse()
169         }
170     }
171 
172     @Test
173     fun `Test primitive vararg types`() {
174         runCodebaseTest(
175             java(
176                 """
177                     package test.pkg;
178                     public class Foo {
179                         public void foo(int... p0) {}
180                     }
181                 """
182             ),
183             kotlin(
184                 """
185                     package test.pkg
186                     class Foo {
187                         fun foo(vararg p0: Int
188                         ) = Unit
189                     }
190                 """
191             ),
192             signature(
193                 """
194                     // Signature format: 4.0
195                     package test.pkg {
196                       public class Foo {
197                         ctor public Foo();
198                         method public void foo(int...);
199                       }
200                     }
201                 """
202                     .trimIndent()
203             )
204         ) {
205             val method = codebase.assertClass("test.pkg.Foo").methods().single()
206 
207             val paramTypes = method.parameters().map { it.type() }
208             assertThat(paramTypes).hasSize(1)
209 
210             // int... / vararg int
211             val intArray = paramTypes[0]
212             assertThat(intArray).isInstanceOf(ArrayTypeItem::class.java)
213             val int = (intArray as ArrayTypeItem).componentType
214             assertThat(int).isInstanceOf(PrimitiveTypeItem::class.java)
215             assertThat((int as PrimitiveTypeItem).kind).isEqualTo(PrimitiveTypeItem.Primitive.INT)
216             assertThat(intArray.isVarargs).isTrue()
217         }
218     }
219 
220     @Test
221     fun `Test multidimensional primitive array types`() {
222         runCodebaseTest(
223             java(
224                 """
225                     package test.pkg;
226                     public class Foo {
227                         public void foo(
228                             int[][] p0,
229                             char[]... p1
230                         ) {}
231                     }
232                 """
233             ),
234             // The Kotlin equivalent can be interpreted with java.lang types instead of primitives
235             signature(
236                 """
237                     // Signature format: 4.0
238                     package test.pkg {
239                       public class Foo {
240                         ctor public Foo();
241                         method public void foo(int[][], char[]...);
242                       }
243                     }
244                 """
245                     .trimIndent()
246             )
247         ) {
248             val method = codebase.assertClass("test.pkg.Foo").methods().single()
249 
250             val paramTypes = method.parameters().map { it.type() }
251             assertThat(paramTypes).hasSize(2)
252 
253             // int[][]
254             val intArrayArray = paramTypes[0]
255             assertThat(intArrayArray).isInstanceOf(ArrayTypeItem::class.java)
256             assertThat((intArrayArray as ArrayTypeItem).isVarargs).isFalse()
257 
258             val intArray = intArrayArray.componentType
259             assertThat(intArray).isInstanceOf(ArrayTypeItem::class.java)
260             assertThat((intArray as ArrayTypeItem).isVarargs).isFalse()
261 
262             val int = intArray.componentType
263             assertThat(int).isInstanceOf(PrimitiveTypeItem::class.java)
264             assertThat((int as PrimitiveTypeItem).kind).isEqualTo(PrimitiveTypeItem.Primitive.INT)
265 
266             // char[]...
267             val charArrayArray = paramTypes[1]
268             assertThat(charArrayArray).isInstanceOf(ArrayTypeItem::class.java)
269             assertThat((charArrayArray as ArrayTypeItem).isVarargs).isTrue()
270 
271             val charArray = charArrayArray.componentType
272             assertThat(charArray).isInstanceOf(ArrayTypeItem::class.java)
273             assertThat((charArray as ArrayTypeItem).isVarargs).isFalse()
274 
275             val char = charArray.componentType
276             assertThat(char).isInstanceOf(PrimitiveTypeItem::class.java)
277             assertThat((char as PrimitiveTypeItem).kind).isEqualTo(PrimitiveTypeItem.Primitive.CHAR)
278         }
279     }
280 
281     @Test
282     fun `Test class array types`() {
283         runCodebaseTest(
284             java(
285                 """
286                     package test.pkg;
287                     public class Foo {
288                         public void foo(
289                             java.lang.String[] p0,
290                             java.lang.String[][] p1,
291                             java.lang.String... p2
292                         ) {}
293                     }
294                 """
295             ),
296             kotlin(
297                 """
298                     package test.pkg
299                     class Foo {
300                         fun foo(
301                             p0: Array<String>,
302                             p1: Array<Array<String>>,
303                             vararg p2: String
304                         ) = Unit
305                     }
306                 """
307             ),
308             signature(
309                 """
310                     // Signature format: 4.0
311                     package test.pkg {
312                       public class Foo {
313                         ctor public Foo();
314                         method public void foo(String![]!, String![]![]!, java.lang.String!...);
315                       }
316                     }
317                 """
318                     .trimIndent()
319             )
320         ) {
321             val method = codebase.assertClass("test.pkg.Foo").methods().single()
322 
323             val paramTypes = method.parameters().map { it.type() }
324             assertThat(paramTypes).hasSize(3)
325 
326             // String[] / Array<String>
327             val simpleArray = paramTypes[0]
328             assertThat(simpleArray).isInstanceOf(ArrayTypeItem::class.java)
329             assertThat((simpleArray as ArrayTypeItem).componentType.isString()).isTrue()
330             assertThat(simpleArray.isVarargs).isFalse()
331 
332             // String[][] / Array<Array<String>>
333             val twoDimensionalArray = paramTypes[1]
334             assertThat(twoDimensionalArray).isInstanceOf(ArrayTypeItem::class.java)
335             assertThat((twoDimensionalArray as ArrayTypeItem).isVarargs).isFalse()
336             val innerArray = twoDimensionalArray.componentType
337             assertThat(innerArray).isInstanceOf(ArrayTypeItem::class.java)
338             assertThat((innerArray as ArrayTypeItem).componentType.isString()).isTrue()
339             assertThat(innerArray.isVarargs).isFalse()
340 
341             // String... / vararg String
342             val varargs = paramTypes[2]
343             assertThat(twoDimensionalArray).isInstanceOf(ArrayTypeItem::class.java)
344             assertThat((varargs as ArrayTypeItem).componentType.isString()).isTrue()
345             assertThat(varargs.isVarargs).isTrue()
346         }
347     }
348 
349     @Test
350     fun `Test wildcard types`() {
351         runCodebaseTest(
352             java(
353                 """
354                     package test.pkg;
355                     public class Foo<T> {
356                         public void foo(
357                             Foo<?> p0,
358                             Foo<? extends java.lang.String> p1,
359                             Foo<? super java.lang.String> p2,
360                             Foo<? extends java.lang.String[]> p3
361                         ) {}
362                     }
363                 """
364             ),
365             kotlin(
366                 """
367                     package test.pkg
368                     class Foo<T> {
369                         fun foo(
370                             p0: Foo<*>,
371                             p1: Foo<out String>,
372                             p2: Foo<in String>,
373                             p3: Foo<out Array<String>>
374                         ) = Unit
375                     }
376                 """
377             ),
378             signature(
379                 """
380                     // Signature format: 4.0
381                     package test.pkg {
382                       public class Foo<T> {
383                         ctor public Foo();
384                         method public void foo(test.pkg.Foo<?>!, test.pkg.Foo<? extends java.lang.String>!, test.pkg.Foo<? super java.lang.String>!, test.pkg.Foo<? extends java.lang.String[]>!);
385                       }
386                     }
387                 """
388                     .trimIndent()
389             )
390         ) {
391             val method = codebase.assertClass("test.pkg.Foo").methods().single()
392 
393             val wildcardTypes =
394                 method.parameters().map {
395                     val paramType = it.type()
396                     assertThat(paramType).isInstanceOf(ClassTypeItem::class.java)
397                     assertThat((paramType as ClassTypeItem).arguments).hasSize(1)
398                     paramType.arguments.single()
399                 }
400             assertThat(wildcardTypes).hasSize(4)
401 
402             // Foo<?> / Foo<*>
403             // Unbounded wildcards implicitly have an Object extends bound
404             val unboundedWildcard = wildcardTypes[0]
405             assertThat(unboundedWildcard).isInstanceOf(WildcardTypeItem::class.java)
406             val unboundedExtendsBound = (unboundedWildcard as WildcardTypeItem).extendsBound
407             assertThat(unboundedExtendsBound).isNotNull()
408             assertThat(unboundedExtendsBound!!.isJavaLangObject()).isTrue()
409             assertThat(unboundedWildcard.superBound).isNull()
410 
411             // Foo<? extends String> / Foo<out String>
412             val extendsBoundWildcard = wildcardTypes[1]
413             assertThat(extendsBoundWildcard).isInstanceOf(WildcardTypeItem::class.java)
414             val extendsBound = (extendsBoundWildcard as WildcardTypeItem).extendsBound
415             assertThat(extendsBound).isNotNull()
416             assertThat(extendsBound!!.isString()).isTrue()
417             assertThat(extendsBoundWildcard.superBound).isNull()
418 
419             // Foo<? super String> / Foo<in String>
420             // A super bounded wildcard implicitly has an Object extends bound
421             val superBoundWildcard = wildcardTypes[2]
422             assertThat(superBoundWildcard).isInstanceOf(WildcardTypeItem::class.java)
423             val superExtendsBound = (superBoundWildcard as WildcardTypeItem).extendsBound
424             assertThat(superExtendsBound).isNotNull()
425             assertThat(superExtendsBound!!.isJavaLangObject()).isTrue()
426             val superBound = superBoundWildcard.superBound
427             assertThat(superBound).isNotNull()
428             assertThat(superBound!!.isString()).isTrue()
429 
430             // Foo<? extends java.lang.String[]> / Foo<in Array<String>>
431             val arrayExtendsBoundWildcard = wildcardTypes[3]
432             assertThat(arrayExtendsBoundWildcard).isInstanceOf(WildcardTypeItem::class.java)
433             val arrayExtendsBound = (arrayExtendsBoundWildcard as WildcardTypeItem).extendsBound
434             assertThat(arrayExtendsBound).isNotNull()
435             assertThat(arrayExtendsBound).isInstanceOf(ArrayTypeItem::class.java)
436             assertThat((arrayExtendsBound as ArrayTypeItem).componentType.isString()).isTrue()
437         }
438     }
439 
440     @Test
441     fun `Test variable types`() {
442         runCodebaseTest(
443             java(
444                 """
445                     package test.pkg;
446                     public class Foo<C> {
447                         public <M> void foo(C p0, M p1) {}
448                     }
449                 """
450             ),
451             kotlin(
452                 """
453                     package test.pkg
454                     class Foo<C> {
455                         fun <M> foo(p0: C, p1: M) = Unit
456                     }
457                 """
458             ),
459             signature(
460                 """
461                     // Signature format: 4.0
462                     package test.pkg {
463                       public class Foo<C> {
464                         ctor public Foo();
465                         method public <M> void foo(C!, M!);
466                       }
467                     }
468                 """
469                     .trimIndent()
470             )
471         ) {
472             val clz = codebase.assertClass("test.pkg.Foo")
473             val classTypeParam = clz.typeParameterList.single()
474             val method = clz.methods().single()
475             val methodTypeParam = method.typeParameterList.single()
476             val paramTypes = method.parameters().map { it.type() }
477             assertThat(paramTypes).hasSize(2)
478 
479             val classTypeVariable = paramTypes[0]
480             classTypeVariable.assertReferencesTypeParameter(classTypeParam)
481             assertThat((classTypeVariable as VariableTypeItem).name).isEqualTo("C")
482 
483             val methodTypeVariable = paramTypes[1]
484             methodTypeVariable.assertReferencesTypeParameter(methodTypeParam)
485             assertThat((methodTypeVariable as VariableTypeItem).name).isEqualTo("M")
486         }
487     }
488 
489     @Test
490     fun `Test method return type variable types`() {
491         runCodebaseTest(
492             java(
493                 """
494                     package test.pkg;
495                     public class Foo<T> {
496                         public T bar1() {}
497                         public <A extends java.lang.String> A bar2() {}
498                         public <A extends java.lang.String> T bar3() {}
499                         public <T extends java.lang.String> T bar4() {}
500                     }
501                 """
502             ),
503             kotlin(
504                 """
505                     package test.pkg
506                     class Foo<T> {
507                         fun bar1(): T {}
508                         fun <A: java.lang.String> bar2(): A {}
509                         fun <A: java.lang.String> bar3(): T {}
510                         fun <T: java.lang.String> bar4(): T {}
511                     }
512                 """
513             ),
514             signature(
515                 """
516                     // Signature format: 4.0
517                     package test.pkg {
518                       public class Foo<T> {
519                         method public T bar1();
520                         method public <A extends java.lang.String> A bar2();
521                         method public <A extends java.lang.String> T bar3();
522                         method public <T extends java.lang.String> T bar4();
523                       }
524                     }
525                 """
526                     .trimIndent()
527             )
528         ) {
529             val foo = codebase.assertClass("test.pkg.Foo")
530             val fooTypeParam = foo.typeParameterList.single()
531 
532             val bar1 = foo.methods().single { it.name() == "bar1" }
533             val bar1Return = bar1.returnType()
534             bar1Return.assertReferencesTypeParameter(fooTypeParam)
535 
536             val bar2 = foo.methods().single { it.name() == "bar2" }
537             val bar2TypeParam = bar2.typeParameterList.single()
538             val bar2Return = bar2.returnType()
539             bar2Return.assertReferencesTypeParameter(bar2TypeParam)
540 
541             val bar3 = foo.methods().single { it.name() == "bar3" }
542             val bar3Return = bar3.returnType()
543             bar3Return.assertReferencesTypeParameter(fooTypeParam)
544 
545             val bar4 = foo.methods().single { it.name() == "bar4" }
546             val bar4TypeParam = bar4.typeParameterList.single()
547             val bar4Return = bar4.returnType()
548             bar4Return.assertReferencesTypeParameter(bar4TypeParam)
549         }
550     }
551 
552     @Test
553     fun `Test method parameter type variable types`() {
554         runCodebaseTest(
555             java(
556                 """
557                     package test.pkg;
558                     public class Foo<T> {
559                         public void bar1(T p0) {}
560                         public <A extends java.lang.String> void bar2(A p0) {}
561                         public <A extends java.lang.String> void bar3(T p0) {}
562                         public <T extends java.lang.String> void bar4(T p0) {}
563                     }
564                 """
565             ),
566             kotlin(
567                 """
568                     package test.pkg
569                     class Foo<T> {
570                         fun bar1(p0: T) = Unit
571                         fun <A: java.lang.String> bar2(p0: A) = Unit
572                         fun <A: java.lang.String> bar3(p0: T) = Unit
573                         fun <T: java.lang.String> bar4(p0: T) = Unit
574                     }
575                 """
576             ),
577             signature(
578                 """
579                     // Signature format: 4.0
580                     package test.pkg {
581                       public class Foo<T> {
582                         method public void bar1(T p);
583                         method public <A extends java.lang.String> void bar2(A p);
584                         method public <A extends java.lang.String> void bar3(T p);
585                         method public <T extends java.lang.String> void bar4(T p);
586                       }
587                     }
588                 """
589                     .trimIndent()
590             )
591         ) {
592             val foo = codebase.assertClass("test.pkg.Foo")
593             val fooParam = foo.typeParameterList.single()
594 
595             val bar1 = foo.methods().single { it.name() == "bar1" }
596             val bar1Param = bar1.parameters().single().type()
597             bar1Param.assertReferencesTypeParameter(fooParam)
598 
599             val bar2 = foo.methods().single { it.name() == "bar2" }
600             val bar2TypeParam = bar2.typeParameterList.single()
601             val bar2Param = bar2.parameters().single().type()
602             bar2Param.assertReferencesTypeParameter(bar2TypeParam)
603 
604             val bar3 = foo.methods().single { it.name() == "bar3" }
605             val bar3Param = bar3.parameters().single().type()
606             bar3Param.assertReferencesTypeParameter(fooParam)
607 
608             val bar4 = foo.methods().single { it.name() == "bar4" }
609             val bar4TypeParam = bar4.typeParameterList.single()
610             val bar4Param = bar4.parameters().single().type()
611             bar4Param.assertReferencesTypeParameter(bar4TypeParam)
612         }
613     }
614 
615     @Test
616     fun `Test field type variable types`() {
617         runCodebaseTest(
618             java(
619                 """
620                     package test.pkg;
621                     public class Foo<T> {
622                         public T foo;
623                     }
624                 """
625                     .trimIndent()
626             ),
627             kotlin(
628                 """
629                     package test.pkg
630                     class Foo<T> {
631                         @JvmField val foo: T
632                     }
633                 """
634             ),
635             signature(
636                 """
637                     // Signature format: 4.0
638                     package test.pkg {
639                       public class Foo<T> {
640                         field public T foo;
641                       }
642                     }
643                 """
644                     .trimIndent()
645             )
646         ) {
647             val foo = codebase.assertClass("test.pkg.Foo")
648             val fooParam = foo.typeParameterList.single()
649 
650             val fieldType = foo.fields().single { it.name() == "foo" }.type()
651             fieldType.assertReferencesTypeParameter(fooParam)
652         }
653     }
654 
655     @Test
656     fun `Test property type variable types`() {
657         runCodebaseTest(
658             // No java equivalent
659             kotlin(
660                 """
661                     package test.pkg
662                     class Foo<T> {
663                         val foo: T
664                     }
665                 """
666                     .trimIndent()
667             ),
668             signature(
669                 """
670                     // Signature format: 4.0
671                     package test.pkg {
672                       public class Foo<T> {
673                         property public T foo;
674                       }
675                     }
676                 """
677                     .trimIndent()
678             )
679         ) {
680             val foo = codebase.assertClass("test.pkg.Foo")
681             val fooParam = foo.typeParameterList.single()
682 
683             val propertyType = foo.properties().single { it.name() == "foo" }.type()
684             propertyType.assertReferencesTypeParameter(fooParam)
685         }
686     }
687 
688     @Test
689     fun `Test class types`() {
690         runCodebaseTest(
691             java(
692                 """
693                     package test.pkg;
694                     public class Foo {
695                         public <T> void foo(
696                             java.lang.String p0,
697                             java.util.List<java.lang.String> p1,
698                             java.util.List<java.lang.String[]> p2,
699                             java.util.Map<java.lang.String, Foo> p3
700                         ) {}
701                     }
702                 """
703             ),
704             kotlin(
705                 """
706                     package test.pkg
707                     class Foo {
708                         fun <T> foo(
709                             p0: String,
710                             p1: List<String>,
711                             p2: List<Array<String>>,
712                             p3: Map<String, Foo>
713                         ) = Unit
714                     }
715                 """
716             ),
717             signature(
718                 """
719                     // Signature format: 4.0
720                     package test.pkg {
721                       public class Foo {
722                         ctor public Foo();
723                         method public <T> void foo(String!, java.util.List<java.lang.String!>!, java.util.List<java.lang.String![]!>!, java.util.Map<java.lang.String!,test.pkg.Foo!>!);
724                       }
725                     }
726                 """
727                     .trimIndent()
728             )
729         ) {
730             val method = codebase.assertClass("test.pkg.Foo").methods().single()
731             val paramTypes = method.parameters().map { it.type() }
732 
733             val stringType = paramTypes[0]
734             assertThat(stringType).isInstanceOf(ClassTypeItem::class.java)
735             assertThat((stringType as ClassTypeItem).qualifiedName).isEqualTo("java.lang.String")
736             assertThat(stringType.className).isEqualTo("String")
737             assertThat(stringType.arguments).isEmpty()
738 
739             // List<String>
740             val stringListType = paramTypes[1]
741             assertThat(stringListType).isInstanceOf(ClassTypeItem::class.java)
742             assertThat((stringListType as ClassTypeItem).qualifiedName).isEqualTo("java.util.List")
743             assertThat(stringListType.className).isEqualTo("List")
744             assertThat(stringListType.arguments).hasSize(1)
745             assertThat(stringListType.arguments.single().isString()).isTrue()
746 
747             // List<String[]> / List<Array<String>>
748             val arrayListType = paramTypes[2]
749             assertThat(arrayListType).isInstanceOf(ClassTypeItem::class.java)
750             assertThat((arrayListType as ClassTypeItem).qualifiedName).isEqualTo("java.util.List")
751             assertThat(arrayListType.arguments).hasSize(1)
752             val arrayType = arrayListType.arguments.single()
753             assertThat(arrayType).isInstanceOf(ArrayTypeItem::class.java)
754             assertThat((arrayType as ArrayTypeItem).componentType.isString()).isTrue()
755 
756             // Map<String, Foo>
757             val mapType = paramTypes[3]
758             assertThat(mapType).isInstanceOf(ClassTypeItem::class.java)
759             assertThat((mapType as ClassTypeItem).qualifiedName).isEqualTo("java.util.Map")
760             assertThat(mapType.arguments).hasSize(2)
761             val mapKeyType = mapType.arguments.first()
762             assertThat(mapKeyType).isInstanceOf(ClassTypeItem::class.java)
763             assertThat((mapKeyType as ClassTypeItem).isString()).isTrue()
764             val mapValueType = mapType.arguments.last()
765             assertThat(mapValueType).isInstanceOf(ClassTypeItem::class.java)
766             assertThat((mapValueType as ClassTypeItem).qualifiedName).isEqualTo("test.pkg.Foo")
767         }
768     }
769 
770     @Test
771     fun `Test inner types`() {
772         runCodebaseTest(
773             java(
774                 """
775                     package test.pkg;
776                     public class Outer {
777                         public class Middle {
778                             public class Inner {}
779                         }
780 
781                         public Outer.Middle.Inner foo() {
782                             return new Outer.Middle.Inner();
783                         }
784                     }
785                 """
786             ),
787             kotlin(
788                 """
789                     package test.pkg
790                     class Outer {
791                         inner class Middle {
792                             inner class Inner
793                         }
794 
795                         fun foo(): Outer.Middle.Inner {
796                             return Outer.Middle.Inner()
797                         }
798                     }
799                 """
800             ),
801             signature(
802                 """
803                     // Signature format: 4.0
804                     package test.pkg {
805                       public class Outer {
806                         ctor public Outer();
807                         method public test.pkg.Outer.Middle.Inner foo();
808                       }
809                       public class Outer.Middle {
810                         ctor public Outer.Middle();
811                       }
812                       public class Outer.Middle.Inner {
813                         ctor public Outer.Middle.Inner();
814                       }
815                     }
816                 """
817                     .trimIndent()
818             )
819         ) {
820             val method = codebase.assertClass("test.pkg.Outer").methods().single()
821 
822             // Outer.Middle.Inner
823             val innerType = method.returnType()
824             assertThat(innerType).isInstanceOf(ClassTypeItem::class.java)
825             assertThat((innerType as ClassTypeItem).qualifiedName)
826                 .isEqualTo("test.pkg.Outer.Middle.Inner")
827             assertThat(innerType.className).isEqualTo("Inner")
828             assertThat(innerType.arguments).isEmpty()
829 
830             val middleType = innerType.outerClassType
831             assertThat(middleType).isNotNull()
832             assertThat(middleType!!.qualifiedName).isEqualTo("test.pkg.Outer.Middle")
833             assertThat(middleType.className).isEqualTo("Middle")
834             assertThat(middleType.arguments).isEmpty()
835 
836             val outerType = middleType.outerClassType
837             assertThat(outerType).isNotNull()
838             assertThat(outerType!!.qualifiedName).isEqualTo("test.pkg.Outer")
839             assertThat(outerType.className).isEqualTo("Outer")
840             assertThat(outerType.arguments).isEmpty()
841             assertThat(outerType.outerClassType).isNull()
842         }
843     }
844 
845     @Test
846     fun `Test inner types from classpath`() {
847         runCodebaseTest(
848             java(
849                 """
850                     package test.pkg;
851 
852                     import java.util.Map;
853 
854                     public class Test {
855                         public Map.Entry<String,String> foo() {
856                             return new Map.Entry<String,String>();
857                         }
858                     }
859                 """
860             ),
861             kotlin(
862                 """
863                     package test.pkg
864 
865                     import java.util.Map
866 
867                     class Test {
868                         fun foo(): Map.Entry<String,String> {
869                             return Map.Entry<String,String>()
870                         }
871                     }
872                 """
873             ),
874             signature(
875                 """
876                     // Signature format: 4.0
877                     package test.pkg {
878                       public class Test {
879                         ctor public Outer();
880                         method public java.util.Map.Entry<java.lang.String,java.lang.String> foo();
881                       }
882                     }
883                 """
884                     .trimIndent()
885             )
886         ) {
887             val method = codebase.assertClass("test.pkg.Test").methods().single()
888 
889             // Map.Entry<String,String>
890             val innerType = method.returnType()
891             assertThat(innerType).isInstanceOf(ClassTypeItem::class.java)
892             assertThat((innerType as ClassTypeItem).qualifiedName).isEqualTo("java.util.Map.Entry")
893             assertThat(innerType.className).isEqualTo("Entry")
894 
895             val outerType = innerType.outerClassType
896             assertThat(outerType).isNotNull()
897             assertThat(outerType!!.qualifiedName).isEqualTo("java.util.Map")
898             assertThat(outerType.className).isEqualTo("Map")
899             assertThat(outerType.arguments).hasSize(0)
900             assertThat(outerType.outerClassType).isNull()
901         }
902     }
903 
904     @Test
905     fun `Test inner parameterized types`() {
906         runCodebaseTest(
907             java(
908                 """
909                     package test.pkg;
910                     public class Outer<O> {
911                         public class Inner<I> {
912                         }
913 
914                         public <P1, P2> Outer<P1>.Inner<P2> foo() {
915                             return new Outer<P1>.Inner<P2>();
916                         }
917                     }
918                 """
919             ),
920             kotlin(
921                 """
922                     package test.pkg
923                     class Outer<O> {
924                         inner class Inner<I>
925 
926                         fun <P1, P2> foo(): Outer<P1>.Inner<P2> {
927                             return Outer<P1>.Inner<P2>()
928                         }
929                     }
930                 """
931             ),
932             signature(
933                 """
934                     // Signature format: 4.0
935                     package test.pkg {
936                       public class Outer<O> {
937                         ctor public Outer();
938                         method public <P1, P2> test.pkg.Outer<P1!>.Inner<P2!>! foo();
939                       }
940                       public class Outer.Inner<I> {
941                         ctor public Outer.Inner();
942                       }
943                     }
944                 """
945                     .trimIndent()
946             )
947         ) {
948             val method = codebase.assertClass("test.pkg.Outer").methods().single()
949             val methodTypeParameters = method.typeParameterList
950             assertThat(methodTypeParameters).hasSize(2)
951             val p1 = methodTypeParameters[0]
952             val p2 = methodTypeParameters[1]
953 
954             // Outer<P1>.Inner<P2>
955             val innerType = method.returnType()
956             assertThat(innerType).isInstanceOf(ClassTypeItem::class.java)
957             assertThat((innerType as ClassTypeItem).qualifiedName).isEqualTo("test.pkg.Outer.Inner")
958             assertThat(innerType.className).isEqualTo("Inner")
959             assertThat(innerType.arguments).hasSize(1)
960             val innerTypeArgument = innerType.arguments.single()
961             innerTypeArgument.assertReferencesTypeParameter(p2)
962             assertThat((innerTypeArgument as VariableTypeItem).name).isEqualTo("P2")
963 
964             val outerType = innerType.outerClassType
965             assertThat(outerType).isNotNull()
966             assertThat(outerType!!.qualifiedName).isEqualTo("test.pkg.Outer")
967             assertThat(outerType.className).isEqualTo("Outer")
968             assertThat(outerType.outerClassType).isNull()
969             assertThat(outerType.arguments).hasSize(1)
970             val outerClassTypeArgument = outerType.arguments.single()
971             outerClassTypeArgument.assertReferencesTypeParameter(p1)
972             assertThat((outerClassTypeArgument as VariableTypeItem).name).isEqualTo("P1")
973         }
974     }
975 
976     @Test
977     fun `Test inner parameterized types without explicit outer type`() {
978         runCodebaseTest(
979             inputSet(
980                 java(
981                     """
982                         package test.pkg;
983 
984                         import test.pkg1.Outer.Middle.Inner;
985 
986                         public class Test {
987                             public Inner<String> foo() {
988                                 return new Inner<String>();
989                             }
990                         }
991                     """
992                 ),
993                 java(
994                     """
995                         package test.pkg1;
996 
997                         public class Outer<O> {
998                             public class Middle {
999                                 public class Inner<I> {}
1000                             }
1001                         }
1002                     """
1003                 ),
1004             ),
1005             inputSet(
1006                 signature(
1007                     "api1.txt",
1008                     """
1009                         // Signature format: 4.0
1010                         package test.pkg1 {
1011                           public class Outer<O> {
1012                             ctor public Outer();
1013                           }
1014                           public class Outer.Middle {
1015                             ctor public Outer.Middle();
1016                           }
1017                           public class Outer.Middle.Inner<I> {
1018                             ctor public Outer.Middle.Inner();
1019                           }
1020                         }
1021                     """
1022                 ),
1023                 signature(
1024                     "api2.txt",
1025                     """
1026                         // Signature format: 4.0
1027                         package test.pkg {
1028                           public class Test {
1029                             ctor public Test();
1030                             method public test.pkg1.Outer.Middle.Inner<String> foo();
1031                           }
1032                         }
1033                     """
1034                 )
1035             )
1036         ) {
1037             val method = codebase.assertClass("test.pkg.Test").methods().single()
1038 
1039             val innerType = method.returnType()
1040             assertThat(innerType).isInstanceOf(ClassTypeItem::class.java)
1041             assertThat((innerType as ClassTypeItem).qualifiedName)
1042                 .isEqualTo("test.pkg1.Outer.Middle.Inner")
1043             assertThat(innerType.className).isEqualTo("Inner")
1044             assertThat(innerType.arguments).hasSize(1)
1045 
1046             val middleType = innerType.outerClassType
1047             assertThat(middleType).isNotNull()
1048             assertThat(middleType!!.qualifiedName).isEqualTo("test.pkg1.Outer.Middle")
1049             assertThat(middleType.className).isEqualTo("Middle")
1050             assertThat(middleType.arguments).hasSize(0)
1051 
1052             val outerType = middleType.outerClassType
1053             assertThat(outerType).isNotNull()
1054             assertThat(outerType!!.qualifiedName).isEqualTo("test.pkg1.Outer")
1055             assertThat(outerType.className).isEqualTo("Outer")
1056             assertThat(outerType.outerClassType).isNull()
1057             assertThat(outerType.arguments).hasSize(0)
1058         }
1059     }
1060 
1061     @Test
1062     fun `Test superclass and interface types using type variables`() {
1063         runCodebaseTest(
1064             java(
1065                 """
1066                     package test.pkg;
1067 
1068                     public class Cache<Query, Result> extends java.util.HashMap<Query,Result> {}
1069 
1070                     public class MyList<E> implements java.util.List<E> {}
1071                 """
1072                     .trimIndent()
1073             ),
1074             kotlin(
1075                 """
1076                     package test.pkg
1077 
1078                     class Cache<Query, Result> : java.util.HashMap<Query, Result>
1079 
1080                     class MyList<E> : java.util.List<E>
1081                 """
1082                     .trimIndent()
1083             ),
1084             signature(
1085                 """
1086                     // Signature format: 2.0
1087                     package test.pkg {
1088                       public class Cache<Query, Result> extends java.util.HashMap<Query,Result> {
1089                       }
1090                       public class MyList<E> implements java.util.List<E> {
1091                       }
1092                     }
1093                 """
1094                     .trimIndent()
1095             )
1096         ) {
1097             // Verify that the Cache superclass type uses the Cache type variables
1098             val cache = codebase.assertClass("test.pkg.Cache")
1099             val cacheTypeParams = cache.typeParameterList
1100             assertThat(cacheTypeParams).hasSize(2)
1101             val queryParam = cacheTypeParams[0]
1102             val resultParam = cacheTypeParams[1]
1103 
1104             val cacheSuperclassType = cache.superClassType()
1105             assertThat(cacheSuperclassType).isInstanceOf(ClassTypeItem::class.java)
1106             assertThat((cacheSuperclassType as ClassTypeItem).qualifiedName)
1107                 .isEqualTo("java.util.HashMap")
1108             assertThat(cacheSuperclassType.arguments).hasSize(2)
1109 
1110             val queryVar = cacheSuperclassType.arguments[0]
1111             queryVar.assertReferencesTypeParameter(queryParam)
1112 
1113             val resultVar = cacheSuperclassType.arguments[1]
1114             resultVar.assertReferencesTypeParameter(resultParam)
1115 
1116             // Verify that the MyList interface type uses the MyList type variable
1117             val myList = codebase.assertClass("test.pkg.MyList")
1118             val myListTypeParams = myList.typeParameterList
1119             assertThat(myListTypeParams).hasSize(1)
1120             val eParam = myListTypeParams.single()
1121 
1122             val myListInterfaces = myList.interfaceTypes()
1123             assertThat(myListInterfaces).hasSize(1)
1124 
1125             val myListInterfaceType = myListInterfaces.single()
1126             assertThat(myListInterfaceType).isInstanceOf(ClassTypeItem::class.java)
1127             assertThat(myListInterfaceType.qualifiedName).isEqualTo("java.util.List")
1128             assertThat(myListInterfaceType.arguments).hasSize(1)
1129 
1130             val eVar = myListInterfaceType.arguments.single()
1131             eVar.assertReferencesTypeParameter(eParam)
1132         }
1133     }
1134 
1135     @Test
1136     fun `Test array of type with parameter used as type parameter`() {
1137         runCodebaseTest(
1138             signature(
1139                 """
1140                     // Signature format: 2.0
1141                     package test.pkg {
1142                       public class Foo {
1143                         method public java.util.Collection<java.util.List<java.lang.String>[]> foo();
1144                       }
1145                     }
1146                 """
1147                     .trimIndent()
1148             ),
1149             java(
1150                 """
1151                     package test.pkg;
1152 
1153                     import java.util.Collection;
1154                     import java.util.List;
1155 
1156                     public class Foo {
1157                         public Collection<List<String>[]> foo() {}
1158                     }
1159                 """,
1160             ),
1161             kotlin(
1162                 """
1163                     package test.pkg
1164 
1165                     class Foo {
1166                         fun foo(): Collection<Array<List<String>>> {}
1167                     }
1168                 """
1169             )
1170         ) {
1171             val method = codebase.assertClass("test.pkg.Foo").methods().single()
1172 
1173             // java.util.Collection<java.util.List<java.lang.String>[]>
1174             val collectionOfArrayOfStringList = method.returnType()
1175             assertThat(collectionOfArrayOfStringList).isInstanceOf(ClassTypeItem::class.java)
1176             assertThat((collectionOfArrayOfStringList as ClassTypeItem).qualifiedName)
1177                 .isEqualTo("java.util.Collection")
1178             assertThat(collectionOfArrayOfStringList.arguments).hasSize(1)
1179 
1180             // java.util.List<java.lang.String>[]
1181             val arrayOfStringList = collectionOfArrayOfStringList.arguments.single()
1182             assertThat(arrayOfStringList).isInstanceOf(ArrayTypeItem::class.java)
1183 
1184             // java.util.List<java.lang.String>
1185             val stringList = (arrayOfStringList as ArrayTypeItem).componentType
1186             assertThat(stringList).isInstanceOf(ClassTypeItem::class.java)
1187             assertThat((stringList as ClassTypeItem).qualifiedName).isEqualTo("java.util.List")
1188             assertThat(stringList.arguments).hasSize(1)
1189 
1190             // java.lang.String
1191             val string = stringList.arguments.single()
1192             assertThat(string.isString()).isTrue()
1193         }
1194     }
1195 
1196     @Test
1197     fun `Test Kotlin collection removeAll parameter type`() {
1198         runCodebaseTest(
1199             kotlin(
1200                 """
1201                     package test.pkg
1202                     abstract class Foo<Z> : MutableCollection<Z> {
1203                         override fun addAll(elements: Collection<Z>): Boolean = true
1204                         override fun containsAll(elements: Collection<Z>): Boolean = true
1205                         override fun removeAll(elements: Collection<Z>): Boolean = true
1206                         override fun retainAll(elements: Collection<Z>): Boolean = true
1207                     }
1208                 """
1209             )
1210         ) {
1211             val fooClass = codebase.assertClass("test.pkg.Foo")
1212             val typeParam = fooClass.typeParameterList.single()
1213 
1214             /**
1215              * Make sure that the [ClassItem] has a method whose single parameter is of the type
1216              * `java.lang.Collection` and then run [body] on that type.
1217              */
1218             fun ClassItem.assertMethodTakesCollection(
1219                 name: String,
1220                 body: TypeArgumentTypeItem.() -> Unit
1221             ) {
1222                 val method = methods().single { it.name() == name }
1223                 val paramType = method.parameters().single().type()
1224                 paramType.assertClassTypeItem {
1225                     assertThat(qualifiedName).isEqualTo("java.util.Collection")
1226                     assertThat(arguments).hasSize(1)
1227                     val argument = arguments.single()
1228                     argument.body()
1229                 }
1230             }
1231 
1232             /**
1233              * Make sure that the [ClassItem] has a method whose single parameter is of the type
1234              * `java.lang.Collection<? extends Z>`.
1235              */
1236             fun ClassItem.assertMethodTakesCollectionWildcardExtendsZ(name: String) {
1237                 assertMethodTakesCollection(name) {
1238                     assertWildcardItem { extendsBound!!.assertReferencesTypeParameter(typeParam) }
1239                 }
1240             }
1241 
1242             // Defined in `java.util.Collection` as `addAll(Collection<? extends E> c)`. The type of
1243             // the `addAll` method in `Foo` should be `addAll(Collection<? extends Z>)`.Where `Z`
1244             // references the type parameter in `Foo<Z>`.
1245             fooClass.assertMethodTakesCollectionWildcardExtendsZ("addAll")
1246 
1247             // Defined in `java.util.Collection` as `...(Collection<?> c)` these methods should be
1248             // `...(Collection<? extends Z>)`.Where `Z` references the type parameter in
1249             // `Foo<Z>`.
1250             //
1251             fooClass.assertMethodTakesCollectionWildcardExtendsZ("containsAll")
1252             fooClass.assertMethodTakesCollectionWildcardExtendsZ("removeAll")
1253             fooClass.assertMethodTakesCollectionWildcardExtendsZ("retainAll")
1254         }
1255     }
1256 
1257     @Test
1258     fun `Test convertType`() {
1259         runCodebaseTest(
1260             inputSet(
1261                 java(
1262                     """
1263                         package test.pkg;
1264                         import java.util.List;
1265                         import java.util.Map;
1266                         public class Parent<M, N> {
1267                             public M getM() {}
1268                             public N[] getNArray() {}
1269                             public List<M> getMList() {}
1270                             public Map<M, N> getMap() {}
1271                             public Parent<? extends M, ? super N> getWildcards() {}
1272                         }
1273                     """
1274                         .trimIndent()
1275                 ),
1276                 java(
1277                     """
1278                         package test.pkg;
1279                         public class Child<X, Y> extends Parent<X, Y> {}
1280                     """
1281                         .trimIndent()
1282                 ),
1283             ),
1284             inputSet(
1285                 signature(
1286                     """
1287                         // Signature format: 5.0
1288                         package test.pkg {
1289                           public class Child<X, Y> extends test.pkg.Parent<X,Y> {
1290                           }
1291                           public class Parent<M, N> {
1292                             method public M getM();
1293                             method public java.util.List<M> getMList();
1294                             method public java.util.Map<M,N> getMap();
1295                             method public N[] getNArray();
1296                             method public test.pkg.Parent<? extends M, ? super N> getWildcards();
1297                           }
1298                         }
1299                     """
1300                         .trimIndent()
1301                 )
1302             ),
1303             inputSet(
1304                 kotlin(
1305                     """
1306                         package test.pkg
1307                         open class Parent<M, N> {
1308                             fun getM(): M {}
1309                             fun getNArray(): Array<N> {}
1310                             fun getMList(): List<M> {}
1311                             fun getMap(): Map<M, N> {}
1312                             fun getWildcards(): Parent<out M, in N> {}
1313                         }
1314                         class Child<X, Y> : Parent<X, Y>()
1315                     """
1316                         .trimIndent()
1317                 )
1318             )
1319         ) {
1320             val parent = codebase.assertClass("test.pkg.Parent")
1321             val child = codebase.assertClass("test.pkg.Child")
1322             val childTypeParams = child.typeParameterList
1323             val x = childTypeParams[0]
1324             val y = childTypeParams[1]
1325 
1326             val mVar = parent.assertMethod("getM", "").returnType()
1327             val xVar = mVar.convertType(child, parent)
1328             assertThat(xVar.toTypeString()).isEqualTo("X")
1329             xVar.assertReferencesTypeParameter(x)
1330 
1331             val nArray = parent.assertMethod("getNArray", "").returnType()
1332             val yArray = nArray.convertType(child, parent)
1333             assertThat(yArray.toTypeString()).isEqualTo("Y[]")
1334             assertThat((yArray as ArrayTypeItem).isVarargs).isFalse()
1335             yArray.componentType.assertReferencesTypeParameter(y)
1336 
1337             val mList = parent.assertMethod("getMList", "").returnType()
1338             val xList = mList.convertType(child, parent)
1339             assertThat(xList.toTypeString()).isEqualTo("java.util.List<X>")
1340             assertThat((xList as ClassTypeItem).qualifiedName).isEqualTo("java.util.List")
1341             xList.arguments.single().assertReferencesTypeParameter(x)
1342 
1343             val mToNMap = parent.assertMethod("getMap", "").returnType()
1344             val xToYMap = mToNMap.convertType(child, parent)
1345             assertThat(xToYMap.toTypeString()).isEqualTo("java.util.Map<X,Y>")
1346             assertThat((xToYMap as ClassTypeItem).qualifiedName).isEqualTo("java.util.Map")
1347             xToYMap.arguments[0].assertReferencesTypeParameter(x)
1348             xToYMap.arguments[1].assertReferencesTypeParameter(y)
1349 
1350             val wildcards = parent.assertMethod("getWildcards", "").returnType()
1351             val convertedWildcards = wildcards.convertType(child, parent)
1352             assertThat(convertedWildcards.toTypeString())
1353                 .isEqualTo("test.pkg.Parent<? extends X,? super Y>")
1354             assertThat((convertedWildcards as ClassTypeItem).qualifiedName)
1355                 .isEqualTo("test.pkg.Parent")
1356             assertThat(convertedWildcards.arguments).hasSize(2)
1357 
1358             val extendsX = convertedWildcards.arguments[0] as WildcardTypeItem
1359             extendsX.extendsBound!!.assertReferencesTypeParameter(x)
1360             val superN = convertedWildcards.arguments[1] as WildcardTypeItem
1361             superN.superBound!!.assertReferencesTypeParameter(y)
1362         }
1363     }
1364 
1365     @Test
1366     fun `Test convertType with maps`() {
1367         runCodebaseTest(
1368             java(
1369                 """
1370                     package test.pkg;
1371                     import java.util.List;
1372                     public class Foo<T, X> {
1373                       public Number numberType;
1374 
1375                       public int primitiveType;
1376                       public int primitiveTypeAfterMatchingConversion;
1377 
1378                       public T variableType;
1379                       public Number variableTypeAfterMatchingConversion;
1380 
1381                       public T[] arrayType;
1382                       public Number[] arrayTypeAfterMatchingConversion;
1383 
1384                       public Foo<T, String> classType;
1385                       public Foo<Number, String> classTypeAfterMatchingConversion;
1386 
1387                       public Foo<? extends T, String> wildcardExtendsType;
1388                       public Foo<? extends Number, String> wildcardExtendsTypeAfterMatchingConversion;
1389 
1390                       public Foo<? super T, String> wildcardSuperType;
1391                       public Foo<? super Number, String> wildcardSuperTypeAfterMatchingConversion;
1392                     }
1393                 """
1394                     .trimIndent()
1395             ),
1396             kotlin(
1397                 """
1398                     package test.pkg
1399                     class Foo<T, X> {
1400                         @JvmField val numberType: Number
1401 
1402                         @JvmField val primitiveType: Int
1403                         @JvmField val primitiveTypeAfterMatchingConversion: Int
1404 
1405                         @JvmField val variableType: T
1406                         @JvmField val variableTypeAfterMatchingConversion: Number
1407 
1408                         @JvmField val arrayType: Array<T>
1409                         @JvmField val arrayTypeAfterMatchingConversion: Array<Number>
1410 
1411                         @JvmField val classType: Foo<T, String>
1412                         @JvmField val classTypeAfterMatchingConversion: Foo<Number, String>
1413 
1414                         @JvmField val wildcardExtendsType: Foo<out T, String>
1415                         @JvmField val wildcardExtendsTypeAfterMatchingConversion: Foo<out Number, String>
1416 
1417                         @JvmField val wildcardSuperType: Foo<in T, String>
1418                         @JvmField val wildcardSuperTypeAfterMatchingConversion: Foo<in Number, String>
1419                     }
1420                 """
1421                     .trimIndent()
1422             ),
1423             signature(
1424                 """
1425                     // Signature format: 5.0
1426                     package test.pkg {
1427                       public class Foo<T, X> {
1428                         field public Number numberType;
1429 
1430                         field public int primitiveType;
1431                         field public int primitiveTypeAfterMatchingConversion;
1432 
1433                         field public T variableType;
1434                         field public Number variableTypeAfterMatchingConversion;
1435 
1436                         field public T[] arrayType;
1437                         field public Number[] arrayTypeAfterMatchingConversion;
1438 
1439                         field public test.pkg.Foo<T, String> classType;
1440                         field public test.pkg.Foo<Number, String> classTypeAfterMatchingConversion;
1441 
1442                         field public test.pkg.Foo<? extends T, String> wildcardExtendsType;
1443                         field public test.pkg.Foo<? extends Number, String> wildcardExtendsTypeAfterMatchingConversion;
1444 
1445                         field public test.pkg.Foo<? super T, String> wildcardSuperType;
1446                         field public test.pkg.Foo<? super Number, String> wildcardSuperTypeAfterMatchingConversion;
1447                       }
1448                     }
1449                 """
1450                     .trimIndent()
1451             )
1452         ) {
1453             val fooClass = codebase.assertClass("test.pkg.Foo")
1454             val t = fooClass.typeParameterList.single { it.name() == "T" }
1455             val x = fooClass.typeParameterList.single { it.name() == "X" }
1456             val numberType = fooClass.assertField("numberType").type() as ReferenceTypeItem
1457 
1458             val matchingBindings = mapOf(t to numberType)
1459             val nonMatchingBindings = mapOf(x to numberType)
1460 
1461             val afterMatchingConversionSuffix = "AfterMatchingConversion"
1462             val fieldsToCheck =
1463                 fooClass.fields().filter {
1464                     it.name() != "numberType" && !it.name().endsWith(afterMatchingConversionSuffix)
1465                 }
1466 
1467             for (fieldItem in fieldsToCheck) {
1468                 val fieldType = fieldItem.type()
1469 
1470                 val fieldName = fieldItem.name()
1471                 val expectedMatchedFieldType =
1472                     fooClass.assertField(fieldName + afterMatchingConversionSuffix).type()
1473 
1474                 assertWithMessage("conversion that matches $fieldName")
1475                     .that(fieldType.convertType(matchingBindings))
1476                     .isEqualTo(expectedMatchedFieldType)
1477 
1478                 // Expect no change if it does not match.
1479                 assertWithMessage("conversion that does not match $fieldName")
1480                     .that(fieldType.convertType(nonMatchingBindings))
1481                     .isEqualTo(fieldType)
1482             }
1483         }
1484     }
1485 
1486     @Test
1487     fun `Test convertType's creation of duplicate objects`() {
1488         runCodebaseTest(
1489             inputSet(
1490                 signature(
1491                     """
1492                         // Signature format: 2.0
1493                         package test.pkg {
1494                           public interface Input<T,Unused> {
1495                             // Field from which the type to be substituted for a type variable will
1496                             // be retrieved.
1497                             field public @NonNull Long javaLongType;
1498 
1499                             // One for each TypeItem subinterface supported in signature files.
1500                             method public @NonNull T @Nullable [] arrayTypeItem();
1501                             method public @Nullable java.util.List<@NonNull T> classTypeItem();
1502                             method public int primitiveTypeItem();
1503                             method public @Nullable T variableTypeItem();
1504                             method public @Nullable java.util.List<? extends @NonNull T> wildcardTypeItem_extendsBound();
1505                             method public @Nullable java.util.List<? super @NonNull T> wildcardTypeItem_superBound();
1506                           }
1507                         }
1508                     """
1509                 ),
1510             ),
1511             inputSet(
1512                 KnownSourceFiles.nonNullSource,
1513                 KnownSourceFiles.nullableSource,
1514                 java(
1515                     """
1516                         package test.pkg;
1517                         import android.annotation.NonNull;
1518                         import android.annotation.Nullable;
1519                         import java.util.List;
1520                         public interface Input<T,Unused> {
1521                             // Field from which the type to be substituted for a type variable will
1522                             // be retrieved.
1523                             @NonNull Long javaLongType;
1524 
1525                             // One for each TypeItem subinterface supported in signature files.
1526                             @NonNull T @Nullable [] arrayTypeItem();
1527                             @Nullable List<@NonNull T> classTypeItem();
1528                             int primitiveTypeItem();
1529                             @Nullable T variableTypeItem();
1530                             @Nullable List<? extends @NonNull T> wildcardTypeItem_extendsBound();
1531                             @Nullable List<? super @NonNull T> wildcardTypeItem_superBound();
1532                         }
1533                     """
1534                 ),
1535             ),
1536             inputSet(
1537                 kotlin(
1538                     """
1539                         package test.pkg
1540                         import java.util.List
1541                         interface Input<T,Unused> {
1542                             // Field from which the type to be substituted for a type variable will
1543                             // be retrieved.
1544                             companion object {
1545                                 @JvmField val javaLongType: java.lang.Long
1546                             }
1547 
1548                             // One for each TypeItem subinterface supported in signature files.
1549                             fun arrayTypeItem(): Array<T>?
1550                             fun classTypeItem(): List<T>?
1551                             fun lambdaTypeItem(): ((T) -> Int)?
1552                             fun primitiveTypeItem(): Int
1553                             fun variableTypeItem(): T?
1554                             fun wildcardTypeItem_extendsBound(): List<out T>?
1555                             fun wildcardTypeItem_superBound(): List<in T>?
1556                         }
1557                     """
1558                 ),
1559             ),
1560         ) {
1561             val inputClass = codebase.assertClass("test.pkg.Input")
1562 
1563             // Get the type variables from the class.
1564             val (usedTypeVariable, unusedTypeVariable) = inputClass.typeParameterList
1565 
1566             // Get the type to substitute
1567             val javaLongType = inputClass.assertField("javaLongType").type() as ReferenceTypeItem
1568 
1569             // Iterate over the methods
1570             val types = buildString {
1571                 for (method in inputClass.methods()) {
1572                     val name = method.name()
1573                     val typeToTest = method.returnType()
1574 
1575                     fun TypeItem.typeInfo() =
1576                         testTypeString(
1577                             annotations = true,
1578                             kotlinStyleNulls = true,
1579                         )
1580 
1581                     append(name).append("\n")
1582                     append("    original: ${typeToTest.typeInfo()}\n")
1583 
1584                     // Map the Unused type variable to java.lang.Long. This should have no effect of
1585                     // on the test type.
1586                     typeToTest.convertType(mapOf(unusedTypeVariable to javaLongType)).also { result
1587                         ->
1588                         append("${"    no change"}: ${result.typeInfo()}\n")
1589                         val unusedMessage =
1590                             "conversion of ${unusedTypeVariable.name()} to $javaLongType in $name"
1591                         assertWithMessage(unusedMessage).that(result).isSameInstanceAs(typeToTest)
1592                     }
1593 
1594                     // Map the T type variable to java.lang.Long. This should change every type
1595                     // except the primitive type.
1596                     typeToTest.convertType(mapOf(usedTypeVariable to javaLongType)).also { result ->
1597                         append("${"    T -> java.lang.Long"}: ${result.typeInfo()}\n")
1598                         val usedMessage =
1599                             "conversion of ${usedTypeVariable.name()} to $javaLongType in $name"
1600                         if (name == "primitiveTypeItem") {
1601                             assertWithMessage(usedMessage).that(result).isSameInstanceAs(typeToTest)
1602                         } else {
1603                             assertWithMessage(usedMessage)
1604                                 .that(result)
1605                                 .isNotSameInstanceAs(typeToTest)
1606                         }
1607                     }
1608 
1609                     append("\n")
1610                 }
1611             }
1612 
1613             val optionalLambda =
1614                 """
1615                     lambdaTypeItem
1616                         original: kotlin.jvm.functions.Function1<T,java.lang.Integer>?
1617                         no change: kotlin.jvm.functions.Function1<T,java.lang.Integer>?
1618                         T -> java.lang.Long: kotlin.jvm.functions.Function1<java.lang.Long,java.lang.Integer>?
1619                 """
1620 
1621             assertEquals(
1622                 """
1623                     arrayTypeItem
1624                         original: T[]?
1625                         no change: T[]?
1626                         T -> java.lang.Long: java.lang.Long[]?
1627 
1628                     classTypeItem
1629                         original: java.util.List<T>?
1630                         no change: java.util.List<T>?
1631                         T -> java.lang.Long: java.util.List<java.lang.Long>?
1632                     ${if (inputFormat == InputFormat.KOTLIN) optionalLambda else ""}
1633                     primitiveTypeItem
1634                         original: int
1635                         no change: int
1636                         T -> java.lang.Long: int
1637 
1638                     variableTypeItem
1639                         original: T?
1640                         no change: T?
1641                         T -> java.lang.Long: java.lang.Long?
1642 
1643                     wildcardTypeItem_extendsBound
1644                         original: java.util.List<? extends T>?
1645                         no change: java.util.List<? extends T>?
1646                         T -> java.lang.Long: java.util.List<? extends java.lang.Long>?
1647 
1648                     wildcardTypeItem_superBound
1649                         original: java.util.List<? super T>?
1650                         no change: java.util.List<? super T>?
1651                         T -> java.lang.Long: java.util.List<? super java.lang.Long>?
1652                 """
1653                     .trimIndent(),
1654                 types.trim()
1655             )
1656         }
1657     }
1658 
1659     @Test
1660     fun `Test transform's creation of duplicate objects`() {
1661         val typeUseAnnotation =
1662             java(
1663                 """
1664             package test.annotation;
1665             import java.lang.annotation.ElementType;
1666             import java.lang.annotation.Target;
1667 
1668             @Target(ElementType.TYPE_USE)
1669             public @interface TypeUse {}
1670         """
1671             )
1672         runCodebaseTest(
1673             inputSet(
1674                 signature(
1675                     """
1676                         // Signature format: 5.0
1677                         // - kotlin-style-nulls=yes
1678                         // - kotlin-name-type-order=yes
1679                         // - include-type-use-annotations=yes
1680                         package test.pkg {
1681                           public interface Input<T> {
1682                             // One for each TypeItem subinterface supported in signature files.
1683                             method public arrayTypeItem(): T @test.annotation.TypeUse []?;
1684                             method public classTypeItem(): @test.annotation.TypeUse java.util.List<@test.annotation.TypeUse T>?;
1685                             method public primitiveTypeItem(): @test.annotation.TypeUse int;
1686                             method public variableTypeItem(): @test.annotation.TypeUse T?;
1687                             method public wildcardTypeItem_extendsBound(): java.util.List<? extends @test.annotation.TypeUse T>?;
1688                             method public wildcardTypeItem_superBound(): java.util.List<? super @test.annotation.TypeUse T>?;
1689                           }
1690                         }
1691                         package test.annotation {
1692                           public @interface TypeUse {
1693                           }
1694                         }
1695                     """
1696                 ),
1697             ),
1698             inputSet(
1699                 KnownSourceFiles.nonNullSource,
1700                 KnownSourceFiles.nullableSource,
1701                 typeUseAnnotation,
1702                 java(
1703                     """
1704                         package test.pkg;
1705                         import android.annotation.NonNull;
1706                         import android.annotation.Nullable;
1707                         import java.util.List;
1708                         import test.annotation.TypeUse;
1709                         public interface Input<T,Unused> {
1710                             @NonNull T @TypeUse @Nullable [] arrayTypeItem();
1711                             @TypeUse @Nullable List<@TypeUse @NonNull T> classTypeItem();
1712                             @TypeUse int primitiveTypeItem();
1713                             @TypeUse @Nullable T variableTypeItem();
1714                             @Nullable List<? extends @TypeUse @NonNull T> wildcardTypeItem_extendsBound();
1715                             @Nullable List<? super @TypeUse @NonNull T> wildcardTypeItem_superBound();
1716                         }
1717                     """
1718                 ),
1719             ),
1720             inputSet(
1721                 typeUseAnnotation,
1722                 kotlin(
1723                     """
1724                         package test.pkg
1725                         import java.util.List
1726                         import test.annotation.TypeUse
1727                         interface Input<T,Unused> {
1728                             fun arrayTypeItem(): @TypeUse Array<T>?
1729                             fun classTypeItem(): @TypeUse List<@TypeUse T>?
1730                             fun lambdaTypeItem(): ((@TypeUse T) -> @TypeUse Int)?
1731                             fun primitiveTypeItem(): @TypeUse Int
1732                             fun variableTypeItem(): @TypeUse T?
1733                             fun wildcardTypeItem_extendsBound(): List<out @TypeUse T>?
1734                             fun wildcardTypeItem_superBound(): List<in @TypeUse T>?
1735                         }
1736                     """
1737                 ),
1738             ),
1739         ) {
1740             val inputClass = codebase.assertClass("test.pkg.Input")
1741 
1742             // Iterate over the methods
1743             val types = buildString {
1744                 for (method in inputClass.methods()) {
1745                     val name = method.name()
1746                     val typeToTest = method.returnType()
1747 
1748                     fun TypeItem.typeInfo() =
1749                         testTypeString(
1750                             annotations = true,
1751                             kotlinStyleNulls = true,
1752                         )
1753 
1754                     append(name).append("\n")
1755                     append("    original: ${typeToTest.typeInfo()}\n")
1756 
1757                     // Check that a no-op transformation returns the TypeItem on which it is called.
1758                     typeToTest.transform(BaseTypeTransformer()).also { result ->
1759                         append("${"    no change"}: ${result.typeInfo()}\n")
1760                         val unusedMessage = "no-op transformation in $name"
1761                         assertWithMessage(unusedMessage).that(result).isSameInstanceAs(typeToTest)
1762                     }
1763 
1764                     // A TypeTransformer that will discard all type annotations.
1765                     val annotationsRemover =
1766                         object : BaseTypeTransformer() {
1767                             override fun transform(modifiers: TypeModifiers): TypeModifiers {
1768                                 return modifiers.substitute(annotations = emptyList())
1769                             }
1770                         }
1771 
1772                     // Check that an actual transformation returns different objects.
1773                     typeToTest.transform(annotationsRemover).also { result ->
1774                         append("    discarded annotations: ${result.typeInfo()}\n")
1775                         val usedMessage = "discarded annotations in $name"
1776                         assertWithMessage(usedMessage).that(result).isNotSameInstanceAs(typeToTest)
1777                     }
1778 
1779                     append("\n")
1780                 }
1781             }
1782 
1783             val optionalLambda =
1784                 """
1785                     lambdaTypeItem
1786                         original: kotlin.jvm.functions.Function1<@test.annotation.TypeUse T,java.lang.@test.annotation.TypeUse Integer>?
1787                         no change: kotlin.jvm.functions.Function1<@test.annotation.TypeUse T,java.lang.@test.annotation.TypeUse Integer>?
1788                         discarded annotations: kotlin.jvm.functions.Function1<T,java.lang.Integer>?
1789                 """
1790 
1791             assertEquals(
1792                 """
1793                     arrayTypeItem
1794                         original: T @test.annotation.TypeUse []?
1795                         no change: T @test.annotation.TypeUse []?
1796                         discarded annotations: T[]?
1797 
1798                     classTypeItem
1799                         original: java.util.@test.annotation.TypeUse List<@test.annotation.TypeUse T>?
1800                         no change: java.util.@test.annotation.TypeUse List<@test.annotation.TypeUse T>?
1801                         discarded annotations: java.util.List<T>?
1802                     ${if (inputFormat == InputFormat.KOTLIN) optionalLambda else ""}
1803                     primitiveTypeItem
1804                         original: @test.annotation.TypeUse int
1805                         no change: @test.annotation.TypeUse int
1806                         discarded annotations: int
1807 
1808                     variableTypeItem
1809                         original: @test.annotation.TypeUse T?
1810                         no change: @test.annotation.TypeUse T?
1811                         discarded annotations: T?
1812 
1813                     wildcardTypeItem_extendsBound
1814                         original: java.util.List<? extends @test.annotation.TypeUse T>?
1815                         no change: java.util.List<? extends @test.annotation.TypeUse T>?
1816                         discarded annotations: java.util.List<? extends T>?
1817 
1818                     wildcardTypeItem_superBound
1819                         original: java.util.List<? super @test.annotation.TypeUse T>?
1820                         no change: java.util.List<? super @test.annotation.TypeUse T>?
1821                         discarded annotations: java.util.List<? super T>?
1822                 """
1823                     .trimIndent(),
1824                 types.trim()
1825             )
1826         }
1827     }
1828 
1829     @Test
1830     fun `Test hasTypeArguments`() {
1831         runCodebaseTest(
1832             java(
1833                 """
1834                     package test.pkg;
1835                     public abstract class Foo implements Comparable<String> {}
1836                 """
1837             ),
1838             kotlin(
1839                 """
1840                     package test.pkg
1841                     abstract class Foo: Comparable<String>
1842                 """
1843             ),
1844             signature(
1845                 """
1846                     // Signature format: 2.0
1847                     package test.pkg {
1848                       public abstract class Foo implements Comparable<String> {}
1849                     }
1850                 """
1851             ),
1852         ) {
1853             val classType = codebase.assertClass("test.pkg.Foo").type()
1854             assertThat(classType.hasTypeArguments()).isFalse()
1855 
1856             val interfaceType = codebase.assertClass("test.pkg.Foo").interfaceTypes().single()
1857             assertThat(interfaceType.hasTypeArguments()).isTrue()
1858         }
1859     }
1860 
1861     @Test
1862     fun `Test toSimpleType on varargs parameter`() {
1863         runCodebaseTest(
1864             java(
1865                 """
1866                     package test.pkg;
1867                     public interface Foo {
1868                         void foo(String...p);
1869                     }
1870                 """
1871             ),
1872             kotlin(
1873                 """
1874                     package test.pkg
1875                     interface Foo {
1876                         fun foo(vararg p: String)
1877                     }
1878                 """
1879             ),
1880             signature(
1881                 """
1882                     // Signature format: 2.0
1883                     package test.pkg {
1884                       public interface Foo {
1885                         method public void foo(String...);
1886                       }
1887                     }
1888                 """
1889             ),
1890         ) {
1891             val varargsType =
1892                 codebase.assertClass("test.pkg.Foo").methods().single().parameters().single().type()
1893             assertThat(varargsType.toSimpleType()).isEqualTo("java.lang.String...")
1894         }
1895     }
1896 
1897     @Test
1898     fun `Test toSimpleType on varargs generic parameter`() {
1899         runCodebaseTest(
1900             java(
1901                 @Suppress("unchecked")
1902                 """
1903                     package test.pkg;
1904                     public interface Foo {
1905                         void foo(Comparable<? super String>...p);
1906                     }
1907                 """
1908             ),
1909             kotlin(
1910                 """
1911                     package test.pkg
1912                     interface Foo {
1913                         fun foo(vararg p: Comparable<String>)
1914                     }
1915                 """
1916             ),
1917             signature(
1918                 """
1919                     // Signature format: 2.0
1920                     package test.pkg {
1921                       public interface Foo {
1922                         method public void foo(Comparable<? super String>...);
1923                       }
1924                     }
1925                 """
1926             ),
1927         ) {
1928             val varargsType =
1929                 codebase.assertClass("test.pkg.Foo").methods().single().parameters().single().type()
1930             assertThat(varargsType.toSimpleType())
1931                 .isEqualTo("Comparable<? super java.lang.String>...")
1932         }
1933     }
1934 
1935     @Test
1936     fun `Test toSimpleType on nested class`() {
1937         runCodebaseTest(
1938             java(
1939                 """
1940                     package test.pkg;
1941                     public interface Foo {
1942                         void foo(Thread.UncaughtExceptionHandler p);
1943                     }
1944                 """
1945             ),
1946             kotlin(
1947                 """
1948                     package test.pkg
1949                     interface Foo {
1950                         fun foo(p: Thread.UncaughtExceptionHandler)
1951                     }
1952                 """
1953             ),
1954             signature(
1955                 """
1956                     // Signature format: 2.0
1957                     package test.pkg {
1958                       public interface Foo {
1959                         method public void foo(Thread.UncaughtExceptionHandler);
1960                       }
1961                     }
1962                 """
1963             ),
1964         ) {
1965             val varargsType =
1966                 codebase.assertClass("test.pkg.Foo").methods().single().parameters().single().type()
1967             assertThat(varargsType.toSimpleType())
1968                 .isEqualTo("java.lang.Thread.UncaughtExceptionHandler")
1969         }
1970     }
1971 
1972     @Test
1973     fun `Non-last varargs param in deprecated method`() {
1974         runCodebaseTest(
1975             kotlin(
1976                 """
1977                     package test.pkg
1978                     class Foo {
1979                         fun notDeprecated(vararg str: String, i: Int) = Unit
1980                         @Deprecated(message = "message", level = DeprecationLevel.WARNING)
1981                         fun deprecatedWarning(vararg str: String, i: Int) = Unit
1982                         @Deprecated(message = "message", level = DeprecationLevel.ERROR)
1983                         fun deprecatedError(vararg str: String, i: Int) = Unit
1984                         @Deprecated(message = "message", level = DeprecationLevel.HIDDEN)
1985                         fun deprecatedHidden(vararg str: String, i: Int) = Unit
1986                     }
1987                 """
1988             )
1989         ) {
1990             val foo = codebase.assertClass("test.pkg.Foo")
1991             val notDeprecated = foo.methods().single { it.name() == "notDeprecated" }
1992             val deprecatedWarning = foo.methods().single { it.name() == "deprecatedWarning" }
1993             val deprecatedError = foo.methods().single { it.name() == "deprecatedError" }
1994             val deprecatedHidden = foo.methods().single { it.name() == "deprecatedHidden" }
1995 
1996             fun MethodItem.firstParameterIsVarargs() =
1997                 (parameters().first().type() as ArrayTypeItem).isVarargs
1998 
1999             assertThat(notDeprecated.firstParameterIsVarargs()).isFalse()
2000             assertThat(deprecatedWarning.firstParameterIsVarargs()).isFalse()
2001             assertThat(deprecatedError.firstParameterIsVarargs()).isFalse()
2002             assertThat(deprecatedHidden.firstParameterIsVarargs()).isFalse()
2003         }
2004     }
2005 }
2006