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