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