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