• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * Copyright (C) 2019 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.psi
18 
19 import com.android.tools.lint.checks.infrastructure.TestFile
20 import com.android.tools.metalava.ARG_CLASS_PATH
21 import com.android.tools.metalava.DriverTest
22 import com.android.tools.metalava.Options
23 import com.android.tools.metalava.java
24 import com.android.tools.metalava.kotlin
25 import com.android.tools.metalava.libcoreNonNullSource
26 import com.android.tools.metalava.libcoreNullableSource
27 import com.android.tools.metalava.model.AnnotationItem
28 import com.android.tools.metalava.model.Item
29 import com.android.tools.metalava.nonNullSource
30 import com.android.tools.metalava.nullableSource
31 import com.android.tools.metalava.options
32 import com.android.tools.metalava.parseSources
33 import com.intellij.psi.JavaRecursiveElementVisitor
34 import com.intellij.psi.PsiAnnotation
35 import com.intellij.psi.PsiType
36 import com.intellij.psi.PsiTypeElement
37 import org.jetbrains.uast.UAnnotation
38 import org.jetbrains.uast.UMethod
39 import org.jetbrains.uast.UTypeReferenceExpression
40 import org.jetbrains.uast.UVariable
41 import org.jetbrains.uast.toUElement
42 import org.jetbrains.uast.visitor.AbstractUastVisitor
43 import org.junit.Assert.assertEquals
44 import org.junit.Test
45 import java.io.File
46 import java.io.PrintWriter
47 import java.io.StringWriter
48 import java.util.function.Predicate
49 
50 class PsiTypePrinterTest : DriverTest() {
51     @Test
52     fun `Test class reference types`() {
53         assertEquals(
54             """
55             Type: PsiClassReferenceType
56             Canonical: java.lang.String
57             Printed: java.lang.String!
58 
59             Type: PsiClassReferenceType
60             Canonical: java.util.List<java.lang.String>
61             Merged: [@Nullable]
62             Printed: java.util.List<java.lang.String!>?
63 
64             Type: PsiClassReferenceType
65             Canonical: java.util.List<java.lang.String>
66             Annotated: java.util.@Nullable List<java.lang.@Nullable String>
67             Printed: java.util.List<java.lang.String?>?
68 
69             Type: PsiClassReferenceType
70             Canonical: java.util.List<java.lang.String>
71             Annotated: java.util.@NonNull List<java.lang.@Nullable String>
72             Printed: java.util.List<java.lang.String?>
73 
74             Type: PsiClassReferenceType
75             Canonical: java.util.List<java.lang.String>
76             Annotated: java.util.@Nullable List<java.lang.@NonNull String>
77             Printed: java.util.List<java.lang.String>?
78 
79             Type: PsiClassReferenceType
80             Canonical: java.util.List<java.lang.String>
81             Annotated: java.util.@NonNull List<java.lang.@NonNull String>
82             Printed: java.util.List<java.lang.String>
83 
84             Type: PsiClassReferenceType
85             Canonical: java.util.Map<java.lang.String,java.lang.Number>
86             Printed: java.util.Map<java.lang.String!,java.lang.Number!>!
87 
88             Type: PsiClassReferenceType
89             Canonical: java.lang.Number
90             Printed: java.lang.Number!
91             """.trimIndent(),
92             prettyPrintTypes(
93                 supportTypeUseAnnotations = true,
94                 kotlinStyleNulls = true,
95                 skip = setOf("int", "long"),
96                 files = listOf(
97                     java(
98                         """
99                         package test.pkg;
100                         import java.util.List;
101                         import java.util.Map;
102 
103                         @SuppressWarnings("ALL")
104                         public class MyClass extends Object {
105                            public String myPlatformField1;
106                            public List<String> getList(Map<String, Number> keys) { return null; }
107 
108                            public @androidx.annotation.Nullable String myNullableField;
109                            public @androidx.annotation.Nullable List<String> myNullableFieldWithPlatformElement;
110 
111                            // Type use annotations
112                            public java.util.@libcore.util.Nullable List<java.lang.@libcore.util.Nullable String> myNullableFieldWithNullableElement;
113                            public java.util.@libcore.util.NonNull List<java.lang.@libcore.util.Nullable String> myNonNullFieldWithNullableElement;
114                            public java.util.@libcore.util.Nullable List<java.lang.@libcore.util.NonNull String> myNullableFieldWithNonNullElement;
115                            public java.util.@libcore.util.NonNull List<java.lang.@libcore.util.NonNull String> myNonNullFieldWithNonNullElement;
116                         }
117                         """
118                     ),
119                     nullableSource,
120                     nonNullSource,
121                     libcoreNonNullSource, // allows TYPE_USE
122                     libcoreNullableSource
123                 )
124             ).trimIndent()
125         )
126     }
127 
128     @Test
129     fun `Test class reference types without Kotlin style nulls`() {
130         assertEquals(
131             """
132             Type: PsiClassReferenceType
133             Canonical: java.util.List<java.lang.String>
134             Annotated: java.util.@Nullable List<java.lang.@Nullable String>
135             Printed: java.util.@libcore.util.Nullable List<java.lang.@libcore.util.Nullable String>
136 
137             Type: PsiClassReferenceType
138             Canonical: java.util.List<java.lang.String>
139             Annotated: java.util.@NonNull List<java.lang.@Nullable String>
140             Printed: java.util.@libcore.util.NonNull List<java.lang.@libcore.util.Nullable String>
141 
142             Type: PsiClassReferenceType
143             Canonical: java.util.List<java.lang.String>
144             Annotated: java.util.@Nullable List<java.lang.@NonNull String>
145             Printed: java.util.@libcore.util.Nullable List<java.lang.@libcore.util.NonNull String>
146 
147             Type: PsiClassReferenceType
148             Canonical: java.util.List<java.lang.String>
149             Annotated: java.util.@NonNull List<java.lang.@NonNull String>
150             Printed: java.util.@libcore.util.NonNull List<java.lang.@libcore.util.NonNull String>
151             """.trimIndent(),
152             prettyPrintTypes(
153                 supportTypeUseAnnotations = true,
154                 kotlinStyleNulls = false,
155                 skip = setOf("int", "long"),
156                 files = listOf(
157                     java(
158                         """
159                         package test.pkg;
160                         import java.util.List;
161                         import java.util.Map;
162 
163                         @SuppressWarnings("ALL")
164                         public class MyClass extends Object {
165                            public java.util.@libcore.util.Nullable List<java.lang.@libcore.util.Nullable String> myNullableFieldWithNullableElement;
166                            public java.util.@libcore.util.NonNull List<java.lang.@libcore.util.Nullable String> myNonNullFieldWithNullableElement;
167                            public java.util.@libcore.util.Nullable List<java.lang.@libcore.util.NonNull String> myNullableFieldWithNonNullElement;
168                            public java.util.@libcore.util.NonNull List<java.lang.@libcore.util.NonNull String> myNonNullFieldWithNonNullElement;
169                         }
170                         """
171                     ),
172                     nullableSource,
173                     nonNullSource,
174                     libcoreNonNullSource, // allows TYPE_USE
175                     libcoreNullableSource
176                 )
177             ).trimIndent()
178         )
179     }
180 
181     @Test
182     fun `Test merge annotations`() {
183         assertEquals(
184             """
185             Type: PsiClassReferenceType
186             Canonical: java.lang.String
187             Merged: [@Nullable]
188             Printed: java.lang.String?
189 
190             Type: PsiArrayType
191             Canonical: java.lang.String[]
192             Merged: [@Nullable]
193             Printed: java.lang.String![]?
194 
195             Type: PsiClassReferenceType
196             Canonical: java.util.List<java.lang.String>
197             Merged: [@Nullable]
198             Printed: java.util.List<java.lang.String!>?
199 
200             Type: PsiClassReferenceType
201             Canonical: java.util.Map<java.lang.String,java.lang.Number>
202             Merged: [@Nullable]
203             Printed: java.util.Map<java.lang.String!,java.lang.Number!>?
204 
205             Type: PsiClassReferenceType
206             Canonical: java.lang.Number
207             Merged: [@Nullable]
208             Printed: java.lang.Number?
209 
210             Type: PsiEllipsisType
211             Canonical: java.lang.String...
212             Merged: [@Nullable]
213             Printed: java.lang.String!...
214             """.trimIndent(),
215             prettyPrintTypes(
216                 supportTypeUseAnnotations = true,
217                 kotlinStyleNulls = true,
218                 skip = setOf("int", "long", "void"),
219                 extraAnnotations = listOf("@libcore.util.Nullable"),
220                 files = listOf(
221                     java(
222                         """
223                         package test.pkg;
224                         import java.util.List;
225                         import java.util.Map;
226 
227                         @SuppressWarnings("ALL")
228                         public class MyClass extends Object {
229                            public String myPlatformField1;
230                            public String[] myPlatformField2;
231                            public List<String> getList(Map<String, Number> keys) { return null; }
232                            public void method(Number number) { }
233                            public void ellipsis(String... args) { }
234                         }
235                         """
236                     ),
237                     nullableSource,
238                     nonNullSource,
239                     libcoreNonNullSource,
240                     libcoreNullableSource
241                 )
242             ).trimIndent()
243         )
244     }
245 
246     @Test
247     fun `Check other annotations than nullness annotations`() {
248         assertEquals(
249             """
250             Type: PsiClassReferenceType
251             Canonical: java.util.List<java.lang.Integer>
252             Annotated: java.util.List<java.lang.@IntRange(from=5,to=10) Integer>
253             Printed: java.util.List<java.lang.@androidx.annotation.IntRange(from=5,to=10) Integer!>!
254             """.trimIndent(),
255             prettyPrintTypes(
256                 supportTypeUseAnnotations = true,
257                 kotlinStyleNulls = true,
258                 files = listOf(
259                     java(
260                         """
261                         package test.pkg;
262                         import java.util.List;
263                         import java.util.Map;
264 
265                         @SuppressWarnings("ALL")
266                         public class MyClass extends Object {
267                            public List<java.lang.@androidx.annotation.IntRange(from=5,to=10) Integer> myRangeList;
268                         }
269                         """
270                     ),
271                     intRangeAsTypeUse
272                 ),
273                 include = setOf(
274                     "java.lang.Integer",
275                     "java.util.List<java.lang.Integer>"
276                 )
277             ).trimIndent()
278         )
279     }
280 
281     @Test
282     fun `Test negative filtering`() {
283         assertEquals(
284             """
285             Type: PsiClassReferenceType
286             Canonical: java.util.List<java.lang.Integer>
287             Annotated: java.util.List<java.lang.@IntRange(from=5,to=10) Integer>
288             Printed: java.util.List<java.lang.Integer!>!
289             """.trimIndent(),
290             prettyPrintTypes(
291                 supportTypeUseAnnotations = true,
292                 kotlinStyleNulls = true,
293                 files = listOf(
294                     java(
295                         """
296                         package test.pkg;
297                         import java.util.List;
298                         import java.util.Map;
299 
300                         @SuppressWarnings("ALL")
301                         public class MyClass extends Object {
302                            public List<java.lang.@androidx.annotation.IntRange(from=5,to=10) Integer> myRangeList;
303                         }
304                         """
305                     ),
306                     intRangeAsTypeUse
307                 ),
308                 include = setOf(
309                     "java.lang.Integer",
310                     "java.util.List<java.lang.Integer>"
311                 ),
312                 // Remove the annotations via filtering
313                 filter = Predicate { false }
314             ).trimIndent()
315         )
316     }
317 
318     @Test
319     fun `Test positive filtering`() {
320         assertEquals(
321             """
322             Type: PsiClassReferenceType
323             Canonical: java.util.List<java.lang.Integer>
324             Annotated: java.util.List<java.lang.@IntRange(from=5,to=10) Integer>
325             Printed: java.util.List<java.lang.@androidx.annotation.IntRange(from=5,to=10) Integer!>!
326             """.trimIndent(),
327             prettyPrintTypes(
328                 supportTypeUseAnnotations = true,
329                 kotlinStyleNulls = true,
330                 files = listOf(
331                     java(
332                         """
333                         package test.pkg;
334                         import java.util.List;
335                         import java.util.Map;
336 
337                         @SuppressWarnings("ALL")
338                         public class MyClass extends Object {
339                            public List<java.lang.@androidx.annotation.IntRange(from=5,to=10) Integer> myRangeList;
340                         }
341                         """
342                     ),
343                     intRangeAsTypeUse
344                 ),
345                 include = setOf(
346                     "java.lang.Integer",
347                     "java.util.List<java.lang.Integer>"
348                 ),
349                 // Include the annotations via filtering
350                 filter = Predicate { true }
351             ).trimIndent()
352         )
353     }
354 
355     // @Test
356     fun `Test primitives`() {
357         assertEquals(
358             """
359             Type: PsiPrimitiveType
360             Canonical: int
361             Printed: int
362 
363             Type: PsiPrimitiveType
364             Canonical: long
365             Printed: long
366 
367             Type: PsiPrimitiveType
368             Canonical: void
369             Printed: void
370 
371             Type: PsiPrimitiveType
372             Canonical: int
373             Annotated: @IntRange(from=5,to=10) int
374             Printed: @androidx.annotation.IntRange(from=5,to=10) int
375             """.trimIndent(),
376             prettyPrintTypes(
377                 supportTypeUseAnnotations = true,
378                 kotlinStyleNulls = true,
379                 files = listOf(
380                     java(
381                         """
382                         package test.pkg;
383 
384                         @SuppressWarnings("ALL")
385                         public class MyClass extends Object {
386                            public void foo() { }
387                            public int myPrimitiveField;
388                            public long myPrimitiveField2;
389                            public void foo(@androidx.annotation.IntRange(from=5,to=10) int foo) { }
390                         }
391                         """
392                     ),
393                     nullableSource,
394                     nonNullSource,
395                     libcoreNonNullSource, // allows TYPE_USE
396                     libcoreNullableSource,
397                     intRangeAsTypeUse
398                 )
399             ).trimIndent()
400         )
401     }
402 
403     // @Test
404     fun `Test primitives with type use turned off`() {
405         assertEquals(
406             """
407             Type: PsiPrimitiveType
408             Canonical: int
409             Printed: int
410 
411             Type: PsiPrimitiveType
412             Canonical: long
413             Printed: long
414 
415             Type: PsiPrimitiveType
416             Canonical: void
417             Printed: void
418 
419             Type: PsiPrimitiveType
420             Canonical: int
421             Annotated: @IntRange(from=5,to=10) int
422             Printed: int
423             """.trimIndent(),
424             prettyPrintTypes(
425                 supportTypeUseAnnotations = false,
426                 kotlinStyleNulls = true,
427                 files = listOf(
428                     java(
429                         """
430                         package test.pkg;
431 
432                         @SuppressWarnings("ALL")
433                         public class MyClass extends Object {
434                            public void foo() { }
435                            public int myPrimitiveField;
436                            public long myPrimitiveField2;
437                            public void foo(@androidx.annotation.IntRange(from=5,to=10) int foo) { }
438                         }
439                         """
440                     ),
441                     nullableSource,
442                     nonNullSource,
443                     libcoreNonNullSource, // allows TYPE_USE
444                     libcoreNullableSource,
445                     intRangeAsTypeUse
446                 )
447             ).trimIndent()
448         )
449     }
450 
451     @Test
452     fun `Test arrays`() {
453         assertEquals(
454             """
455             Type: PsiArrayType
456             Canonical: java.lang.String[]
457             Printed: java.lang.String![]!
458 
459             Type: PsiArrayType
460             Canonical: java.lang.String[][]
461             Printed: java.lang.String![]![]!
462 
463             Type: PsiArrayType
464             Canonical: java.lang.String[]
465             Annotated: java.lang.@Nullable String @Nullable []
466             Printed: java.lang.String?[]?
467 
468             Type: PsiArrayType
469             Canonical: java.lang.String[]
470             Annotated: java.lang.@NonNull String @Nullable []
471             Printed: java.lang.String[]?
472 
473             Type: PsiArrayType
474             Canonical: java.lang.String[]
475             Annotated: java.lang.@Nullable String @NonNull []
476             Printed: java.lang.String?[]
477             """.trimIndent(),
478             prettyPrintTypes(
479                 supportTypeUseAnnotations = true,
480                 kotlinStyleNulls = true,
481                 files = listOf(
482                     java(
483                         """
484                         package test.pkg;
485 
486                         @SuppressWarnings("ALL")
487                         public class MyClass extends Object {
488                            public String[] myArray1;
489                            public String[][] myArray2;
490                            public java.lang.@libcore.util.Nullable String @libcore.util.Nullable [] array1;
491                            public java.lang.@libcore.util.NonNull String @libcore.util.Nullable [] array2;
492                            public java.lang.@libcore.util.Nullable String @libcore.util.NonNull [] array3;
493                         }
494                         """
495                     ),
496                     libcoreNonNullSource,
497                     libcoreNullableSource
498                 ),
499                 skip = setOf("int", "java.lang.String")
500             ).trimIndent()
501         )
502     }
503 
504     @Test
505     fun `Test ellipsis types`() {
506         assertEquals(
507             """
508             Type: PsiEllipsisType
509             Canonical: java.lang.String...
510             Printed: java.lang.String!...
511 
512             Type: PsiEllipsisType
513             Canonical: java.lang.String...
514             Annotated: java.lang.@Nullable String @NonNull ...
515             Merged: [@NonNull]
516             Printed: java.lang.String?...
517             """.trimIndent(),
518             prettyPrintTypes(
519                 supportTypeUseAnnotations = true,
520                 kotlinStyleNulls = true,
521                 files = listOf(
522                     java(
523                         """
524                         package test.pkg;
525                         import java.util.List;
526                         import java.util.Map;
527 
528                         @SuppressWarnings("ALL")
529                         public class MyClass extends Object {
530                             // Ellipsis type
531                            public void ellipsis1(String... args) { }
532                            public void ellipsis2(java.lang.@libcore.util.Nullable String @libcore.util.NonNull ... args) { }
533                         }
534                         """
535                     ),
536                     libcoreNonNullSource,
537                     libcoreNullableSource
538                 ),
539                 skip = setOf("void", "int", "java.lang.String")
540             ).trimIndent()
541         )
542     }
543 
544     @Test
545     fun `Test wildcard type`() {
546         assertEquals(
547             """
548             Type: PsiClassReferenceType
549             Canonical: T
550             Annotated: @NonNull T
551             Merged: [@NonNull]
552             Printed: T
553 
554             Type: PsiWildcardType
555             Canonical: ? super T
556             Printed: ? super T
557 
558             Type: PsiClassReferenceType
559             Canonical: T
560             Printed: T!
561 
562             Type: PsiClassReferenceType
563             Canonical: java.util.Collection<? extends T>
564             Annotated: java.util.Collection<? extends @Nullable T>
565             Merged: [@Nullable]
566             Printed: java.util.Collection<? extends T?>?
567 
568             Type: PsiWildcardType
569             Canonical: ? extends T
570             Annotated: ? extends @Nullable T
571             Printed: ? extends T?
572 
573             Type: PsiClassReferenceType
574             Canonical: T
575             Annotated: @Nullable T
576             Merged: [@Nullable]
577             Printed: T?
578             """.trimIndent(),
579             prettyPrintTypes(
580                 supportTypeUseAnnotations = true,
581                 kotlinStyleNulls = true,
582                 files = listOf(
583                     java(
584                         """
585                         package test.pkg;
586                         import java.util.List;
587                         import java.util.Map;
588 
589                         @SuppressWarnings("ALL")
590                         public class MyClass extends Object {
591                             // Intersection type
592                             @libcore.util.NonNull public static <T extends java.lang.String & java.lang.Comparable<? super T>> T foo(@libcore.util.Nullable java.util.Collection<? extends @libcore.util.Nullable T> coll) { return null; }
593                         }
594                         """
595                     ),
596                     libcoreNonNullSource,
597                     libcoreNullableSource
598                 ),
599                 skip = setOf("int")
600             ).trimIndent()
601         )
602     }
603 
604     @Test
605     fun `Test primitives in arrays cannot be null`() {
606         assertEquals(
607             """
608             Type: PsiClassReferenceType
609             Canonical: java.util.List<int[]>
610             Printed: java.util.List<int[]!>!
611 
612             Type: PsiClassReferenceType
613             Canonical: java.util.List<boolean[][]>
614             Printed: java.util.List<boolean[]![]!>!
615             """.trimIndent(),
616             prettyPrintTypes(
617                 supportTypeUseAnnotations = true,
618                 kotlinStyleNulls = true,
619                 files = listOf(
620                     java(
621                         """
622                         package test.pkg;
623                         import java.util.List;
624 
625                         @SuppressWarnings("ALL")
626                         public class MyClass extends Object {
627                             public List<int[]> ints;
628                             public List<boolean[][]> booleans;
629                         }
630                         """
631                     )
632                 ),
633                 skip = setOf("int")
634             ).trimIndent()
635         )
636     }
637 
638     @Test
639     fun `Test kotlin`() {
640         assertEquals(
641             """
642             Type: PsiClassReferenceType
643             Canonical: java.lang.String
644             Merged: [@NonNull]
645             Printed: java.lang.String
646 
647             Type: PsiClassReferenceType
648             Canonical: java.util.Map<java.lang.String,java.lang.String>
649             Merged: [@Nullable]
650             Printed: java.util.Map<java.lang.String,java.lang.String>?
651 
652             Type: PsiPrimitiveType
653             Canonical: void
654             Printed: void
655 
656             Type: PsiPrimitiveType
657             Canonical: int
658             Merged: [@NonNull]
659             Printed: int
660 
661             Type: PsiClassReferenceType
662             Canonical: java.lang.Integer
663             Merged: [@Nullable]
664             Printed: java.lang.Integer?
665 
666             Type: PsiEllipsisType
667             Canonical: java.lang.String...
668             Merged: [@NonNull]
669             Printed: java.lang.String!...
670             """.trimIndent(),
671             prettyPrintTypes(
672                 supportTypeUseAnnotations = true,
673                 kotlinStyleNulls = true,
674                 files = listOf(
675                     kotlin(
676                         """
677                         package test.pkg
678                         class Foo {
679                             val foo1: String = "test1"
680                             val foo2: String? = "test1"
681                             val foo3: MutableMap<String?, String>? = null
682                             fun method1(int: Int = 42,
683                                 int2: Int? = null,
684                                 byte: Int = 2 * 21,
685                                 str: String = "hello " + "world",
686                                 vararg args: String) { }
687                         }
688                         """
689                     )
690                 )
691             ).trimIndent()
692         )
693     }
694 
695     @Test
696     fun `Test inner class references`() {
697         assertEquals(
698             """
699             Type: PsiClassReferenceType
700             Canonical: test.pkg.MyClass.MyInner
701             Printed: test.pkg.MyClass.MyInner!
702             """.trimIndent(),
703             prettyPrintTypes(
704                 supportTypeUseAnnotations = true,
705                 kotlinStyleNulls = true,
706                 files = listOf(
707                     java(
708                         """
709                         package test.pkg;
710                         import java.util.List;
711                         import java.util.Map;
712 
713                         @SuppressWarnings("ALL")
714                         public class MyClass extends Object {
715                            public test.pkg.MyClass.MyInner getObserver() { return null; }
716 
717                            public class MyInner {
718                            }
719                         }
720                         """
721                     )
722                 ),
723                 skip = setOf("void", "int", "java.lang.String")
724             ).trimIndent()
725         )
726     }
727 
728     @Test
729     fun `Test type bounds`() {
730         assertEquals(
731             """
732             Type: PsiClassReferenceType
733             Canonical: java.util.List<? extends java.lang.Number>
734             Printed: java.util.List<? extends java.lang.Number>!
735 
736             Type: PsiWildcardType
737             Canonical: ? extends java.lang.Number
738             Printed: ? extends java.lang.Number
739 
740             Type: PsiClassReferenceType
741             Canonical: java.lang.Number
742             Printed: java.lang.Number!
743 
744             Type: PsiClassReferenceType
745             Canonical: java.util.Map<? extends java.lang.Number,? super java.lang.Number>
746             Printed: java.util.Map<? extends java.lang.Number,? super java.lang.Number>!
747 
748             Type: PsiWildcardType
749             Canonical: ? super java.lang.Number
750             Printed: ? super java.lang.Number
751             """.trimIndent(),
752             prettyPrintTypes(
753                 supportTypeUseAnnotations = true,
754                 kotlinStyleNulls = true,
755                 files = listOf(
756                     java(
757                         """
758                         package test.pkg;
759                         import java.util.List;
760                         import java.util.Map;
761 
762                         @SuppressWarnings("ALL")
763                         public class MyClass extends Object {
764                            public void foo1(List<? extends Number> arg) { }
765                            public void foo2(Map<? extends Number, ? super Number> arg) { }
766                         }
767                         """
768                     )
769                 ),
770                 skip = setOf("void")
771             ).trimIndent()
772         )
773     }
774 
775     data class Entry(
776         val type: PsiType,
777         val elementAnnotations: List<AnnotationItem>?,
778         val canonical: String,
779         val annotated: String,
780         val printed: String
781     )
782 
783     private fun prettyPrintTypes(
784         files: List<TestFile>,
785         filter: Predicate<Item>? = null,
786         kotlinStyleNulls: Boolean = true,
787         supportTypeUseAnnotations: Boolean = true,
788         skip: Set<String> = emptySet(),
789         include: Set<String> = emptySet(),
790         extraAnnotations: List<String> = emptyList()
791     ): String {
792         val dir = createProject(*files.toTypedArray())
793         val sourcePath = listOf(File(dir, "src"))
794 
795         val sourceFiles = mutableListOf<File>()
796         fun addFiles(file: File) {
797             if (file.isFile) {
798                 sourceFiles.add(file)
799             } else {
800                 for (child in file.listFiles()) {
801                     addFiles(child)
802                 }
803             }
804         }
805         addFiles(dir)
806 
807         val classPath = mutableListOf<File>()
808         val classPathProperty: String = System.getProperty("java.class.path")
809         for (path in classPathProperty.split(':')) {
810             val file = File(path)
811             if (file.isFile) {
812                 classPath.add(file)
813             }
814         }
815         classPath.add(getAndroidJar())
816 
817         options = Options(arrayOf(ARG_CLASS_PATH, getAndroidJar().path))
818         // TestDriver#check normally sets this for all the other tests
819         val codebase = parseSources(
820             sourceFiles, "test project",
821             sourcePath = sourcePath, classpath = classPath
822         )
823 
824         val results = LinkedHashMap<String, Entry>()
825         fun handleType(type: PsiType, annotations: List<AnnotationItem> = emptyList()) {
826             val key = type.getCanonicalText(true)
827             if (results.contains(key)) {
828                 return
829             }
830             val canonical = type.getCanonicalText(false)
831             if (skip.contains(key) || skip.contains(canonical)) {
832                 return
833             }
834             if (include.isNotEmpty() && !(include.contains(key) || include.contains(canonical))) {
835                 return
836             }
837 
838             val mapAnnotations = false
839             val printer = PsiTypePrinter(codebase, filter, mapAnnotations, kotlinStyleNulls, supportTypeUseAnnotations)
840 
841             var mergeAnnotations: MutableList<AnnotationItem>? = null
842             if (extraAnnotations.isNotEmpty()) {
843                 val list = mutableListOf<AnnotationItem>()
844                 for (annotation in extraAnnotations) {
845                     list.add(codebase.createAnnotation(annotation))
846                 }
847                 mergeAnnotations = list
848             }
849             if (annotations.isNotEmpty()) {
850                 val list = mutableListOf<AnnotationItem>()
851                 for (annotation in annotations) {
852                     list.add(annotation)
853                 }
854                 if (mergeAnnotations == null) {
855                     mergeAnnotations = list
856                 } else {
857                     mergeAnnotations.addAll(list)
858                 }
859             }
860 
861             val pretty = printer.getAnnotatedCanonicalText(type, mergeAnnotations)
862             results[key] = Entry(type, mergeAnnotations, canonical, key, pretty)
863         }
864 
865         for (unit in codebase.units) {
866             unit.toUElement()?.accept(object : AbstractUastVisitor() {
867                 override fun visitMethod(node: UMethod): Boolean {
868                     handle(node.returnType, node.uAnnotations)
869 
870                     // Visit all the type elements in the method: this helps us pick up
871                     // the type parameter lists for example which contains some interesting
872                     // stuff such as type bounds
873                     val psi = node.sourcePsi
874                     psi?.accept(object : JavaRecursiveElementVisitor() {
875                         override fun visitTypeElement(type: PsiTypeElement) {
876                             handle(type.type, psiAnnotations = type.annotations)
877                             super.visitTypeElement(type)
878                         }
879                     })
880                     return super.visitMethod(node)
881                 }
882 
883                 override fun visitVariable(node: UVariable): Boolean {
884                     handle(node.type, node.uAnnotations)
885                     return super.visitVariable(node)
886                 }
887 
888                 private fun handle(
889                     type: PsiType?,
890                     uastAnnotations: List<UAnnotation> = emptyList(),
891                     psiAnnotations: Array<PsiAnnotation> = emptyArray()
892                 ) {
893                     type ?: return
894 
895                     val annotations = mutableListOf<AnnotationItem>()
896                     for (annotation in uastAnnotations) {
897                         annotations.add(UAnnotationItem.create(codebase, annotation))
898                     }
899                     for (annotation in psiAnnotations) {
900                         annotations.add(PsiAnnotationItem.create(codebase, annotation))
901                     }
902 
903                     handleType(type, annotations)
904                 }
905 
906                 override fun visitTypeReferenceExpression(node: UTypeReferenceExpression): Boolean {
907                     handleType(node.type)
908                     return super.visitTypeReferenceExpression(node)
909                 }
910             })
911         }
912 
913         val writer = StringWriter()
914         val printWriter = PrintWriter(writer)
915 
916         results.keys.forEach { key ->
917             val cleanKey = key.replace("libcore.util.", "").replace("androidx.annotation.", "")
918             val entry = results[key]!!
919             val string = entry.printed
920             val type = entry.type
921             val typeName = type.javaClass.simpleName
922             val canonical = entry.canonical
923             printWriter.printf("Type: %s\n", typeName)
924             printWriter.printf("Canonical: %s\n", canonical)
925             if (cleanKey != canonical) {
926                 printWriter.printf("Annotated: %s\n", cleanKey)
927             }
928             val elementAnnotations = entry.elementAnnotations
929             if (elementAnnotations != null && elementAnnotations.isNotEmpty()) {
930                 printWriter.printf(
931                     "Merged: %s\n",
932                     elementAnnotations.toString()
933                         .replace("androidx.annotation.", "")
934                         .replace("libcore.util.", "")
935                 )
936             }
937             printWriter.printf("Printed: %s\n\n", string)
938         }
939 
940         return writer.toString().removeSuffix("\n\n")
941     }
942 
943     // TYPE_USE version of intRangeAnnotationSource
944     private val intRangeAsTypeUse = java(
945         """
946         package androidx.annotation;
947         import java.lang.annotation.*;
948         import static java.lang.annotation.ElementType.*;
949         import static java.lang.annotation.RetentionPolicy.SOURCE;
950         @Retention(SOURCE)
951         @Target({METHOD,PARAMETER,FIELD,LOCAL_VARIABLE,ANNOTATION_TYPE,TYPE_USE})
952         public @interface IntRange {
953             long from() default Long.MIN_VALUE;
954             long to() default Long.MAX_VALUE;
955         }
956         """
957     ).indented()
958 }
959