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.Compatibility 21 import com.android.tools.metalava.DriverTest 22 import com.android.tools.metalava.compatibility 23 import com.android.tools.metalava.libcoreNonNullSource 24 import com.android.tools.metalava.libcoreNullableSource 25 import com.android.tools.metalava.model.AnnotationItem 26 import com.android.tools.metalava.model.Item 27 import com.android.tools.metalava.nonNullSource 28 import com.android.tools.metalava.nullableSource 29 import com.android.tools.metalava.parseSources 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 classPath.add(getPlatformFile("android.jar")) 812 813 // TestDriver#check normally sets this for all the other tests 814 compatibility = Compatibility(false) 815 val codebase = parseSources( 816 sourceFiles, "test project", 817 sourcePath = sourcePath, classpath = classPath 818 ) 819 820 val results = LinkedHashMap<String, Entry>() 821 fun handleType(type: PsiType, annotations: List<AnnotationItem> = emptyList()) { 822 val key = type.getCanonicalText(true) 823 if (results.contains(key)) { 824 return 825 } 826 val canonical = type.getCanonicalText(false) 827 if (skip.contains(key) || skip.contains(canonical)) { 828 return 829 } 830 if (include.isNotEmpty() && !(include.contains(key) || include.contains(canonical))) { 831 return 832 } 833 834 val mapAnnotations = false 835 val printer = PsiTypePrinter(codebase, filter, mapAnnotations, kotlinStyleNulls, supportTypeUseAnnotations) 836 837 var mergeAnnotations: MutableList<AnnotationItem>? = null 838 if (extraAnnotations.isNotEmpty()) { 839 val list = mutableListOf<AnnotationItem>() 840 for (annotation in extraAnnotations) { 841 list.add(codebase.createAnnotation(annotation)) 842 } 843 mergeAnnotations = list 844 } 845 if (annotations.isNotEmpty()) { 846 val list = mutableListOf<AnnotationItem>() 847 for (annotation in annotations) { 848 list.add(annotation) 849 } 850 if (mergeAnnotations == null) { 851 mergeAnnotations = list 852 } else { 853 mergeAnnotations.addAll(list) 854 } 855 } 856 857 val pretty = printer.getAnnotatedCanonicalText(type, mergeAnnotations) 858 results[key] = Entry(type, mergeAnnotations, canonical, key, pretty) 859 } 860 861 for (unit in codebase.units) { 862 unit.toUElement()?.accept(object : AbstractUastVisitor() { 863 override fun visitMethod(node: UMethod): Boolean { 864 handle(node.returnType, node.uAnnotations) 865 866 // Visit all the type elements in the method: this helps us pick up 867 // the type parameter lists for example which contains some interesting 868 // stuff such as type bounds 869 val psi = node.sourcePsi 870 psi?.accept(object : JavaRecursiveElementVisitor() { 871 override fun visitTypeElement(type: PsiTypeElement) { 872 handle(type.type, psiAnnotations = type.annotations) 873 super.visitTypeElement(type) 874 } 875 }) 876 return super.visitMethod(node) 877 } 878 879 override fun visitVariable(node: UVariable): Boolean { 880 handle(node.type, node.uAnnotations) 881 return super.visitVariable(node) 882 } 883 884 private fun handle( 885 type: PsiType?, 886 uastAnnotations: List<UAnnotation> = emptyList(), 887 psiAnnotations: Array<PsiAnnotation> = emptyArray() 888 ) { 889 type ?: return 890 891 val annotations = mutableListOf<AnnotationItem>() 892 for (annotation in uastAnnotations) { 893 annotations.add(UAnnotationItem.create(codebase, annotation)) 894 } 895 for (annotation in psiAnnotations) { 896 annotations.add(PsiAnnotationItem.create(codebase, annotation)) 897 } 898 899 handleType(type, annotations) 900 } 901 902 override fun visitTypeReferenceExpression(node: UTypeReferenceExpression): Boolean { 903 handleType(node.type) 904 return super.visitTypeReferenceExpression(node) 905 } 906 }) 907 } 908 909 val writer = StringWriter() 910 val printWriter = PrintWriter(writer) 911 912 results.keys.forEach { key -> 913 val cleanKey = key.replace("libcore.util.", "").replace("androidx.annotation.", "") 914 val entry = results[key]!! 915 val string = entry.printed 916 val type = entry.type 917 val typeName = type.javaClass.simpleName 918 val canonical = entry.canonical 919 printWriter.printf("Type: %s\n", typeName) 920 printWriter.printf("Canonical: %s\n", canonical) 921 if (cleanKey != canonical) { 922 printWriter.printf("Annotated: %s\n", cleanKey) 923 } 924 val elementAnnotations = entry.elementAnnotations 925 if (elementAnnotations != null && elementAnnotations.isNotEmpty()) { 926 printWriter.printf("Merged: %s\n", elementAnnotations.toString() 927 .replace("androidx.annotation.", "") 928 .replace("libcore.util.", "")) 929 } 930 printWriter.printf("Printed: %s\n\n", string) 931 } 932 933 return writer.toString().removeSuffix("\n\n") 934 } 935 936 // TYPE_USE version of intRangeAnnotationSource 937 private val intRangeAsTypeUse = java( 938 """ 939 package androidx.annotation; 940 import java.lang.annotation.*; 941 import static java.lang.annotation.ElementType.*; 942 import static java.lang.annotation.RetentionPolicy.SOURCE; 943 @Retention(SOURCE) 944 @Target({METHOD,PARAMETER,FIELD,LOCAL_VARIABLE,ANNOTATION_TYPE,TYPE_USE}) 945 public @interface IntRange { 946 long from() default Long.MIN_VALUE; 947 long to() default Long.MAX_VALUE; 948 } 949 """ 950 ).indented() 951 } 952