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