1 /*
2  * 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.ClassItem
21 import com.android.tools.metalava.model.ClassTypeItem
22 import com.android.tools.metalava.model.PrimitiveTypeItem
23 import com.android.tools.metalava.model.ReferenceTypeItem
24 import com.android.tools.metalava.model.TypeArgumentTypeItem
25 import com.android.tools.metalava.model.VariableTypeItem
26 import com.android.tools.metalava.model.WildcardTypeItem
27 import com.android.tools.metalava.model.testsuite.BaseModelTest
28 import com.android.tools.metalava.testing.java
29 import com.android.tools.metalava.testing.kotlin
30 import com.google.common.truth.Truth.assertThat
31 import com.google.common.truth.Truth.assertWithMessage
32 import org.junit.Test
33 
34 class CommonTypeItemTest : BaseModelTest() {
35     @Test
Test primitive typesnull36     fun `Test primitive types`() {
37         runCodebaseTest(
38             java(
39                 """
40                     package test.pkg;
41                     public class Foo {
42                         public void foo(
43                             boolean p0,
44                             byte p1,
45                             char p2,
46                             double p3,
47                             float p4,
48                             int p5,
49                             long p6,
50                             short p7
51                         ) {}
52                     }
53                 """
54             ),
55             kotlin(
56                 """
57                     package test.pkg
58                     class Foo {
59                         fun foo(
60                             p0: Boolean,
61                             p1: Byte,
62                             p2: Char,
63                             p3: Double,
64                             p4: Float,
65                             p5: Int,
66                             p6: Long,
67                             p7: Short
68                         ) = Unit
69                     }
70                 """
71             ),
72             signature(
73                 """
74                     // Signature format: 3.0
75                     package test.pkg {
76                       public class Foo {
77                         ctor public Foo();
78                         method public void foo(boolean, byte, char, double, float, int, long, short);
79                       }
80                     }
81                 """
82                     .trimIndent()
83             )
84         ) {
85             val method = codebase.assertClass("test.pkg.Foo").methods().single()
86 
87             val returnType = method.returnType()
88             assertThat(returnType).isInstanceOf(PrimitiveTypeItem::class.java)
89             assertThat((returnType as PrimitiveTypeItem).kind)
90                 .isEqualTo(PrimitiveTypeItem.Primitive.VOID)
91 
92             val expectedParamTypes =
93                 listOf(
94                     PrimitiveTypeItem.Primitive.BOOLEAN,
95                     PrimitiveTypeItem.Primitive.BYTE,
96                     PrimitiveTypeItem.Primitive.CHAR,
97                     PrimitiveTypeItem.Primitive.DOUBLE,
98                     PrimitiveTypeItem.Primitive.FLOAT,
99                     PrimitiveTypeItem.Primitive.INT,
100                     PrimitiveTypeItem.Primitive.LONG,
101                     PrimitiveTypeItem.Primitive.SHORT
102                 )
103 
104             val params = method.parameters().map { it.type() }
105             assertThat(params).hasSize(expectedParamTypes.size)
106             for ((param, expectedKind) in params.zip(expectedParamTypes)) {
107                 assertThat(param).isInstanceOf(PrimitiveTypeItem::class.java)
108                 assertThat((param as PrimitiveTypeItem).kind).isEqualTo(expectedKind)
109             }
110         }
111     }
112 
113     @Test
Test primitive array typesnull114     fun `Test primitive array types`() {
115         runCodebaseTest(
116             java(
117                 """
118                     package test.pkg;
119                     public class Foo {
120                         public void foo(
121                             int[] p0,
122                             char[] p1
123                         ) {}
124                     }
125                 """
126             ),
127             // The Kotlin equivalent can be interpreted with java.lang types instead of primitives
128             signature(
129                 """
130                     // Signature format: 3.0
131                     package test.pkg {
132                       public class Foo {
133                         ctor public Foo();
134                         method public void foo(int[], char[]);
135                       }
136                     }
137                 """
138                     .trimIndent()
139             )
140         ) {
141             val method = codebase.assertClass("test.pkg.Foo").methods().single()
142 
143             val paramTypes = method.parameters().map { it.type() }
144             assertThat(paramTypes).hasSize(2)
145 
146             // int[]
147             val intArray = paramTypes[0]
148             assertThat(intArray).isInstanceOf(ArrayTypeItem::class.java)
149             val int = (intArray as ArrayTypeItem).componentType
150             assertThat(int).isInstanceOf(PrimitiveTypeItem::class.java)
151             assertThat((int as PrimitiveTypeItem).kind).isEqualTo(PrimitiveTypeItem.Primitive.INT)
152             assertThat(intArray.isVarargs).isFalse()
153 
154             // char[]
155             val charArray = paramTypes[1]
156             assertThat(charArray).isInstanceOf(ArrayTypeItem::class.java)
157             val char = (charArray as ArrayTypeItem).componentType
158             assertThat(char).isInstanceOf(PrimitiveTypeItem::class.java)
159             assertThat((char as PrimitiveTypeItem).kind).isEqualTo(PrimitiveTypeItem.Primitive.CHAR)
160             assertThat(charArray.isVarargs).isFalse()
161         }
162     }
163 
164     @Test
Test primitive vararg typesnull165     fun `Test primitive vararg types`() {
166         runCodebaseTest(
167             java(
168                 """
169                     package test.pkg;
170                     public class Foo {
171                         public void foo(int... p0) {}
172                     }
173                 """
174             ),
175             kotlin(
176                 """
177                     package test.pkg
178                     class Foo {
179                         fun foo(vararg p0: Int
180                         ) = Unit
181                     }
182                 """
183             ),
184             signature(
185                 """
186                     // Signature format: 3.0
187                     package test.pkg {
188                       public class Foo {
189                         ctor public Foo();
190                         method public void foo(int...);
191                       }
192                     }
193                 """
194                     .trimIndent()
195             )
196         ) {
197             val method = codebase.assertClass("test.pkg.Foo").methods().single()
198 
199             val paramTypes = method.parameters().map { it.type() }
200             assertThat(paramTypes).hasSize(1)
201 
202             // int... / vararg int
203             val intArray = paramTypes[0]
204             assertThat(intArray).isInstanceOf(ArrayTypeItem::class.java)
205             val int = (intArray as ArrayTypeItem).componentType
206             assertThat(int).isInstanceOf(PrimitiveTypeItem::class.java)
207             assertThat((int as PrimitiveTypeItem).kind).isEqualTo(PrimitiveTypeItem.Primitive.INT)
208             assertThat(intArray.isVarargs).isTrue()
209         }
210     }
211 
212     @Test
Test multidimensional primitive array typesnull213     fun `Test multidimensional primitive array types`() {
214         runCodebaseTest(
215             java(
216                 """
217                     package test.pkg;
218                     public class Foo {
219                         public void foo(
220                             int[][] p0,
221                             char[]... p1
222                         ) {}
223                     }
224                 """
225             ),
226             // The Kotlin equivalent can be interpreted with java.lang types instead of primitives
227             signature(
228                 """
229                     // Signature format: 3.0
230                     package test.pkg {
231                       public class Foo {
232                         ctor public Foo();
233                         method public void foo(int[][], char[]...);
234                       }
235                     }
236                 """
237                     .trimIndent()
238             )
239         ) {
240             val method = codebase.assertClass("test.pkg.Foo").methods().single()
241 
242             val paramTypes = method.parameters().map { it.type() }
243             assertThat(paramTypes).hasSize(2)
244 
245             // int[][]
246             val intArrayArray = paramTypes[0]
247             assertThat(intArrayArray).isInstanceOf(ArrayTypeItem::class.java)
248             assertThat((intArrayArray as ArrayTypeItem).isVarargs).isFalse()
249 
250             val intArray = intArrayArray.componentType
251             assertThat(intArray).isInstanceOf(ArrayTypeItem::class.java)
252             assertThat((intArray as ArrayTypeItem).isVarargs).isFalse()
253 
254             val int = intArray.componentType
255             assertThat(int).isInstanceOf(PrimitiveTypeItem::class.java)
256             assertThat((int as PrimitiveTypeItem).kind).isEqualTo(PrimitiveTypeItem.Primitive.INT)
257 
258             // char[]...
259             val charArrayArray = paramTypes[1]
260             assertThat(charArrayArray).isInstanceOf(ArrayTypeItem::class.java)
261             assertThat((charArrayArray as ArrayTypeItem).isVarargs).isTrue()
262 
263             val charArray = charArrayArray.componentType
264             assertThat(charArray).isInstanceOf(ArrayTypeItem::class.java)
265             assertThat((charArray as ArrayTypeItem).isVarargs).isFalse()
266 
267             val char = charArray.componentType
268             assertThat(char).isInstanceOf(PrimitiveTypeItem::class.java)
269             assertThat((char as PrimitiveTypeItem).kind).isEqualTo(PrimitiveTypeItem.Primitive.CHAR)
270         }
271     }
272 
273     @Test
Test class array typesnull274     fun `Test class array types`() {
275         runCodebaseTest(
276             java(
277                 """
278                     package test.pkg;
279                     public class Foo {
280                         public void foo(
281                             java.lang.String[] p0,
282                             java.lang.String[][] p1,
283                             java.lang.String... p2
284                         ) {}
285                     }
286                 """
287             ),
288             kotlin(
289                 """
290                     package test.pkg
291                     class Foo {
292                         fun foo(
293                             p0: Array<String>,
294                             p1: Array<Array<String>>,
295                             vararg p2: String
296                         ) = Unit
297                     }
298                 """
299             ),
300             signature(
301                 """
302                     // Signature format: 3.0
303                     package test.pkg {
304                       public class Foo {
305                         ctor public Foo();
306                         method public void foo(String![]!, String![]![]!, java.lang.String!...);
307                       }
308                     }
309                 """
310                     .trimIndent()
311             )
312         ) {
313             val method = codebase.assertClass("test.pkg.Foo").methods().single()
314 
315             val paramTypes = method.parameters().map { it.type() }
316             assertThat(paramTypes).hasSize(3)
317 
318             // String[] / Array<String>
319             val simpleArray = paramTypes[0]
320             assertThat(simpleArray).isInstanceOf(ArrayTypeItem::class.java)
321             assertThat((simpleArray as ArrayTypeItem).componentType.isString()).isTrue()
322             assertThat(simpleArray.isVarargs).isFalse()
323 
324             // String[][] / Array<Array<String>>
325             val twoDimensionalArray = paramTypes[1]
326             assertThat(twoDimensionalArray).isInstanceOf(ArrayTypeItem::class.java)
327             assertThat((twoDimensionalArray as ArrayTypeItem).isVarargs).isFalse()
328             val innerArray = twoDimensionalArray.componentType
329             assertThat(innerArray).isInstanceOf(ArrayTypeItem::class.java)
330             assertThat((innerArray as ArrayTypeItem).componentType.isString()).isTrue()
331             assertThat(innerArray.isVarargs).isFalse()
332 
333             // String... / vararg String
334             val varargs = paramTypes[2]
335             assertThat(twoDimensionalArray).isInstanceOf(ArrayTypeItem::class.java)
336             assertThat((varargs as ArrayTypeItem).componentType.isString()).isTrue()
337             assertThat(varargs.isVarargs).isTrue()
338         }
339     }
340 
341     @Test
Test wildcard typesnull342     fun `Test wildcard types`() {
343         runCodebaseTest(
344             java(
345                 """
346                     package test.pkg;
347                     public class Foo<T> {
348                         public void foo(
349                             Foo<?> p0,
350                             Foo<? extends java.lang.String> p1,
351                             Foo<? super java.lang.String> p2,
352                             Foo<? extends java.lang.String[]> p3
353                         ) {}
354                     }
355                 """
356             ),
357             kotlin(
358                 """
359                     package test.pkg
360                     class Foo<T> {
361                         fun foo(
362                             p0: Foo<*>,
363                             p1: Foo<out String>,
364                             p2: Foo<in String>,
365                             p3: Foo<out Array<String>>
366                         ) = Unit
367                     }
368                 """
369             ),
370             signature(
371                 """
372                     // Signature format: 3.0
373                     package test.pkg {
374                       public class Foo<T> {
375                         ctor public Foo();
376                         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[]>!);
377                       }
378                     }
379                 """
380                     .trimIndent()
381             )
382         ) {
383             val method = codebase.assertClass("test.pkg.Foo").methods().single()
384 
385             val wildcardTypes =
386                 method.parameters().map {
387                     val paramType = it.type()
388                     assertThat(paramType).isInstanceOf(ClassTypeItem::class.java)
389                     assertThat((paramType as ClassTypeItem).arguments).hasSize(1)
390                     paramType.arguments.single()
391                 }
392             assertThat(wildcardTypes).hasSize(4)
393 
394             // Foo<?> / Foo<*>
395             // Unbounded wildcards implicitly have an Object extends bound
396             val unboundedWildcard = wildcardTypes[0]
397             assertThat(unboundedWildcard).isInstanceOf(WildcardTypeItem::class.java)
398             val unboundedExtendsBound = (unboundedWildcard as WildcardTypeItem).extendsBound
399             assertThat(unboundedExtendsBound).isNotNull()
400             assertThat(unboundedExtendsBound!!.isJavaLangObject()).isTrue()
401             assertThat(unboundedWildcard.superBound).isNull()
402 
403             // Foo<? extends String> / Foo<out String>
404             val extendsBoundWildcard = wildcardTypes[1]
405             assertThat(extendsBoundWildcard).isInstanceOf(WildcardTypeItem::class.java)
406             val extendsBound = (extendsBoundWildcard as WildcardTypeItem).extendsBound
407             assertThat(extendsBound).isNotNull()
408             assertThat(extendsBound!!.isString()).isTrue()
409             assertThat(extendsBoundWildcard.superBound).isNull()
410 
411             // Foo<? super String> / Foo<in String>
412             // A super bounded wildcard implicitly has an Object extends bound
413             val superBoundWildcard = wildcardTypes[2]
414             assertThat(superBoundWildcard).isInstanceOf(WildcardTypeItem::class.java)
415             val superExtendsBound = (superBoundWildcard as WildcardTypeItem).extendsBound
416             assertThat(superExtendsBound).isNotNull()
417             assertThat(superExtendsBound!!.isJavaLangObject()).isTrue()
418             val superBound = superBoundWildcard.superBound
419             assertThat(superBound).isNotNull()
420             assertThat(superBound!!.isString()).isTrue()
421 
422             // Foo<? extends java.lang.String[]> / Foo<in Array<String>>
423             val arrayExtendsBoundWildcard = wildcardTypes[3]
424             assertThat(arrayExtendsBoundWildcard).isInstanceOf(WildcardTypeItem::class.java)
425             val arrayExtendsBound = (arrayExtendsBoundWildcard as WildcardTypeItem).extendsBound
426             assertThat(arrayExtendsBound).isNotNull()
427             assertThat(arrayExtendsBound).isInstanceOf(ArrayTypeItem::class.java)
428             assertThat((arrayExtendsBound as ArrayTypeItem).componentType.isString()).isTrue()
429         }
430     }
431 
432     @Test
Test variable typesnull433     fun `Test variable types`() {
434         runCodebaseTest(
435             java(
436                 """
437                     package test.pkg;
438                     public class Foo<C> {
439                         public <M> void foo(C p0, M p1) {}
440                     }
441                 """
442             ),
443             kotlin(
444                 """
445                     package test.pkg
446                     class Foo<C> {
447                         fun <M> foo(p0: C, p1: M) = Unit
448                     }
449                 """
450             ),
451             signature(
452                 """
453                     // Signature format: 3.0
454                     package test.pkg {
455                       public class Foo<C> {
456                         ctor public Foo();
457                         method public <M> void foo(C!, M!);
458                       }
459                     }
460                 """
461                     .trimIndent()
462             )
463         ) {
464             val clz = codebase.assertClass("test.pkg.Foo")
465             val classTypeParam = clz.typeParameterList.single()
466             val method = clz.methods().single()
467             val methodTypeParam = method.typeParameterList.single()
468             val paramTypes = method.parameters().map { it.type() }
469             assertThat(paramTypes).hasSize(2)
470 
471             val classTypeVariable = paramTypes[0]
472             classTypeVariable.assertReferencesTypeParameter(classTypeParam)
473             assertThat((classTypeVariable as VariableTypeItem).name).isEqualTo("C")
474 
475             val methodTypeVariable = paramTypes[1]
476             methodTypeVariable.assertReferencesTypeParameter(methodTypeParam)
477             assertThat((methodTypeVariable as VariableTypeItem).name).isEqualTo("M")
478         }
479     }
480 
481     @Test
Test method return type variable typesnull482     fun `Test method return type variable types`() {
483         runCodebaseTest(
484             java(
485                 """
486                     package test.pkg;
487                     public class Foo<T> {
488                         public T bar1() {}
489                         public <A extends java.lang.String> A bar2() {}
490                         public <A extends java.lang.String> T bar3() {}
491                         public <T extends java.lang.String> T bar4() {}
492                     }
493                 """
494             ),
495             kotlin(
496                 """
497                     package test.pkg
498                     class Foo<T> {
499                         fun bar1(): T {}
500                         fun <A: java.lang.String> bar2(): A {}
501                         fun <A: java.lang.String> bar3(): T {}
502                         fun <T: java.lang.String> bar4(): T {}
503                     }
504                 """
505             ),
506             signature(
507                 """
508                     // Signature format: 3.0
509                     package test.pkg {
510                       public class Foo<T> {
511                         method public T bar1();
512                         method public <A extends java.lang.String> A bar2();
513                         method public <A extends java.lang.String> T bar3();
514                         method public <T extends java.lang.String> T bar4();
515                       }
516                     }
517                 """
518                     .trimIndent()
519             )
520         ) {
521             val foo = codebase.assertClass("test.pkg.Foo")
522             val fooTypeParam = foo.typeParameterList.single()
523 
524             val bar1 = foo.methods().single { it.name() == "bar1" }
525             val bar1Return = bar1.returnType()
526             bar1Return.assertReferencesTypeParameter(fooTypeParam)
527 
528             val bar2 = foo.methods().single { it.name() == "bar2" }
529             val bar2TypeParam = bar2.typeParameterList.single()
530             val bar2Return = bar2.returnType()
531             bar2Return.assertReferencesTypeParameter(bar2TypeParam)
532 
533             val bar3 = foo.methods().single { it.name() == "bar3" }
534             val bar3Return = bar3.returnType()
535             bar3Return.assertReferencesTypeParameter(fooTypeParam)
536 
537             val bar4 = foo.methods().single { it.name() == "bar4" }
538             val bar4TypeParam = bar4.typeParameterList.single()
539             val bar4Return = bar4.returnType()
540             bar4Return.assertReferencesTypeParameter(bar4TypeParam)
541         }
542     }
543 
544     @Test
Test method parameter type variable typesnull545     fun `Test method parameter type variable types`() {
546         runCodebaseTest(
547             java(
548                 """
549                     package test.pkg;
550                     public class Foo<T> {
551                         public void bar1(T p0) {}
552                         public <A extends java.lang.String> void bar2(A p0) {}
553                         public <A extends java.lang.String> void bar3(T p0) {}
554                         public <T extends java.lang.String> void bar4(T p0) {}
555                     }
556                 """
557             ),
558             kotlin(
559                 """
560                     package test.pkg
561                     class Foo<T> {
562                         fun bar1(p0: T) = Unit
563                         fun <A: java.lang.String> bar2(p0: A) = Unit
564                         fun <A: java.lang.String> bar3(p0: T) = Unit
565                         fun <T: java.lang.String> bar4(p0: T) = Unit
566                     }
567                 """
568             ),
569             signature(
570                 """
571                     // Signature format: 3.0
572                     package test.pkg {
573                       public class Foo<T> {
574                         method public void bar1(T p);
575                         method public <A extends java.lang.String> void bar2(A p);
576                         method public <A extends java.lang.String> void bar3(T p);
577                         method public <T extends java.lang.String> void bar4(T p);
578                       }
579                     }
580                 """
581                     .trimIndent()
582             )
583         ) {
584             val foo = codebase.assertClass("test.pkg.Foo")
585             val fooParam = foo.typeParameterList.single()
586 
587             val bar1 = foo.methods().single { it.name() == "bar1" }
588             val bar1Param = bar1.parameters().single().type()
589             bar1Param.assertReferencesTypeParameter(fooParam)
590 
591             val bar2 = foo.methods().single { it.name() == "bar2" }
592             val bar2TypeParam = bar2.typeParameterList.single()
593             val bar2Param = bar2.parameters().single().type()
594             bar2Param.assertReferencesTypeParameter(bar2TypeParam)
595 
596             val bar3 = foo.methods().single { it.name() == "bar3" }
597             val bar3Param = bar3.parameters().single().type()
598             bar3Param.assertReferencesTypeParameter(fooParam)
599 
600             val bar4 = foo.methods().single { it.name() == "bar4" }
601             val bar4TypeParam = bar4.typeParameterList.single()
602             val bar4Param = bar4.parameters().single().type()
603             bar4Param.assertReferencesTypeParameter(bar4TypeParam)
604         }
605     }
606 
607     @Test
Test field type variable typesnull608     fun `Test field type variable types`() {
609         runCodebaseTest(
610             java(
611                 """
612                     package test.pkg;
613                     public class Foo<T> {
614                         public T foo;
615                     }
616                 """
617                     .trimIndent()
618             ),
619             kotlin(
620                 """
621                     package test.pkg
622                     class Foo<T> {
623                         @JvmField val foo: T
624                     }
625                 """
626             ),
627             signature(
628                 """
629                     // Signature format: 3.0
630                     package test.pkg {
631                       public class Foo<T> {
632                         field public T foo;
633                       }
634                     }
635                 """
636                     .trimIndent()
637             )
638         ) {
639             val foo = codebase.assertClass("test.pkg.Foo")
640             val fooParam = foo.typeParameterList.single()
641 
642             val fieldType = foo.fields().single { it.name() == "foo" }.type()
643             fieldType.assertReferencesTypeParameter(fooParam)
644         }
645     }
646 
647     @Test
Test property type variable typesnull648     fun `Test property type variable types`() {
649         runCodebaseTest(
650             // No java equivalent
651             kotlin(
652                 """
653                     package test.pkg
654                     class Foo<T> {
655                         val foo: T
656                     }
657                 """
658                     .trimIndent()
659             ),
660             signature(
661                 """
662                     // Signature format: 3.0
663                     package test.pkg {
664                       public class Foo<T> {
665                         property public T foo;
666                       }
667                     }
668                 """
669                     .trimIndent()
670             )
671         ) {
672             val foo = codebase.assertClass("test.pkg.Foo")
673             val fooParam = foo.typeParameterList.single()
674 
675             val propertyType = foo.properties().single { it.name() == "foo" }.type()
676             propertyType.assertReferencesTypeParameter(fooParam)
677         }
678     }
679 
680     @Test
Test class typesnull681     fun `Test class types`() {
682         runCodebaseTest(
683             java(
684                 """
685                     package test.pkg;
686                     public class Foo {
687                         public <T> void foo(
688                             java.lang.String p0,
689                             java.util.List<java.lang.String> p1,
690                             java.util.List<java.lang.String[]> p2,
691                             java.util.Map<java.lang.String, Foo> p3
692                         ) {}
693                     }
694                 """
695             ),
696             kotlin(
697                 """
698                     package test.pkg
699                     class Foo {
700                         fun <T> foo(
701                             p0: String,
702                             p1: List<String>,
703                             p2: List<Array<String>>,
704                             p3: Map<String, Foo>
705                         ) = Unit
706                     }
707                 """
708             ),
709             signature(
710                 """
711                     // Signature format: 3.0
712                     package test.pkg {
713                       public class Foo {
714                         ctor public Foo();
715                         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!>!);
716                       }
717                     }
718                 """
719                     .trimIndent()
720             )
721         ) {
722             val method = codebase.assertClass("test.pkg.Foo").methods().single()
723             val paramTypes = method.parameters().map { it.type() }
724 
725             val stringType = paramTypes[0]
726             assertThat(stringType).isInstanceOf(ClassTypeItem::class.java)
727             assertThat((stringType as ClassTypeItem).qualifiedName).isEqualTo("java.lang.String")
728             assertThat(stringType.className).isEqualTo("String")
729             assertThat(stringType.arguments).isEmpty()
730 
731             // List<String>
732             val stringListType = paramTypes[1]
733             assertThat(stringListType).isInstanceOf(ClassTypeItem::class.java)
734             assertThat((stringListType as ClassTypeItem).qualifiedName).isEqualTo("java.util.List")
735             assertThat(stringListType.className).isEqualTo("List")
736             assertThat(stringListType.arguments).hasSize(1)
737             assertThat(stringListType.arguments.single().isString()).isTrue()
738 
739             // List<String[]> / List<Array<String>>
740             val arrayListType = paramTypes[2]
741             assertThat(arrayListType).isInstanceOf(ClassTypeItem::class.java)
742             assertThat((arrayListType as ClassTypeItem).qualifiedName).isEqualTo("java.util.List")
743             assertThat(arrayListType.arguments).hasSize(1)
744             val arrayType = arrayListType.arguments.single()
745             assertThat(arrayType).isInstanceOf(ArrayTypeItem::class.java)
746             assertThat((arrayType as ArrayTypeItem).componentType.isString()).isTrue()
747 
748             // Map<String, Foo>
749             val mapType = paramTypes[3]
750             assertThat(mapType).isInstanceOf(ClassTypeItem::class.java)
751             assertThat((mapType as ClassTypeItem).qualifiedName).isEqualTo("java.util.Map")
752             assertThat(mapType.arguments).hasSize(2)
753             val mapKeyType = mapType.arguments.first()
754             assertThat(mapKeyType).isInstanceOf(ClassTypeItem::class.java)
755             assertThat((mapKeyType as ClassTypeItem).isString()).isTrue()
756             val mapValueType = mapType.arguments.last()
757             assertThat(mapValueType).isInstanceOf(ClassTypeItem::class.java)
758             assertThat((mapValueType as ClassTypeItem).qualifiedName).isEqualTo("test.pkg.Foo")
759         }
760     }
761 
762     @Test
Test inner typesnull763     fun `Test inner types`() {
764         runCodebaseTest(
765             java(
766                 """
767                     package test.pkg;
768                     public class Outer {
769                         public class Middle {
770                             public class Inner {}
771                         }
772 
773                         public Outer.Middle.Inner foo() {
774                             return new Outer.Middle.Inner();
775                         }
776                     }
777                 """
778             ),
779             kotlin(
780                 """
781                     package test.pkg
782                     class Outer {
783                         inner class Middle {
784                             inner class Inner
785                         }
786 
787                         fun foo(): Outer.Middle.Inner {
788                             return Outer.Middle.Inner()
789                         }
790                     }
791                 """
792             ),
793             signature(
794                 """
795                     // Signature format: 3.0
796                     package test.pkg {
797                       public class Outer {
798                         ctor public Outer();
799                         method public test.pkg.Outer.Middle.Inner foo();
800                       }
801                       public class Outer.Middle {
802                         ctor public Outer.Middle();
803                       }
804                       public class Outer.Middle.Inner {
805                         ctor public Outer.Middle.Inner();
806                       }
807                     }
808                 """
809                     .trimIndent()
810             )
811         ) {
812             val method = codebase.assertClass("test.pkg.Outer").methods().single()
813 
814             // Outer.Middle.Inner
815             val innerType = method.returnType()
816             assertThat(innerType).isInstanceOf(ClassTypeItem::class.java)
817             assertThat((innerType as ClassTypeItem).qualifiedName)
818                 .isEqualTo("test.pkg.Outer.Middle.Inner")
819             assertThat(innerType.className).isEqualTo("Inner")
820             assertThat(innerType.arguments).isEmpty()
821 
822             val middleType = innerType.outerClassType
823             assertThat(middleType).isNotNull()
824             assertThat(middleType!!.qualifiedName).isEqualTo("test.pkg.Outer.Middle")
825             assertThat(middleType.className).isEqualTo("Middle")
826             assertThat(middleType.arguments).isEmpty()
827 
828             val outerType = middleType.outerClassType
829             assertThat(outerType).isNotNull()
830             assertThat(outerType!!.qualifiedName).isEqualTo("test.pkg.Outer")
831             assertThat(outerType.className).isEqualTo("Outer")
832             assertThat(outerType.arguments).isEmpty()
833             assertThat(outerType.outerClassType).isNull()
834         }
835     }
836 
837     @Test
Test inner types from classpathnull838     fun `Test inner types from classpath`() {
839         runCodebaseTest(
840             java(
841                 """
842                     package test.pkg;
843 
844                     import java.util.Map;
845 
846                     public class Test {
847                         public Map.Entry<String,String> foo() {
848                             return new Map.Entry<String,String>();
849                         }
850                     }
851                 """
852             ),
853             kotlin(
854                 """
855                     package test.pkg
856 
857                     import java.util.Map
858 
859                     class Test {
860                         fun foo(): Map.Entry<String,String> {
861                             return Map.Entry<String,String>()
862                         }
863                     }
864                 """
865             ),
866             signature(
867                 """
868                     // Signature format: 3.0
869                     package test.pkg {
870                       public class Test {
871                         ctor public Outer();
872                         method public java.util.Map.Entry<java.lang.String,java.lang.String> foo();
873                       }
874                     }
875                 """
876                     .trimIndent()
877             )
878         ) {
879             val method = codebase.assertClass("test.pkg.Test").methods().single()
880 
881             // Map.Entry<String,String>
882             val innerType = method.returnType()
883             assertThat(innerType).isInstanceOf(ClassTypeItem::class.java)
884             assertThat((innerType as ClassTypeItem).qualifiedName).isEqualTo("java.util.Map.Entry")
885             assertThat(innerType.className).isEqualTo("Entry")
886 
887             val outerType = innerType.outerClassType
888             assertThat(outerType).isNotNull()
889             assertThat(outerType!!.qualifiedName).isEqualTo("java.util.Map")
890             assertThat(outerType.className).isEqualTo("Map")
891             assertThat(outerType.arguments).hasSize(0)
892             assertThat(outerType.outerClassType).isNull()
893         }
894     }
895 
896     @Test
Test inner parameterized typesnull897     fun `Test inner parameterized types`() {
898         runCodebaseTest(
899             java(
900                 """
901                     package test.pkg;
902                     public class Outer<O> {
903                         public class Inner<I> {
904                         }
905 
906                         public <P1, P2> Outer<P1>.Inner<P2> foo() {
907                             return new Outer<P1>.Inner<P2>();
908                         }
909                     }
910                 """
911             ),
912             kotlin(
913                 """
914                     package test.pkg
915                     class Outer<O> {
916                         inner class Inner<I>
917 
918                         fun <P1, P2> foo(): Outer<P1>.Inner<P2> {
919                             return Outer<P1>.Inner<P2>()
920                         }
921                     }
922                 """
923             ),
924             signature(
925                 """
926                     // Signature format: 3.0
927                     package test.pkg {
928                       public class Outer<O> {
929                         ctor public Outer();
930                         method public <P1, P2> test.pkg.Outer<P1!>.Inner<P2!>! foo();
931                       }
932                       public class Outer.Inner<I> {
933                         ctor public Outer.Inner();
934                       }
935                     }
936                 """
937                     .trimIndent()
938             )
939         ) {
940             val method = codebase.assertClass("test.pkg.Outer").methods().single()
941             val methodTypeParameters = method.typeParameterList
942             assertThat(methodTypeParameters).hasSize(2)
943             val p1 = methodTypeParameters[0]
944             val p2 = methodTypeParameters[1]
945 
946             // Outer<P1>.Inner<P2>
947             val innerType = method.returnType()
948             assertThat(innerType).isInstanceOf(ClassTypeItem::class.java)
949             assertThat((innerType as ClassTypeItem).qualifiedName).isEqualTo("test.pkg.Outer.Inner")
950             assertThat(innerType.className).isEqualTo("Inner")
951             assertThat(innerType.arguments).hasSize(1)
952             val innerTypeArgument = innerType.arguments.single()
953             innerTypeArgument.assertReferencesTypeParameter(p2)
954             assertThat((innerTypeArgument as VariableTypeItem).name).isEqualTo("P2")
955 
956             val outerType = innerType.outerClassType
957             assertThat(outerType).isNotNull()
958             assertThat(outerType!!.qualifiedName).isEqualTo("test.pkg.Outer")
959             assertThat(outerType.className).isEqualTo("Outer")
960             assertThat(outerType.outerClassType).isNull()
961             assertThat(outerType.arguments).hasSize(1)
962             val outerClassTypeArgument = outerType.arguments.single()
963             outerClassTypeArgument.assertReferencesTypeParameter(p1)
964             assertThat((outerClassTypeArgument as VariableTypeItem).name).isEqualTo("P1")
965         }
966     }
967 
968     @Test
Test inner parameterized types without explicit outer typenull969     fun `Test inner parameterized types without explicit outer type`() {
970         runCodebaseTest(
971             inputSet(
972                 java(
973                     """
974                         package test.pkg;
975 
976                         import test.pkg1.Outer.Middle.Inner;
977 
978                         public class Test {
979                             public Inner<String> foo() {
980                                 return new Inner<String>();
981                             }
982                         }
983                     """
984                 ),
985                 java(
986                     """
987                         package test.pkg1;
988 
989                         public class Outer<O> {
990                             public class Middle {
991                                 public class Inner<I> {}
992                             }
993                         }
994                     """
995                 ),
996             ),
997             inputSet(
998                 signature(
999                     """
1000                         // Signature format: 3.0
1001                         package test.pkg1 {
1002                           public class Outer<O> {
1003                             ctor public Outer();
1004                           }
1005                           public class Outer.Middle {
1006                             ctor public Outer.Middle();
1007                           }
1008                           public class Outer.Middle.Inner<I> {
1009                             ctor public Outer.Middle.Inner();
1010                           }
1011                         }
1012                     """
1013                 ),
1014                 signature(
1015                     """
1016                         // Signature format: 3.0
1017                         package test.pkg {
1018                           public class Test {
1019                             ctor public Test();
1020                             method public test.pkg1.Outer.Middle.Inner<String> foo();
1021                           }
1022                         }
1023                     """
1024                 )
1025             )
1026         ) {
1027             val method = codebase.assertClass("test.pkg.Test").methods().single()
1028 
1029             val innerType = method.returnType()
1030             assertThat(innerType).isInstanceOf(ClassTypeItem::class.java)
1031             assertThat((innerType as ClassTypeItem).qualifiedName)
1032                 .isEqualTo("test.pkg1.Outer.Middle.Inner")
1033             assertThat(innerType.className).isEqualTo("Inner")
1034             assertThat(innerType.arguments).hasSize(1)
1035 
1036             val middleType = innerType.outerClassType
1037             assertThat(middleType).isNotNull()
1038             assertThat(middleType!!.qualifiedName).isEqualTo("test.pkg1.Outer.Middle")
1039             assertThat(middleType.className).isEqualTo("Middle")
1040             assertThat(middleType.arguments).hasSize(0)
1041 
1042             val outerType = middleType.outerClassType
1043             assertThat(outerType).isNotNull()
1044             assertThat(outerType!!.qualifiedName).isEqualTo("test.pkg1.Outer")
1045             assertThat(outerType.className).isEqualTo("Outer")
1046             assertThat(outerType.outerClassType).isNull()
1047             assertThat(outerType.arguments).hasSize(0)
1048         }
1049     }
1050 
1051     @Test
Test superclass and interface types using type variablesnull1052     fun `Test superclass and interface types using type variables`() {
1053         runCodebaseTest(
1054             java(
1055                 """
1056                     package test.pkg;
1057 
1058                     public class Cache<Query, Result> extends java.util.HashMap<Query,Result> {}
1059 
1060                     public class MyList<E> implements java.util.List<E> {}
1061                 """
1062                     .trimIndent()
1063             ),
1064             kotlin(
1065                 """
1066                     package test.pkg
1067 
1068                     class Cache<Query, Result> : java.util.HashMap<Query, Result>
1069 
1070                     class MyList<E> : java.util.List<E>
1071                 """
1072                     .trimIndent()
1073             ),
1074             signature(
1075                 """
1076                     // Signature format: 2.0
1077                     package test.pkg {
1078                       public class Cache<Query, Result> extends java.util.HashMap<Query,Result> {
1079                       }
1080                       public class MyList<E> implements java.util.List<E> {
1081                       }
1082                     }
1083                 """
1084                     .trimIndent()
1085             )
1086         ) {
1087             // Verify that the Cache superclass type uses the Cache type variables
1088             val cache = codebase.assertClass("test.pkg.Cache")
1089             val cacheTypeParams = cache.typeParameterList
1090             assertThat(cacheTypeParams).hasSize(2)
1091             val queryParam = cacheTypeParams[0]
1092             val resultParam = cacheTypeParams[1]
1093 
1094             val cacheSuperclassType = cache.superClassType()
1095             assertThat(cacheSuperclassType).isInstanceOf(ClassTypeItem::class.java)
1096             assertThat((cacheSuperclassType as ClassTypeItem).qualifiedName)
1097                 .isEqualTo("java.util.HashMap")
1098             assertThat(cacheSuperclassType.arguments).hasSize(2)
1099 
1100             val queryVar = cacheSuperclassType.arguments[0]
1101             queryVar.assertReferencesTypeParameter(queryParam)
1102 
1103             val resultVar = cacheSuperclassType.arguments[1]
1104             resultVar.assertReferencesTypeParameter(resultParam)
1105 
1106             // Verify that the MyList interface type uses the MyList type variable
1107             val myList = codebase.assertClass("test.pkg.MyList")
1108             val myListTypeParams = myList.typeParameterList
1109             assertThat(myListTypeParams).hasSize(1)
1110             val eParam = myListTypeParams.single()
1111 
1112             val myListInterfaces = myList.interfaceTypes()
1113             assertThat(myListInterfaces).hasSize(1)
1114 
1115             val myListInterfaceType = myListInterfaces.single()
1116             assertThat(myListInterfaceType).isInstanceOf(ClassTypeItem::class.java)
1117             assertThat(myListInterfaceType.qualifiedName).isEqualTo("java.util.List")
1118             assertThat(myListInterfaceType.arguments).hasSize(1)
1119 
1120             val eVar = myListInterfaceType.arguments.single()
1121             eVar.assertReferencesTypeParameter(eParam)
1122         }
1123     }
1124 
1125     @Test
Test array of type with parameter used as type parameternull1126     fun `Test array of type with parameter used as type parameter`() {
1127         runCodebaseTest(
1128             signature(
1129                 """
1130                     // Signature format: 2.0
1131                     package test.pkg {
1132                       public class Foo {
1133                         method public java.util.Collection<java.util.List<java.lang.String>[]> foo();
1134                       }
1135                     }
1136                 """
1137                     .trimIndent()
1138             ),
1139             java(
1140                 """
1141                     package test.pkg;
1142 
1143                     import java.util.Collection;
1144                     import java.util.List;
1145 
1146                     public class Foo {
1147                         public Collection<List<String>[]> foo() {}
1148                     }
1149                 """,
1150             ),
1151             kotlin(
1152                 """
1153                     package test.pkg
1154 
1155                     class Foo {
1156                         fun foo(): Collection<Array<List<String>>> {}
1157                     }
1158                 """
1159             )
1160         ) {
1161             val method = codebase.assertClass("test.pkg.Foo").methods().single()
1162 
1163             // java.util.Collection<java.util.List<java.lang.String>[]>
1164             val collectionOfArrayOfStringList = method.returnType()
1165             assertThat(collectionOfArrayOfStringList).isInstanceOf(ClassTypeItem::class.java)
1166             assertThat((collectionOfArrayOfStringList as ClassTypeItem).qualifiedName)
1167                 .isEqualTo("java.util.Collection")
1168             assertThat(collectionOfArrayOfStringList.arguments).hasSize(1)
1169 
1170             // java.util.List<java.lang.String>[]
1171             val arrayOfStringList = collectionOfArrayOfStringList.arguments.single()
1172             assertThat(arrayOfStringList).isInstanceOf(ArrayTypeItem::class.java)
1173 
1174             // java.util.List<java.lang.String>
1175             val stringList = (arrayOfStringList as ArrayTypeItem).componentType
1176             assertThat(stringList).isInstanceOf(ClassTypeItem::class.java)
1177             assertThat((stringList as ClassTypeItem).qualifiedName).isEqualTo("java.util.List")
1178             assertThat(stringList.arguments).hasSize(1)
1179 
1180             // java.lang.String
1181             val string = stringList.arguments.single()
1182             assertThat(string.isString()).isTrue()
1183         }
1184     }
1185 
1186     @Test
Test Kotlin collection removeAll parameter typenull1187     fun `Test Kotlin collection removeAll parameter type`() {
1188         runCodebaseTest(
1189             kotlin(
1190                 """
1191                     package test.pkg
1192                     abstract class Foo<Z> : MutableCollection<Z> {
1193                         override fun addAll(elements: Collection<Z>): Boolean = true
1194                         override fun containsAll(elements: Collection<Z>): Boolean = true
1195                         override fun removeAll(elements: Collection<Z>): Boolean = true
1196                         override fun retainAll(elements: Collection<Z>): Boolean = true
1197                     }
1198                 """
1199             )
1200         ) {
1201             val fooClass = codebase.assertClass("test.pkg.Foo")
1202             val typeParam = fooClass.typeParameterList.single()
1203 
1204             /**
1205              * Make sure that the [ClassItem] has a method whose single parameter is of the type
1206              * `java.lang.Collection` and then run [body] on that type.
1207              */
1208             fun ClassItem.assertMethodTakesCollection(
1209                 name: String,
1210                 body: TypeArgumentTypeItem.() -> Unit
1211             ) {
1212                 val method = methods().single { it.name() == name }
1213                 val paramType = method.parameters().single().type()
1214                 paramType.assertClassTypeItem {
1215                     assertThat(qualifiedName).isEqualTo("java.util.Collection")
1216                     assertThat(arguments).hasSize(1)
1217                     val argument = arguments.single()
1218                     argument.body()
1219                 }
1220             }
1221 
1222             /**
1223              * Make sure that the [ClassItem] has a method whose single parameter is of the type
1224              * `java.lang.Collection<? extends Z>`.
1225              */
1226             fun ClassItem.assertMethodTakesCollectionWildcardExtendsZ(name: String) {
1227                 assertMethodTakesCollection(name) {
1228                     assertWildcardItem { extendsBound!!.assertReferencesTypeParameter(typeParam) }
1229                 }
1230             }
1231 
1232             // Defined in `java.util.Collection` as `addAll(Collection<? extends E> c)`. The type of
1233             // the `addAll` method in `Foo` should be `addAll(Collection<? extends Z>)`.Where `Z`
1234             // references the type parameter in `Foo<Z>`.
1235             fooClass.assertMethodTakesCollectionWildcardExtendsZ("addAll")
1236 
1237             // Defined in `java.util.Collection` as `...(Collection<?> c)` these methods should be
1238             // `...(Collection<? extends Z>)`.Where `Z` references the type parameter in
1239             // `Foo<Z>`.
1240             //
1241             fooClass.assertMethodTakesCollectionWildcardExtendsZ("containsAll")
1242             fooClass.assertMethodTakesCollectionWildcardExtendsZ("removeAll")
1243             fooClass.assertMethodTakesCollectionWildcardExtendsZ("retainAll")
1244         }
1245     }
1246 
1247     @Test
Test convertTypenull1248     fun `Test convertType`() {
1249         runCodebaseTest(
1250             inputSet(
1251                 java(
1252                     """
1253                         package test.pkg;
1254                         import java.util.List;
1255                         import java.util.Map;
1256                         public class Parent<M, N> {
1257                             public M getM() {}
1258                             public N[] getNArray() {}
1259                             public List<M> getMList() {}
1260                             public Map<M, N> getMap() {}
1261                             public Parent<? extends M, ? super N> getWildcards() {}
1262                         }
1263                     """
1264                         .trimIndent()
1265                 ),
1266                 java(
1267                     """
1268                         package test.pkg;
1269                         public class Child<X, Y> extends Parent<X, Y> {}
1270                     """
1271                         .trimIndent()
1272                 ),
1273             ),
1274             inputSet(
1275                 signature(
1276                     """
1277                         // Signature format: 5.0
1278                         package test.pkg {
1279                           public class Child<X, Y> extends test.pkg.Parent<X,Y> {
1280                           }
1281                           public class Parent<M, N> {
1282                             method public M getM();
1283                             method public java.util.List<M> getMList();
1284                             method public java.util.Map<M,N> getMap();
1285                             method public N[] getNArray();
1286                             method public test.pkg.Parent<? extends M, ? super N> getWildcards();
1287                           }
1288                         }
1289                     """
1290                         .trimIndent()
1291                 )
1292             ),
1293             inputSet(
1294                 kotlin(
1295                     """
1296                         package test.pkg
1297                         open class Parent<M, N> {
1298                             fun getM(): M {}
1299                             fun getNArray(): Array<N> {}
1300                             fun getMList(): List<M> {}
1301                             fun getMap(): Map<M, N> {}
1302                             fun getWildcards(): Parent<out M, in N> {}
1303                         }
1304                         class Child<X, Y> : Parent<X, Y>()
1305                     """
1306                         .trimIndent()
1307                 )
1308             )
1309         ) {
1310             val parent = codebase.assertClass("test.pkg.Parent")
1311             val child = codebase.assertClass("test.pkg.Child")
1312             val childTypeParams = child.typeParameterList
1313             val x = childTypeParams[0]
1314             val y = childTypeParams[1]
1315 
1316             val mVar = parent.assertMethod("getM", "").returnType()
1317             val xVar = mVar.convertType(child, parent)
1318             assertThat(xVar.toTypeString()).isEqualTo("X")
1319             xVar.assertReferencesTypeParameter(x)
1320 
1321             val nArray = parent.assertMethod("getNArray", "").returnType()
1322             val yArray = nArray.convertType(child, parent)
1323             assertThat(yArray.toTypeString()).isEqualTo("Y[]")
1324             assertThat((yArray as ArrayTypeItem).isVarargs).isFalse()
1325             yArray.componentType.assertReferencesTypeParameter(y)
1326 
1327             val mList = parent.assertMethod("getMList", "").returnType()
1328             val xList = mList.convertType(child, parent)
1329             assertThat(xList.toTypeString()).isEqualTo("java.util.List<X>")
1330             assertThat((xList as ClassTypeItem).qualifiedName).isEqualTo("java.util.List")
1331             xList.arguments.single().assertReferencesTypeParameter(x)
1332 
1333             val mToNMap = parent.assertMethod("getMap", "").returnType()
1334             val xToYMap = mToNMap.convertType(child, parent)
1335             assertThat(xToYMap.toTypeString()).isEqualTo("java.util.Map<X,Y>")
1336             assertThat((xToYMap as ClassTypeItem).qualifiedName).isEqualTo("java.util.Map")
1337             xToYMap.arguments[0].assertReferencesTypeParameter(x)
1338             xToYMap.arguments[1].assertReferencesTypeParameter(y)
1339 
1340             val wildcards = parent.assertMethod("getWildcards", "").returnType()
1341             val convertedWildcards = wildcards.convertType(child, parent)
1342             assertThat(convertedWildcards.toTypeString())
1343                 .isEqualTo("test.pkg.Parent<? extends X,? super Y>")
1344             assertThat((convertedWildcards as ClassTypeItem).qualifiedName)
1345                 .isEqualTo("test.pkg.Parent")
1346             assertThat(convertedWildcards.arguments).hasSize(2)
1347 
1348             val extendsX = convertedWildcards.arguments[0] as WildcardTypeItem
1349             extendsX.extendsBound!!.assertReferencesTypeParameter(x)
1350             val superN = convertedWildcards.arguments[1] as WildcardTypeItem
1351             superN.superBound!!.assertReferencesTypeParameter(y)
1352         }
1353     }
1354 
1355     @Test
Test convertType with mapsnull1356     fun `Test convertType with maps`() {
1357         runCodebaseTest(
1358             java(
1359                 """
1360                     package test.pkg;
1361                     import java.util.List;
1362                     public class Foo<T, X> {
1363                       public Number numberType;
1364 
1365                       public int primitiveType;
1366                       public int primitiveTypeAfterMatchingConversion;
1367 
1368                       public T variableType;
1369                       public Number variableTypeAfterMatchingConversion;
1370 
1371                       public T[] arrayType;
1372                       public Number[] arrayTypeAfterMatchingConversion;
1373 
1374                       public Foo<T, String> classType;
1375                       public Foo<Number, String> classTypeAfterMatchingConversion;
1376 
1377                       public Foo<? extends T, String> wildcardExtendsType;
1378                       public Foo<? extends Number, String> wildcardExtendsTypeAfterMatchingConversion;
1379 
1380                       public Foo<? super T, String> wildcardSuperType;
1381                       public Foo<? super Number, String> wildcardSuperTypeAfterMatchingConversion;
1382                     }
1383                 """
1384                     .trimIndent()
1385             ),
1386             kotlin(
1387                 """
1388                     package test.pkg
1389                     class Foo<T, X> {
1390                         @JvmField val numberType: Number
1391 
1392                         @JvmField val primitiveType: Int
1393                         @JvmField val primitiveTypeAfterMatchingConversion: Int
1394 
1395                         @JvmField val variableType: T
1396                         @JvmField val variableTypeAfterMatchingConversion: Number
1397 
1398                         @JvmField val arrayType: Array<T>
1399                         @JvmField val arrayTypeAfterMatchingConversion: Array<Number>
1400 
1401                         @JvmField val classType: Foo<T, String>
1402                         @JvmField val classTypeAfterMatchingConversion: Foo<Number, String>
1403 
1404                         @JvmField val wildcardExtendsType: Foo<out T, String>
1405                         @JvmField val wildcardExtendsTypeAfterMatchingConversion: Foo<out Number, String>
1406 
1407                         @JvmField val wildcardSuperType: Foo<in T, String>
1408                         @JvmField val wildcardSuperTypeAfterMatchingConversion: Foo<in Number, String>
1409                     }
1410                 """
1411                     .trimIndent()
1412             ),
1413             signature(
1414                 """
1415                     // Signature format: 5.0
1416                     package test.pkg {
1417                       public class Foo<T, X> {
1418                         field public Number numberType;
1419 
1420                         field public int primitiveType;
1421                         field public int primitiveTypeAfterMatchingConversion;
1422 
1423                         field public T variableType;
1424                         field public Number variableTypeAfterMatchingConversion;
1425 
1426                         field public T[] arrayType;
1427                         field public Number[] arrayTypeAfterMatchingConversion;
1428 
1429                         field public test.pkg.Foo<T, String> classType;
1430                         field public test.pkg.Foo<Number, String> classTypeAfterMatchingConversion;
1431 
1432                         field public test.pkg.Foo<? extends T, String> wildcardExtendsType;
1433                         field public test.pkg.Foo<? extends Number, String> wildcardExtendsTypeAfterMatchingConversion;
1434 
1435                         field public test.pkg.Foo<? super T, String> wildcardSuperType;
1436                         field public test.pkg.Foo<? super Number, String> wildcardSuperTypeAfterMatchingConversion;
1437                       }
1438                     }
1439                 """
1440                     .trimIndent()
1441             )
1442         ) {
1443             val fooClass = codebase.assertClass("test.pkg.Foo")
1444             val t = fooClass.typeParameterList.single { it.name() == "T" }
1445             val x = fooClass.typeParameterList.single { it.name() == "X" }
1446             val numberType = fooClass.assertField("numberType").type() as ReferenceTypeItem
1447 
1448             val matchingBindings = mapOf(t to numberType)
1449             val nonMatchingBindings = mapOf(x to numberType)
1450 
1451             val afterMatchingConversionSuffix = "AfterMatchingConversion"
1452             val fieldsToCheck =
1453                 fooClass.fields().filter {
1454                     it.name() != "numberType" && !it.name().endsWith(afterMatchingConversionSuffix)
1455                 }
1456 
1457             for (fieldItem in fieldsToCheck) {
1458                 val fieldType = fieldItem.type()
1459 
1460                 val fieldName = fieldItem.name()
1461                 val expectedMatchedFieldType =
1462                     fooClass.assertField(fieldName + afterMatchingConversionSuffix).type()
1463 
1464                 assertWithMessage("conversion that matches $fieldName")
1465                     .that(fieldType.convertType(matchingBindings))
1466                     .isEqualTo(expectedMatchedFieldType)
1467 
1468                 // Expect no change if it does not match.
1469                 assertWithMessage("conversion that does not match $fieldName")
1470                     .that(fieldType.convertType(nonMatchingBindings))
1471                     .isEqualTo(fieldType)
1472             }
1473         }
1474     }
1475 
1476     @Test
Test hasTypeArgumentsnull1477     fun `Test hasTypeArguments`() {
1478         runCodebaseTest(
1479             java(
1480                 """
1481                     package test.pkg;
1482                     public abstract class Foo implements Comparable<String> {}
1483                 """
1484             ),
1485             kotlin(
1486                 """
1487                     package test.pkg
1488                     abstract class Foo: Comparable<String>
1489                 """
1490             ),
1491             signature(
1492                 """
1493                     // Signature format: 2.0
1494                     package test.pkg {
1495                       public abstract class Foo implements Comparable<String> {}
1496                     }
1497                 """
1498             ),
1499         ) {
1500             val classType = codebase.assertClass("test.pkg.Foo").type()
1501             assertThat(classType.hasTypeArguments()).isFalse()
1502 
1503             val interfaceType = codebase.assertClass("test.pkg.Foo").interfaceTypes().single()
1504             assertThat(interfaceType.hasTypeArguments()).isTrue()
1505         }
1506     }
1507 }
1508