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