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