1 /* <lambda>null2 * Copyright (C) 2017 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 18 19 import com.android.tools.metalava.doclava1.ApiPredicate 20 import com.android.tools.metalava.doclava1.Errors 21 import com.android.tools.metalava.model.AnnotationAttributeValue 22 import com.android.tools.metalava.model.ClassItem 23 import com.android.tools.metalava.model.Codebase 24 import com.android.tools.metalava.model.ConstructorItem 25 import com.android.tools.metalava.model.FieldItem 26 import com.android.tools.metalava.model.Item 27 import com.android.tools.metalava.model.MethodItem 28 import com.android.tools.metalava.model.PackageItem 29 import com.android.tools.metalava.model.PackageList 30 import com.android.tools.metalava.model.ParameterItem 31 import com.android.tools.metalava.model.TypeItem 32 import com.android.tools.metalava.model.psi.EXPAND_DOCUMENTATION 33 import com.android.tools.metalava.model.visitors.ApiVisitor 34 import com.android.tools.metalava.model.visitors.ItemVisitor 35 import java.util.ArrayList 36 import java.util.HashMap 37 import java.util.HashSet 38 import java.util.function.Predicate 39 40 /** 41 * The [ApiAnalyzer] is responsible for walking over the various 42 * classes and members and compute visibility etc of the APIs 43 */ 44 class ApiAnalyzer( 45 /** The code to analyze */ 46 private val codebase: Codebase 47 ) { 48 /** All packages in the API */ 49 private val packages: PackageList = codebase.getPackages() 50 51 fun computeApi() { 52 if (codebase.trustedApi()) { 53 // The codebase is already an API; no consistency checks to be performed 54 return 55 } 56 57 // Apply options for packages that should be hidden 58 hidePackages() 59 skipEmitPackages() 60 61 // Propagate visibility down into individual elements -- if a class is hidden, 62 // then the methods and fields are hidden etc 63 propagateHiddenRemovedAndDocOnly(false) 64 } 65 66 fun addConstructors(filter: Predicate<Item>) { 67 // Let's say I have 68 // class GrandParent { public GrandParent(int) {} } 69 // class Parent { Parent(int) {} } 70 // class Child { public Child(int) {} } 71 // 72 // Here Parent's constructor is not public. For normal stub generation I'd end up with this: 73 // class GrandParent { public GrandParent(int) {} } 74 // class Parent { } 75 // class Child { public Child(int) {} } 76 // 77 // This doesn't compile - Parent can't have a default constructor since there isn't 78 // one for it to invoke on GrandParent. 79 // 80 // I can generate a fake constructor instead, such as 81 // Parent() { super(0); } 82 // 83 // But it's hard to do this lazily; what if I'm generating the Child class first? 84 // Therefore, we'll instead walk over the hierarchy and insert these constructors 85 // into the Item hierarchy such that code generation can find them. 86 // 87 // (We also need to handle the throws list, so we can't just unconditionally 88 // insert package private constructors 89 // 90 // To do this right I really need to process super constructors before the classes 91 // depending on them. 92 93 // Mark all classes that are the super class of some other class: 94 val allClasses = packages.allClasses().filter { filter.test(it) } 95 96 codebase.clearTags() 97 allClasses.forEach { cls -> 98 cls.superClass()?.tag = true 99 } 100 101 val leafClasses = allClasses.filter { !it.tag }.toList() 102 103 // Now walk through all the leaf classes, and walk up the super hierarchy 104 // and recursively add constructors; we'll do it recursively to make sure that 105 // the superclass has had its constructors initialized first (such that we can 106 // match the parameter lists and throws signatures), and we use the tag fields 107 // to avoid looking at all the internal classes more than once. 108 codebase.clearTags() 109 leafClasses 110 // Filter classes by filter here to not waste time in hidden packages 111 .filter { filter.test(it) } 112 .forEach { addConstructors(it, filter, true) } 113 } 114 115 /** 116 * Handle computing constructor hierarchy. We'll be setting several attributes: 117 * [ClassItem.defaultConstructor] : The default constructor to invoke in this 118 * class from subclasses. **NOTE**: This constructor may not be part of 119 * the [ClassItem.constructors] list, e.g. for package private default constructors 120 * we've inserted (because there were no public constructors or constructors not 121 * using hidden parameter types.) 122 * 123 * If we can find a public constructor we'll put that here instead. 124 * 125 * [ConstructorItem.superConstructor] The default constructor to invoke. If set, 126 * use this rather than the [ClassItem.defaultConstructor]. 127 * 128 * [ClassItem.hasPrivateConstructor] Set if this class has one or more private 129 * constructors. 130 * 131 * [Item.tag] : mark for avoiding repeated iteration of internal item nodes 132 * 133 * 134 */ 135 private fun addConstructors(cls: ClassItem, filter: Predicate<Item>, isLeaf: Boolean) { 136 // What happens if we have 137 // package foo: 138 // public class A { public A(int) } 139 // package bar 140 // public class B extends A { public B(int) } 141 // If I just try inserting package private constructors here things will NOT work: 142 // package foo: 143 // public class A { public A(int); A() {} } 144 // package bar 145 // public class B extends A { public B(int); B() } 146 // because A <() is not accessible from B() -- it's outside the same package. 147 // 148 // So, I'll need to model the real constructors for all the scenarios where that 149 // works. 150 // 151 // The remaining challenge is that there will be some gaps: when I don't have 152 // a default constructor, subclass constructors will have to have an explicit 153 // super(args) call to pick the parent constructor to use. And which one? 154 // It generally doesn't matter; just pick one, but unfortunately, the super 155 // constructor can throw exceptions, and in that case the subclass constructor 156 // must also throw all those constructors (you can't surround a super call 157 // with try/catch.) Luckily, the source code already needs to do this to 158 // compile, so we can just use the same constructor as the super call. 159 // But there are two cases we have to deal with: 160 // (1) the constructor doesn't call a super constructor; it calls another 161 // constructor on this class. 162 // (2) the super constructor it *does* call isn't available. 163 // 164 // For (1), this means that our stub code generator should be prepared to 165 // handle both super- and this- dispatches; we'll handle this by pointing 166 // it to the constructor to use, and it checks to see if the containing class 167 // for the constructor is the same to decide whether to emit "this" or "super". 168 169 if (cls.tag || !cls.isClass()) { // Don't add constructors to interfaces, enums, annotations, etc 170 return 171 } 172 173 // First handle its super class hierarchy to make sure that we've 174 // already constructed super classes 175 val superClass = cls.filteredSuperclass(filter) 176 superClass?.let { it -> addConstructors(it, filter, false) } 177 cls.tag = true 178 179 if (superClass != null) { 180 val superDefaultConstructor = superClass.defaultConstructor 181 if (superDefaultConstructor != null) { 182 val constructors = cls.constructors() 183 for (constructor in constructors) { 184 val superConstructor = constructor.superConstructor 185 if (superConstructor == null || 186 (superConstructor.containingClass() != superClass && 187 superConstructor.containingClass() != cls) 188 ) { 189 constructor.superConstructor = superDefaultConstructor 190 } 191 } 192 } 193 } 194 195 // Find default constructor, if one doesn't exist 196 if (!isLeaf || cls.hasPrivateConstructor || cls.constructors().isNotEmpty()) { 197 val constructors = cls.constructors() 198 for (constructor in constructors) { 199 if (constructor.parameters().isEmpty() && constructor.isPublic && !constructor.hidden) { 200 cls.defaultConstructor = constructor 201 return 202 } 203 } 204 205 if (!constructors.isEmpty()) { 206 // Try to pick the constructor, sorting first by "matches filter", then by 207 // uses available types, then by fewest throwables, then fewest parameters, 208 // then based on order in listFilter.test(cls) 209 val first = constructors.first() 210 val best = 211 if (constructors.size > 1) { 212 constructors.foldRight(first) { current, next -> pickBest(current, next, filter) } 213 } else { 214 first 215 } 216 217 if (cls.filteredConstructors(filter).contains(best)) { 218 cls.defaultConstructor = best 219 return 220 } 221 222 if (!referencesExcludedType(best, filter)) { 223 cls.defaultConstructor = best 224 best.mutableModifiers().setPackagePrivate(true) 225 best.hidden = false 226 best.docOnly = false 227 return 228 } 229 } 230 231 // No constructors, yet somebody extends this (or private constructor): we have to invent one, such that 232 // subclasses can dispatch to it in the stub files etc 233 cls.defaultConstructor = cls.createDefaultConstructor().also { 234 it.mutableModifiers().setPackagePrivate(true) 235 it.hidden = false 236 it.superConstructor = superClass?.defaultConstructor 237 } 238 } 239 } 240 241 private fun referencesExcludedType(constructor: ConstructorItem, filter: Predicate<Item>): Boolean { 242 // Checks parameter types and throws types 243 for (parameter in constructor.parameters()) { 244 val type = parameter.type() 245 if (type.referencesExcludedType(filter)) { 246 return true 247 } 248 } 249 for (cls in constructor.throwsTypes()) { 250 if (!filter.test(cls)) { 251 return true 252 } 253 } 254 255 return false 256 } 257 258 // TODO: Annotation test: @ParameterName, if present, must be supplied on *all* the arguments! 259 // Warn about @DefaultValue("null"); they probably meant @DefaultNull 260 // Supplying default parameter in override is not allowed! 261 262 private fun pickBest( 263 current: ConstructorItem, 264 next: ConstructorItem, 265 filter: Predicate<Item> 266 ): ConstructorItem { 267 val currentMatchesFilter = filter.test(current) 268 val nextMatchesFilter = filter.test(next) 269 if (currentMatchesFilter != nextMatchesFilter) { 270 return if (currentMatchesFilter) { 271 current 272 } else { 273 next 274 } 275 } 276 277 val currentUsesAvailableTypes = !referencesExcludedType(current, filter) 278 val nextUsesAvailableTypes = !referencesExcludedType(next, filter) 279 if (currentUsesAvailableTypes != nextUsesAvailableTypes) { 280 return if (currentUsesAvailableTypes) { 281 current 282 } else { 283 next 284 } 285 } 286 287 val currentThrowsCount = current.throwsTypes().size 288 val nextThrowsCount = next.throwsTypes().size 289 290 return if (currentThrowsCount < nextThrowsCount) { 291 current 292 } else if (currentThrowsCount > nextThrowsCount) { 293 next 294 } else { 295 val currentParameterCount = current.parameters().size 296 val nextParameterCount = next.parameters().size 297 if (currentParameterCount <= nextParameterCount) { 298 current 299 } else next 300 } 301 } 302 303 fun generateInheritedStubs(filterEmit: Predicate<Item>, filterReference: Predicate<Item>) { 304 // When analyzing libraries we may discover some new classes during traversal; these aren't 305 // part of the API but may be super classes or interfaces; these will then be added into the 306 // package class lists, which could trigger a concurrent modification, so create a snapshot 307 // of the class list and iterate over it: 308 val allClasses = packages.allClasses().toList() 309 allClasses.forEach { 310 if (filterEmit.test(it)) { 311 generateInheritedStubs(it, filterEmit, filterReference) 312 } 313 } 314 } 315 316 private fun generateInheritedStubs(cls: ClassItem, filterEmit: Predicate<Item>, filterReference: Predicate<Item>) { 317 if (!cls.isClass()) return 318 if (cls.superClass() == null) return 319 val superClasses: Sequence<ClassItem> = generateSequence(cls.superClass()) { it.superClass() } 320 val hiddenSuperClasses: Sequence<ClassItem> = 321 superClasses.filter { !filterReference.test(it) && !it.isJavaLangObject() } 322 323 if (hiddenSuperClasses.none()) { // not missing any implementation methods 324 return 325 } 326 327 addInheritedStubsFrom(cls, hiddenSuperClasses, superClasses, filterEmit, filterReference) 328 addInheritedInterfacesFrom(cls, hiddenSuperClasses, filterReference) 329 } 330 331 private fun addInheritedInterfacesFrom( 332 cls: ClassItem, 333 hiddenSuperClasses: Sequence<ClassItem>, 334 filterReference: Predicate<Item> 335 ) { 336 var interfaceTypes: MutableList<TypeItem>? = null 337 var interfaceTypeClasses: MutableList<ClassItem>? = null 338 for (hiddenSuperClass in hiddenSuperClasses) { 339 for (hiddenInterface in hiddenSuperClass.interfaceTypes()) { 340 val hiddenInterfaceClass = hiddenInterface.asClass() 341 if (filterReference.test(hiddenInterfaceClass ?: continue)) { 342 if (interfaceTypes == null) { 343 interfaceTypes = cls.interfaceTypes().toMutableList() 344 interfaceTypeClasses = 345 interfaceTypes.asSequence().map { it.asClass() }.filterNotNull().toMutableList() 346 if (cls.isInterface()) { 347 cls.superClass()?.let { interfaceTypeClasses.add(it) } 348 } 349 cls.setInterfaceTypes(interfaceTypes) 350 } 351 if (interfaceTypeClasses!!.any { it == hiddenInterfaceClass }) { 352 continue 353 } 354 355 interfaceTypeClasses.add(hiddenInterfaceClass) 356 357 if (hiddenInterfaceClass.hasTypeVariables()) { 358 val mapping = cls.mapTypeVariables(hiddenSuperClass) 359 if (mapping.isNotEmpty()) { 360 val mappedType: TypeItem = hiddenInterface.convertType(mapping, cls) 361 interfaceTypes.add(mappedType) 362 continue 363 } 364 } 365 366 interfaceTypes.add(hiddenInterface) 367 } 368 } 369 } 370 } 371 372 private fun addInheritedStubsFrom( 373 cls: ClassItem, 374 hiddenSuperClasses: Sequence<ClassItem>, 375 superClasses: Sequence<ClassItem>, 376 filterEmit: Predicate<Item>, 377 filterReference: Predicate<Item> 378 ) { 379 380 // Also generate stubs for any methods we would have inherited from abstract parents 381 // All methods from super classes that (1) aren't overridden in this class already, and 382 // (2) are overriding some method that is in a public interface accessible from this class. 383 val interfaces: Set<TypeItem> = cls.allInterfaceTypes(filterReference).asSequence().toSet() 384 385 // Note that we can't just call method.superMethods() to and see whether any of their containing 386 // classes are among our target APIs because it's possible that the super class doesn't actually 387 // implement the interface, but still provides a matching signature for the interface. 388 // Instead we'll look through all of our interface methods and look for potential overrides 389 val interfaceNames = mutableMapOf<String, MutableList<MethodItem>>() 390 for (interfaceType in interfaces) { 391 val interfaceClass = interfaceType.asClass() ?: continue 392 for (method in interfaceClass.methods()) { 393 val name = method.name() 394 val list = interfaceNames[name] ?: run { 395 val list = ArrayList<MethodItem>() 396 interfaceNames[name] = list 397 list 398 } 399 list.add(method) 400 } 401 } 402 403 // Also add in any abstract methods from public super classes 404 val publicSuperClasses = superClasses.filter { filterEmit.test(it) && !it.isJavaLangObject() } 405 for (superClass in publicSuperClasses) { 406 for (method in superClass.methods()) { 407 if (!method.modifiers.isAbstract() || !method.modifiers.isPublicOrProtected()) { 408 continue 409 } 410 val name = method.name() 411 val list = interfaceNames[name] ?: run { 412 val list = ArrayList<MethodItem>() 413 interfaceNames[name] = list 414 list 415 } 416 list.add(method) 417 } 418 } 419 420 // Also add in any concrete public methods from hidden super classes 421 for (superClass in hiddenSuperClasses) { 422 for (method in superClass.methods()) { 423 if (method.modifiers.isAbstract() || !method.modifiers.isPublic()) { 424 continue 425 } 426 427 if (method.hasHiddenType(filterReference)) { 428 continue 429 } 430 431 val name = method.name() 432 val list = interfaceNames[name] ?: run { 433 val list = ArrayList<MethodItem>() 434 interfaceNames[name] = list 435 list 436 } 437 list.add(method) 438 } 439 } 440 441 // Find all methods that are inherited from these classes into our class 442 // (making sure that we don't have duplicates, e.g. a method defined by one 443 // inherited class and then overridden by another closer one). 444 // map from method name to super methods overriding our interfaces 445 val map = HashMap<String, MutableList<MethodItem>>() 446 447 for (superClass in hiddenSuperClasses) { 448 for (method in superClass.methods()) { 449 val modifiers = method.modifiers 450 if (!modifiers.isPrivate() && !modifiers.isAbstract()) { 451 val name = method.name() 452 val candidates = interfaceNames[name] ?: continue 453 val parameterCount = method.parameters().size 454 for (superMethod in candidates) { 455 if (parameterCount != superMethod.parameters().count()) { 456 continue 457 } 458 if (method.matches(superMethod)) { 459 val list = map[name] ?: run { 460 val newList = ArrayList<MethodItem>() 461 map[name] = newList 462 newList 463 } 464 list.add(method) 465 break 466 } 467 } 468 } 469 } 470 } 471 472 // Remove any methods that are overriding any of our existing methods 473 for (method in cls.methods()) { 474 val name = method.name() 475 val candidates = map[name] ?: continue 476 val iterator = candidates.listIterator() 477 while (iterator.hasNext()) { 478 val inheritedMethod = iterator.next() 479 if (method.matches(inheritedMethod)) { 480 iterator.remove() 481 } 482 } 483 } 484 485 // Next remove any overrides among the remaining super methods (e.g. one method from a hidden parent is 486 // overriding another method from a more distant hidden parent). 487 map.values.forEach { methods -> 488 if (methods.size >= 2) { 489 for (candidate in ArrayList(methods)) { 490 for (superMethod in candidate.allSuperMethods()) { 491 methods.remove(superMethod) 492 } 493 } 494 } 495 } 496 497 // We're now left with concrete methods in hidden parents that are implementing methods in public 498 // interfaces that are listed in this class. Create stubs for them: 499 map.values.flatten().forEach { 500 val method = cls.createMethod(it) 501 /* Insert comment marker: This is useful for debugging purposes but doesn't 502 belong in the stub 503 method.documentation = "// Inlined stub from hidden parent class ${it.containingClass().qualifiedName()}\n" + 504 method.documentation 505 */ 506 method.inheritedMethod = true 507 method.inheritedFrom = it.containingClass() 508 509 // The documentation may use relative references to classes in import statements 510 // in the original class, so expand the documentation to be fully qualified. 511 @Suppress("ConstantConditionIf") 512 if (!EXPAND_DOCUMENTATION) { 513 method.documentation = it.fullyQualifiedDocumentation() 514 } 515 cls.addMethod(method) 516 } 517 } 518 519 /** Hide packages explicitly listed in [Options.hidePackages] */ 520 private fun hidePackages() { 521 for (pkgName in options.hidePackages) { 522 val pkg = codebase.findPackage(pkgName) ?: continue 523 pkg.hidden = true 524 pkg.included = false // because included has already been initialized 525 } 526 } 527 528 /** Apply emit filters listed in [Options.skipEmitPackages] */ 529 private fun skipEmitPackages() { 530 for (pkgName in options.skipEmitPackages) { 531 val pkg = codebase.findPackage(pkgName) ?: continue 532 pkg.emit = false 533 } 534 } 535 536 /** 537 * Merge in external qualifier annotations (i.e. ones intended to be included in the API written 538 * from all configured sources. 539 */ 540 fun mergeExternalQualifierAnnotations() { 541 if (!options.mergeQualifierAnnotations.isEmpty()) { 542 AnnotationsMerger(codebase).mergeQualifierAnnotations(options.mergeQualifierAnnotations) 543 } 544 } 545 546 /** Merge in external show/hide annotations from all configured sources */ 547 fun mergeExternalInclusionAnnotations() { 548 if (!options.mergeInclusionAnnotations.isEmpty()) { 549 AnnotationsMerger(codebase).mergeInclusionAnnotations(options.mergeInclusionAnnotations) 550 } 551 } 552 553 /** 554 * Propagate the hidden flag down into individual elements -- if a class is hidden, then the methods and fields 555 * are hidden etc 556 */ 557 private fun propagateHiddenRemovedAndDocOnly(includingFields: Boolean) { 558 packages.accept(object : ItemVisitor(visitConstructorsAsMethods = true, nestInnerClasses = true) { 559 override fun visitPackage(pkg: PackageItem) { 560 when { 561 options.hidePackages.contains(pkg.qualifiedName()) -> pkg.hidden = true 562 pkg.modifiers.hasShowAnnotation() -> pkg.hidden = false 563 pkg.modifiers.hasHideAnnotations() -> pkg.hidden = true 564 } 565 val containingPackage = pkg.containingPackage() 566 if (containingPackage != null) { 567 if (containingPackage.hidden && !containingPackage.isDefault) { 568 pkg.hidden = true 569 } 570 if (containingPackage.docOnly) { 571 pkg.docOnly = true 572 } 573 } 574 } 575 576 override fun visitClass(cls: ClassItem) { 577 val containingClass = cls.containingClass() 578 if (cls.modifiers.hasShowAnnotation()) { 579 cls.hidden = false 580 // Make containing package non-hidden if it contains a show-annotation 581 // class. Doclava does this in PackageInfo.isHidden(). 582 cls.containingPackage().hidden = false 583 if (cls.containingClass() != null) { 584 ensureParentVisible(cls) 585 } 586 } else if (cls.modifiers.hasHideAnnotations()) { 587 cls.hidden = true 588 } else if (containingClass != null) { 589 if (containingClass.hidden) { 590 cls.hidden = true 591 } else if (containingClass.originallyHidden && containingClass.modifiers.hasShowSingleAnnotation()) { 592 // See explanation in visitMethod 593 cls.hidden = true 594 } 595 if (containingClass.docOnly) { 596 cls.docOnly = true 597 } 598 if (containingClass.removed) { 599 cls.removed = true 600 } 601 } else { 602 val containingPackage = cls.containingPackage() 603 if (containingPackage.hidden && !containingPackage.isDefault) { 604 cls.hidden = true 605 } else if (containingPackage.originallyHidden) { 606 // Package was marked hidden; it's been unhidden by some other 607 // classes (marked with show annotations) but this class 608 // should continue to default. 609 cls.hidden = true 610 } 611 if (containingPackage.docOnly && !containingPackage.isDefault) { 612 cls.docOnly = true 613 } 614 if (containingPackage.removed && !cls.modifiers.hasShowAnnotation()) { 615 cls.removed = true 616 } 617 } 618 } 619 620 override fun visitMethod(method: MethodItem) { 621 if (method.modifiers.hasShowAnnotation()) { 622 method.hidden = false 623 ensureParentVisible(method) 624 } else if (method.modifiers.hasHideAnnotations()) { 625 method.hidden = true 626 } else { 627 val containingClass = method.containingClass() 628 if (containingClass.hidden) { 629 method.hidden = true 630 } else if (containingClass.originallyHidden && containingClass.modifiers.hasShowSingleAnnotation()) { 631 // This is a member in a class that was hidden but then unhidden; 632 // but it was unhidden by a non-recursive (single) show annotation, so 633 // don't inherit the show annotation into this item. 634 method.hidden = true 635 } 636 if (containingClass.docOnly) { 637 method.docOnly = true 638 } 639 if (containingClass.removed) { 640 method.removed = true 641 } 642 } 643 } 644 645 override fun visitField(field: FieldItem) { 646 if (field.modifiers.hasShowAnnotation()) { 647 field.hidden = false 648 ensureParentVisible(field) 649 } else if (field.modifiers.hasHideAnnotations()) { 650 field.hidden = true 651 } else { 652 val containingClass = field.containingClass() 653 /* We don't always propagate field visibility down to the fields 654 because we sometimes move fields around, and in that 655 case we don't want to carry forward the "hidden" attribute 656 from the field that wasn't marked on the field but its 657 container interface. 658 */ 659 if (includingFields && containingClass.hidden) { 660 field.hidden = true 661 } else if (containingClass.originallyHidden && containingClass.modifiers.hasShowSingleAnnotation()) { 662 // See explanation in visitMethod 663 field.hidden = true 664 } 665 if (containingClass.docOnly) { 666 field.docOnly = true 667 } 668 if (containingClass.removed) { 669 field.removed = true 670 } 671 } 672 } 673 674 private fun ensureParentVisible(item: Item) { 675 val parent = item.parent() ?: return 676 if (parent.hidden && item.modifiers.hasShowSingleAnnotation()) { 677 val annotation = item.modifiers.annotations().find { 678 options.showSingleAnnotations.contains(it.qualifiedName()) 679 } ?: options.showSingleAnnotations.first() 680 reporter.report( 681 Errors.SHOWING_MEMBER_IN_HIDDEN_CLASS, item, 682 "Attempting to unhide ${item.describe()}, but surrounding ${parent.describe()} is " + 683 "hidden and should also be annotated with $annotation" 684 ) 685 } 686 } 687 }) 688 } 689 690 private fun checkSystemPermissions(method: MethodItem) { 691 if (method.isImplicitConstructor()) { // Don't warn on non-source elements like implicit default constructors 692 return 693 } 694 695 val annotation = method.modifiers.findAnnotation(ANDROID_REQUIRES_PERMISSION) 696 var hasAnnotation = false 697 698 if (annotation != null) { 699 hasAnnotation = true 700 for (attribute in annotation.attributes()) { 701 var values: List<AnnotationAttributeValue>? = null 702 var any = false 703 when (attribute.name) { 704 "value", "allOf" -> { 705 values = attribute.leafValues() 706 } 707 "anyOf" -> { 708 any = true 709 values = attribute.leafValues() 710 } 711 } 712 713 values ?: continue 714 715 val system = ArrayList<String>() 716 val nonSystem = ArrayList<String>() 717 val missing = ArrayList<String>() 718 for (value in values) { 719 val perm = (value.value() ?: value.toSource()).toString() 720 val level = codebase.getPermissionLevel(perm) 721 if (level == null) { 722 if (any) { 723 missing.add(perm) 724 continue 725 } 726 727 reporter.report( 728 Errors.REQUIRES_PERMISSION, method, 729 "Permission '$perm' is not defined by manifest ${codebase.manifest}." 730 ) 731 continue 732 } 733 if (level.contains("normal") || level.contains("dangerous") || 734 level.contains("ephemeral") 735 ) { 736 nonSystem.add(perm) 737 } else { 738 system.add(perm) 739 } 740 } 741 if (any && missing.size == values.size) { 742 reporter.report( 743 Errors.REQUIRES_PERMISSION, method, 744 "None of the permissions ${missing.joinToString()} are defined by manifest " + 745 "${codebase.manifest}." 746 ) 747 } 748 749 if (system.isEmpty() && nonSystem.isEmpty()) { 750 hasAnnotation = false 751 } else if (any && !nonSystem.isEmpty() || !any && system.isEmpty()) { 752 reporter.report( 753 Errors.REQUIRES_PERMISSION, method, "Method '" + method.name() + 754 "' must be protected with a system permission; it currently" + 755 " allows non-system callers holding " + nonSystem.toString() 756 ) 757 } 758 } 759 } 760 761 if (!hasAnnotation) { 762 reporter.report( 763 Errors.REQUIRES_PERMISSION, method, "Method '" + method.name() + 764 "' must be protected with a system permission." 765 ) 766 } 767 } 768 769 fun performChecks() { 770 if (codebase.trustedApi()) { 771 // The codebase is already an API; no consistency checks to be performed 772 return 773 } 774 775 val checkSystemApi = !reporter.isSuppressed(Errors.REQUIRES_PERMISSION) && 776 options.showAnnotations.contains(ANDROID_SYSTEM_API) && options.manifest != null 777 val checkHiddenShowAnnotations = !reporter.isSuppressed(Errors.UNHIDDEN_SYSTEM_API) && 778 options.showAnnotations.isNotEmpty() 779 780 packages.accept(object : ApiVisitor() { 781 override fun visitParameter(parameter: ParameterItem) { 782 checkTypeReferencesHidden(parameter, parameter.type()) 783 } 784 785 override fun visitItem(item: Item) { 786 if (item.deprecated && !item.documentation.contains("@deprecated") && 787 // Don't warn about this in Kotlin; the Kotlin deprecation annotation includes deprecation 788 // messages (unlike java.lang.Deprecated which has no attributes). Instead, these 789 // are added to the documentation by the [DocAnalyzer]. 790 !item.isKotlin() 791 ) { 792 reporter.report( 793 Errors.DEPRECATION_MISMATCH, item, 794 "${item.toString().capitalize()}: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match" 795 ) 796 // TODO: Check opposite (doc tag but no annotation) 797 } 798 799 if (checkHiddenShowAnnotations && 800 item.hasShowAnnotation() && 801 !item.documentation.contains("@hide") && 802 !item.modifiers.hasShowSingleAnnotation() 803 ) { 804 val annotationName = (item.modifiers.annotations().firstOrNull { 805 options.showAnnotations.contains(it.qualifiedName()) 806 }?.qualifiedName() ?: options.showAnnotations.first()).removePrefix(ANDROID_ANNOTATION_PREFIX) 807 reporter.report( 808 Errors.UNHIDDEN_SYSTEM_API, item, 809 "@$annotationName APIs must also be marked @hide: ${item.describe()}" 810 ) 811 } 812 } 813 814 override fun visitClass(cls: ClassItem) { 815 // Propagate @Deprecated flags down from classes into inner classes, if configured. 816 // Done here rather than in the analyzer which propagates visibility, since we want to do it 817 // after warning 818 val containingClass = cls.containingClass() 819 if (containingClass != null && containingClass.deprecated && compatibility.propagateDeprecatedInnerClasses) { 820 cls.deprecated = true 821 } 822 823 if (checkSystemApi) { 824 // Look for Android @SystemApi exposed outside the normal SDK; we require 825 // that they're protected with a system permission. 826 // Also flag @SystemApi apis not annotated with @hide. 827 828 // This class is a system service if it's annotated with @SystemService, 829 // or if it's android.content.pm.PackageManager 830 if (cls.modifiers.isAnnotatedWith("android.annotation.SystemService") || 831 cls.qualifiedName() == "android.content.pm.PackageManager" 832 ) { 833 // Check permissions on system services 834 for (method in cls.filteredMethods(filterEmit)) { 835 checkSystemPermissions(method) 836 } 837 } 838 } 839 } 840 841 override fun visitField(field: FieldItem) { 842 val containingClass = field.containingClass() 843 if (containingClass.deprecated && compatibility.propagateDeprecatedMembers) { 844 field.deprecated = true 845 } 846 847 checkTypeReferencesHidden(field, field.type()) 848 } 849 850 override fun visitMethod(method: MethodItem) { 851 if (!method.isConstructor()) { 852 checkTypeReferencesHidden( 853 method, 854 method.returnType()!! 855 ) // returnType is nullable only for constructors 856 } 857 858 val containingClass = method.containingClass() 859 if (containingClass.deprecated && compatibility.propagateDeprecatedMembers) { 860 method.deprecated = true 861 } 862 863 // Make sure we don't annotate findViewById & getSystemService as @Nullable. 864 // See for example 68914170. 865 val name = method.name() 866 if ((name == "findViewById" || name == "getSystemService") && method.parameters().size == 1 && 867 method.modifiers.isNullable() 868 ) { 869 reporter.report( 870 Errors.EXPECTED_PLATFORM_TYPE, method, 871 "$method should not be annotated @Nullable; it should be left unspecified to make it a platform type" 872 ) 873 val annotation = method.modifiers.annotations().find { it.isNullable() } 874 annotation?.let { 875 method.mutableModifiers().removeAnnotation(it) 876 // Have to also clear the annotation out of the return type itself, if it's a type 877 // use annotation 878 method.returnType()?.scrubAnnotations() 879 } 880 } 881 } 882 883 private fun checkTypeReferencesHidden(item: Item, type: TypeItem) { 884 if (type.primitive) { 885 return 886 } 887 888 val cls = type.asClass() 889 890 // Don't flag type parameters like T 891 if (cls?.isTypeParameter == true) { 892 return 893 } 894 895 // class may be null for things like array types and ellipsis types, 896 // but iterating through the type argument classes below will find and 897 // check the component class 898 if (cls != null && !filterReference.test(cls) && !cls.isFromClassPath()) { 899 reporter.report( 900 Errors.HIDDEN_TYPE_PARAMETER, item, 901 "${item.toString().capitalize()} references hidden type $type." 902 ) 903 } 904 905 type.typeArgumentClasses() 906 .filter { it != cls } 907 .forEach { checkTypeReferencesHidden(item, it) } 908 } 909 910 private fun checkTypeReferencesHidden(item: Item, cls: ClassItem) { 911 if (!filterReference.test(cls)) { 912 if (!cls.isFromClassPath()) { 913 reporter.report( 914 Errors.HIDDEN_TYPE_PARAMETER, item, 915 "${item.toString().capitalize()} references hidden type $cls." 916 ) 917 } 918 } else { 919 cls.typeArgumentClasses() 920 .filter { it != cls } 921 .forEach { checkTypeReferencesHidden(item, it) } 922 } 923 } 924 }) 925 } 926 927 fun handleStripping() { 928 // TODO: Switch to visitor iteration 929 // val stubPackages = options.stubPackages 930 val stubImportPackages = options.stubImportPackages 931 handleStripping(stubImportPackages) 932 } 933 934 private fun handleStripping(stubImportPackages: Set<String>) { 935 val notStrippable = HashSet<ClassItem>(5000) 936 937 val filter = ApiPredicate(ignoreShown = true) 938 939 // If a class is public or protected, not hidden, not imported and marked as included, 940 // then we can't strip it 941 val allTopLevelClasses = codebase.getPackages().allTopLevelClasses().toList() 942 allTopLevelClasses 943 .filter { it.checkLevel() && it.emit && !it.hidden() } 944 .forEach { 945 cantStripThis(it, filter, notStrippable, stubImportPackages, it, "self") 946 } 947 948 // complain about anything that looks includeable but is not supposed to 949 // be written, e.g. hidden things 950 for (cl in notStrippable) { 951 if (!cl.isHiddenOrRemoved()) { 952 for (m in cl.methods()) { 953 if (!m.checkLevel()) { 954 continue 955 } 956 if (m.isHiddenOrRemoved()) { 957 reporter.report( 958 Errors.UNAVAILABLE_SYMBOL, m, 959 "Reference to unavailable method " + m.name() 960 ) 961 } else if (m.deprecated) { 962 // don't bother reporting deprecated methods 963 // unless they are public 964 reporter.report( 965 Errors.DEPRECATED, m, "Method " + cl.qualifiedName() + "." + 966 m.name() + " is deprecated" 967 ) 968 } 969 970 val returnType = m.returnType() 971 if (!m.deprecated && !cl.deprecated && returnType != null && returnType.asClass()?.deprecated == true) { 972 reporter.report( 973 Errors.REFERENCES_DEPRECATED, m, 974 "Return type of deprecated type $returnType in ${cl.qualifiedName()}.${m.name()}(): this method should also be deprecated" 975 ) 976 } 977 978 var hiddenClass = findHiddenClasses(returnType, stubImportPackages) 979 if (hiddenClass != null && !hiddenClass.isFromClassPath()) { 980 if (hiddenClass.qualifiedName() == returnType?.asClass()?.qualifiedName()) { 981 // Return type is hidden 982 reporter.report( 983 Errors.UNAVAILABLE_SYMBOL, m, 984 "Method ${cl.qualifiedName()}.${m.name()} returns unavailable " + 985 "type ${hiddenClass.simpleName()}" 986 ) 987 } else { 988 // Return type contains a generic parameter 989 reporter.report( 990 Errors.HIDDEN_TYPE_PARAMETER, m, 991 "Method ${cl.qualifiedName()}.${m.name()} returns unavailable " + 992 "type ${hiddenClass.simpleName()} as a type parameter" 993 ) 994 } 995 } 996 997 for (p in m.parameters()) { 998 val t = p.type() 999 if (!t.primitive) { 1000 if (!m.deprecated && !cl.deprecated && t.asClass()?.deprecated == true) { 1001 reporter.report( 1002 Errors.REFERENCES_DEPRECATED, m, 1003 "Parameter of deprecated type $t in ${cl.qualifiedName()}.${m.name()}(): this method should also be deprecated" 1004 ) 1005 } 1006 1007 hiddenClass = findHiddenClasses(t, stubImportPackages) 1008 if (hiddenClass != null && !hiddenClass.isFromClassPath()) { 1009 if (hiddenClass.qualifiedName() == t.asClass()?.qualifiedName()) { 1010 // Parameter type is hidden 1011 reporter.report( 1012 Errors.UNAVAILABLE_SYMBOL, m, 1013 "Parameter of unavailable type $t in ${cl.qualifiedName()}.${m.name()}()" 1014 ) 1015 } else { 1016 // Parameter type contains a generic parameter 1017 reporter.report( 1018 Errors.HIDDEN_TYPE_PARAMETER, m, 1019 "Parameter uses type parameter of unavailable type $t in ${cl.qualifiedName()}.${m.name()}()" 1020 ) 1021 } 1022 } 1023 } 1024 } 1025 1026 val t = m.returnType() 1027 if (t != null && !t.primitive && !m.deprecated && !cl.deprecated && t.asClass()?.deprecated == true) { 1028 reporter.report( 1029 Errors.REFERENCES_DEPRECATED, m, 1030 "Returning deprecated type $t from ${cl.qualifiedName()}.${m.name()}(): this method should also be deprecated" 1031 ) 1032 } 1033 } 1034 1035 if (!cl.deprecated) { 1036 val s = cl.superClass() 1037 if (s?.deprecated == true) { 1038 reporter.report( 1039 Errors.EXTENDS_DEPRECATED, cl, 1040 "Extending deprecated super class $s from ${cl.qualifiedName()}: this class should also be deprecated" 1041 ) 1042 } 1043 1044 for (t in cl.interfaceTypes()) { 1045 if (t.asClass()?.deprecated == true) { 1046 reporter.report( 1047 Errors.EXTENDS_DEPRECATED, cl, 1048 "Implementing interface of deprecated type $t in ${cl.qualifiedName()}: this class should also be deprecated" 1049 ) 1050 } 1051 } 1052 } 1053 } else if (cl.deprecated) { 1054 // not hidden, but deprecated 1055 reporter.report(Errors.DEPRECATED, cl, "Class ${cl.qualifiedName()} is deprecated") 1056 } else if (reporter.isSuppressed(Errors.REFERENCES_HIDDEN, cl)) { 1057 // If we're not reporting hidden references, bring the type back 1058 // Bring this class back 1059 cl.hidden = false 1060 cl.removed = false 1061 cl.notStrippable = true 1062 } 1063 } 1064 } 1065 1066 private fun cantStripThis( 1067 cl: ClassItem, 1068 filter: Predicate<Item>, 1069 notStrippable: MutableSet<ClassItem>, 1070 stubImportPackages: Set<String>?, 1071 from: Item, 1072 usage: String 1073 ) { 1074 if (stubImportPackages != null && stubImportPackages.contains(cl.containingPackage().qualifiedName())) { 1075 // if the package is imported then it does not need stubbing. 1076 return 1077 } 1078 1079 if (cl.isFromClassPath()) { 1080 return 1081 } 1082 1083 if ((cl.isHiddenOrRemoved() || cl.isPackagePrivate && !cl.checkLevel()) && !cl.isTypeParameter) { 1084 reporter.report( 1085 Errors.REFERENCES_HIDDEN, from, 1086 "Class ${cl.qualifiedName()} is ${if (cl.isHiddenOrRemoved()) "hidden" else "not public"} but was referenced ($usage) from public ${from.describe( 1087 false 1088 )}" 1089 ) 1090 cl.notStrippable = true 1091 } 1092 1093 if (!notStrippable.add(cl)) { 1094 // slight optimization: if it already contains cl, it already contains 1095 // all of cl's parents 1096 return 1097 } 1098 1099 // cant strip any public fields or their generics 1100 for (field in cl.fields()) { 1101 if (!filter.test(field)) { 1102 continue 1103 } 1104 val fieldType = field.type() 1105 if (!fieldType.primitive) { 1106 val typeClass = fieldType.asClass() 1107 if (typeClass != null) { 1108 cantStripThis(typeClass, filter, notStrippable, stubImportPackages, field, "as field type") 1109 } 1110 for (cls in fieldType.typeArgumentClasses()) { 1111 if (cls == typeClass) { 1112 continue 1113 } 1114 cantStripThis(cls, filter, notStrippable, stubImportPackages, field, "as field type argument class") 1115 } 1116 } 1117 } 1118 // cant strip any of the type's generics 1119 for (cls in cl.typeArgumentClasses()) { 1120 cantStripThis(cls, filter, notStrippable, stubImportPackages, cl, "as type argument") 1121 } 1122 // cant strip any of the annotation elements 1123 // cantStripThis(cl.annotationElements(), notStrippable); 1124 // take care of methods 1125 cantStripThis(cl.methods(), filter, notStrippable, stubImportPackages) 1126 cantStripThis(cl.constructors(), filter, notStrippable, stubImportPackages) 1127 // blow the outer class open if this is an inner class 1128 val containingClass = cl.containingClass() 1129 if (containingClass != null) { 1130 cantStripThis(containingClass, filter, notStrippable, stubImportPackages, cl, "as containing class") 1131 } 1132 // blow open super class and interfaces 1133 // TODO: Consider using val superClass = cl.filteredSuperclass(filter) 1134 val superClass = cl.superClass() 1135 if (superClass != null) { 1136 if (superClass.isHiddenOrRemoved()) { 1137 // cl is a public class declared as extending a hidden superclass. 1138 // this is not a desired practice but it's happened, so we deal 1139 // with it by finding the first super class which passes checkLevel for purposes of 1140 // generating the doc & stub information, and proceeding normally. 1141 if (!superClass.isFromClassPath()) { 1142 reporter.report( 1143 Errors.HIDDEN_SUPERCLASS, cl, "Public class " + cl.qualifiedName() + 1144 " stripped of unavailable superclass " + superClass.qualifiedName() 1145 ) 1146 } 1147 } else { 1148 // doclava would also mark the package private super classes as unhidden, but that's not 1149 // right (this was just done for its stub handling) 1150 // cantStripThis(superClass, filter, notStrippable, stubImportPackages, cl, "as super class") 1151 1152 if (superClass.isPrivate && !superClass.isFromClassPath()) { 1153 reporter.report( 1154 Errors.PRIVATE_SUPERCLASS, cl, "Public class " + 1155 cl.qualifiedName() + " extends private class " + superClass.qualifiedName() 1156 ) 1157 } 1158 } 1159 } 1160 } 1161 1162 private fun cantStripThis( 1163 methods: List<MethodItem>, 1164 filter: Predicate<Item>, 1165 notStrippable: MutableSet<ClassItem>, 1166 stubImportPackages: Set<String>? 1167 ) { 1168 // for each method, blow open the parameters, throws and return types. also blow open their 1169 // generics 1170 for (method in methods) { 1171 if (!filter.test(method)) { 1172 continue 1173 } 1174 for (typeParameterClass in method.typeArgumentClasses()) { 1175 cantStripThis( 1176 typeParameterClass, 1177 filter, 1178 notStrippable, 1179 stubImportPackages, 1180 method, 1181 "as type parameter" 1182 ) 1183 } 1184 for (parameter in method.parameters()) { 1185 for (parameterTypeClass in parameter.type().typeArgumentClasses()) { 1186 cantStripThis( 1187 parameterTypeClass, 1188 filter, 1189 notStrippable, 1190 stubImportPackages, 1191 parameter, 1192 "as parameter type" 1193 ) 1194 for (tcl in parameter.type().typeArgumentClasses()) { 1195 if (tcl == parameterTypeClass) { 1196 continue 1197 } 1198 if (tcl.isHiddenOrRemoved()) { 1199 reporter.report( 1200 Errors.UNAVAILABLE_SYMBOL, method, 1201 "Parameter of hidden type ${tcl.fullName()} " + 1202 "in ${method.containingClass().qualifiedName()}.${method.name()}()" 1203 ) 1204 } else { 1205 cantStripThis( 1206 tcl, 1207 filter, 1208 notStrippable, 1209 stubImportPackages, 1210 parameter, 1211 "as type parameter" 1212 ) 1213 } 1214 } 1215 } 1216 } 1217 for (thrown in method.throwsTypes()) { 1218 cantStripThis(thrown, filter, notStrippable, stubImportPackages, method, "as exception") 1219 } 1220 val returnType = method.returnType() 1221 if (returnType != null && !returnType.primitive) { 1222 val returnTypeClass = returnType.asClass() 1223 if (returnTypeClass != null) { 1224 cantStripThis(returnTypeClass, filter, notStrippable, stubImportPackages, method, "as return type") 1225 for (tyItem in returnType.typeArgumentClasses()) { 1226 if (tyItem == returnTypeClass) { 1227 continue 1228 } 1229 cantStripThis( 1230 tyItem, 1231 filter, 1232 notStrippable, 1233 stubImportPackages, 1234 method, 1235 "as return type parameter" 1236 ) 1237 } 1238 } 1239 } 1240 } 1241 } 1242 1243 /** 1244 * Find references to hidden classes. 1245 * 1246 * This finds hidden classes that are used by public parts of the API in order to ensure the 1247 * API is self consistent and does not reference classes that are not included in 1248 * the stubs. Any such references cause an error to be reported. 1249 * 1250 * A reference to an imported class is not treated as an error, even though imported classes 1251 * are hidden from the stub generation. That is because imported classes are, by definition, 1252 * excluded from the set of classes for which stubs are required. 1253 * 1254 * @param ti the type information to examine for references to hidden classes. 1255 * @param stubImportPackages the possibly null set of imported package names. 1256 * @return a reference to a hidden class or null if there are none 1257 */ 1258 private fun findHiddenClasses(ti: TypeItem?, stubImportPackages: Set<String>?): ClassItem? { 1259 ti ?: return null 1260 val ci = ti.asClass() ?: return null 1261 return findHiddenClasses(ci, stubImportPackages) 1262 } 1263 1264 private fun findHiddenClasses(ci: ClassItem, stubImportPackages: Set<String>?): ClassItem? { 1265 if (stubImportPackages != null && stubImportPackages.contains(ci.containingPackage().qualifiedName())) { 1266 return null 1267 } 1268 if (ci.isHiddenOrRemoved()) return ci 1269 for (tii in ci.toType().typeArgumentClasses()) { 1270 // Avoid infinite recursion in the case of Foo<T extends Foo> 1271 if (tii != ci) { 1272 val hiddenClass = findHiddenClasses(tii, stubImportPackages) 1273 if (hiddenClass != null) return hiddenClass 1274 } 1275 } 1276 return null 1277 } 1278 } 1279