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