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 }