• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * Copyright (C) 2023 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.model.annotation
18 
19 import com.android.tools.metalava.model.ANDROIDX_ANNOTATION_PREFIX
20 import com.android.tools.metalava.model.ANDROIDX_NONNULL
21 import com.android.tools.metalava.model.ANDROIDX_NULLABLE
22 import com.android.tools.metalava.model.ANDROID_ANNOTATION_PREFIX
23 import com.android.tools.metalava.model.ANDROID_DEPRECATED_FOR_SDK
24 import com.android.tools.metalava.model.ANDROID_FLAGGED_API
25 import com.android.tools.metalava.model.ANDROID_NONNULL
26 import com.android.tools.metalava.model.ANDROID_NULLABLE
27 import com.android.tools.metalava.model.ANDROID_SYSTEM_API
28 import com.android.tools.metalava.model.ANDROID_TEST_API
29 import com.android.tools.metalava.model.ANNOTATION_ATTR_VALUE
30 import com.android.tools.metalava.model.ANNOTATION_EXTERNAL
31 import com.android.tools.metalava.model.ANNOTATION_EXTERNAL_ONLY
32 import com.android.tools.metalava.model.ANNOTATION_IN_ALL_STUBS
33 import com.android.tools.metalava.model.ANNOTATION_IN_DOC_STUBS_AND_EXTERNAL
34 import com.android.tools.metalava.model.ANNOTATION_SDK_STUBS_ONLY
35 import com.android.tools.metalava.model.ANNOTATION_SIGNATURE_ONLY
36 import com.android.tools.metalava.model.ANNOTATION_STUBS_ONLY
37 import com.android.tools.metalava.model.AnnotationInfo
38 import com.android.tools.metalava.model.AnnotationItem
39 import com.android.tools.metalava.model.AnnotationRetention
40 import com.android.tools.metalava.model.AnnotationTarget
41 import com.android.tools.metalava.model.BaseAnnotationManager
42 import com.android.tools.metalava.model.ClassItem
43 import com.android.tools.metalava.model.ClassOrigin
44 import com.android.tools.metalava.model.Codebase
45 import com.android.tools.metalava.model.FilterPredicate
46 import com.android.tools.metalava.model.JAVA_LANG_PREFIX
47 import com.android.tools.metalava.model.JVM_STATIC
48 import com.android.tools.metalava.model.MethodItem
49 import com.android.tools.metalava.model.ModifierList
50 import com.android.tools.metalava.model.NO_ANNOTATION_TARGETS
51 import com.android.tools.metalava.model.RECENTLY_NONNULL
52 import com.android.tools.metalava.model.RECENTLY_NULLABLE
53 import com.android.tools.metalava.model.SUPPRESS_COMPATIBILITY_ANNOTATION_QUALIFIED
54 import com.android.tools.metalava.model.SelectableItem
55 import com.android.tools.metalava.model.ShowOrHide
56 import com.android.tools.metalava.model.Showability
57 import com.android.tools.metalava.model.Showability.Companion.REVERT_UNSTABLE_API
58 import com.android.tools.metalava.model.TypedefMode
59 import com.android.tools.metalava.model.annotation.DefaultAnnotationManager.Config
60 import com.android.tools.metalava.model.api.flags.ApiFlag
61 import com.android.tools.metalava.model.api.flags.ApiFlags
62 import com.android.tools.metalava.model.computeTypeNullability
63 import com.android.tools.metalava.model.hasAnnotation
64 import com.android.tools.metalava.model.isNonNullAnnotation
65 import com.android.tools.metalava.model.isNullableAnnotation
66 
67 /** The type of lambda that can construct a key from an [AnnotationItem] */
68 typealias KeyFactory = (annotationItem: AnnotationItem) -> String
69 
70 class DefaultAnnotationManager(private val config: Config = Config()) : BaseAnnotationManager() {
71 
72     data class Config(
73         val passThroughAnnotations: Set<String> = emptySet(),
74         val allShowAnnotations: AnnotationFilter = AnnotationFilter.emptyFilter(),
75         val showAnnotations: AnnotationFilter = AnnotationFilter.emptyFilter(),
76         val showSingleAnnotations: AnnotationFilter = AnnotationFilter.emptyFilter(),
77         val showForStubPurposesAnnotations: AnnotationFilter = AnnotationFilter.emptyFilter(),
78         val hideAnnotations: AnnotationFilter = AnnotationFilter.emptyFilter(),
79         val suppressCompatibilityMetaAnnotations: Set<String> = emptySet(),
80         val excludeAnnotations: Set<String> = emptySet(),
81         val typedefMode: TypedefMode = TypedefMode.NONE,
82         val apiPredicate: FilterPredicate = FilterPredicate { true },
83         /**
84          * Provider of an optional [Codebase] object that will be used when reverting flagged APIs.
85          */
86         val previouslyReleasedCodebaseProvider: () -> Codebase? = { null },
87 
88         /**
89          * The set of available [ApiFlag]s.
90          *
91          * If this is `null` then no [ApiFlag]s have been provided, otherwise it contains an
92          * [ApiFlag] for every provided flag. Flags that are not provided will default to
93          * [ApiFlag.REVERT_FLAGGED_API].
94          */
95         val apiFlags: ApiFlags? = null,
96     )
97 
98     /**
99      * Map from annotation name to the [KeyFactory] to use to create a key.
100      *
101      * See [getKeyForAnnotationItem] to see how this is used.
102      */
103     private val annotationNameToKeyFactory: Map<String, KeyFactory>
104 
105     init {
106         /** Use the complete source representation of the item as the key. */
107         fun useSourceAsKey(annotationItem: AnnotationItem): String {
108             val qualifiedName = annotationItem.qualifiedName
109             val attributes = annotationItem.attributes
110             if (attributes.isEmpty()) {
111                 return qualifiedName
112             }
113             return buildString {
114                 append(qualifiedName)
115                 append("(")
116                 attributes.forEachIndexed { index, attribute ->
117                     if (index > 0) {
118                         append(",")
119                     }
120                     append(attribute)
121                 }
122                 append(")")
123             }
124         }
125 
126         // The list of all filters.
127         val filters =
128             listOf(
129                 config.allShowAnnotations,
130                 config.showSingleAnnotations,
131                 config.showForStubPurposesAnnotations,
132                 config.hideAnnotations,
133             )
134 
135         // Build a list of the names of annotations whose AnnotationInfo could be dependent on an
136         // annotation attributes and not just its name.
137         val annotationNames = buildList {
138             // Iterate over all the annotation names matched by all the filters currently used by
139             // [LazyAnnotationInfo] and associate them with a [KeyFactory] that will use the
140             // complete source representation of the annotation as the key. This is needed because
141             // filters can match on attribute values as well as the name.
142             for (filter in filters) {
143                 addAll(filter.getIncludedAnnotationNames())
144             }
145 
146             // ApiFlags have been provided so the flag name specified on an
147             // `android.annotation.FlaggedApi` will affect the state of the associated
148             // AnnotationInfo so make sure to use the flag name in the cache key for `FlaggedApi`
149             // annotations.
150             if (config.apiFlags != null) add(ANDROID_FLAGGED_API)
151         }
152 
153         // Use KeyFactory that uses the complete source representation as the key and not just the
154         // annotation name which is the default.
155         annotationNameToKeyFactory = annotationNames.associateWith { ::useSourceAsKey }
156     }
157 
158     override fun getKeyForAnnotationItem(annotationItem: AnnotationItem): String {
159         val qualifiedName = annotationItem.qualifiedName
160 
161         // Check to see if this requires a special [KeyFactory] and use it if it does.
162         val keyFactory = annotationNameToKeyFactory.get(qualifiedName)
163         if (keyFactory != null) {
164             return keyFactory(annotationItem)
165         }
166 
167         // No special key factory is needed so just use the qualified name as the key.
168         return qualifiedName
169     }
170 
171     override fun computeAnnotationInfo(annotationItem: AnnotationItem): AnnotationInfo {
172         return LazyAnnotationInfo(this, config, annotationItem)
173     }
174 
175     override fun normalizeInputName(qualifiedName: String?): String? {
176         qualifiedName ?: return null
177         if (passThroughAnnotation(qualifiedName)) {
178             return qualifiedName
179         }
180 
181         if (config.excludeAnnotations.contains(qualifiedName)) {
182             return null
183         }
184 
185         when (qualifiedName) {
186             // Resource annotations
187             "android.annotation.AnimRes" -> return "androidx.annotation.AnimRes"
188             "android.annotation.AnimatorRes" -> return "androidx.annotation.AnimatorRes"
189             "android.annotation.AnyRes" -> return "androidx.annotation.AnyRes"
190             "android.annotation.ArrayRes" -> return "androidx.annotation.ArrayRes"
191             "android.annotation.AttrRes" -> return "androidx.annotation.AttrRes"
192             "android.annotation.BoolRes" -> return "androidx.annotation.BoolRes"
193             "android.annotation.ColorRes" -> return "androidx.annotation.ColorRes"
194             "android.annotation.DimenRes" -> return "androidx.annotation.DimenRes"
195             "android.annotation.DrawableRes" -> return "androidx.annotation.DrawableRes"
196             "android.annotation.FontRes" -> return "androidx.annotation.FontRes"
197             "android.annotation.FractionRes" -> return "androidx.annotation.FractionRes"
198             "android.annotation.IdRes" -> return "androidx.annotation.IdRes"
199             "android.annotation.IntegerRes" -> return "androidx.annotation.IntegerRes"
200             "android.annotation.InterpolatorRes" -> return "androidx.annotation.InterpolatorRes"
201             "android.annotation.LayoutRes" -> return "androidx.annotation.LayoutRes"
202             "android.annotation.MenuRes" -> return "androidx.annotation.MenuRes"
203             "android.annotation.PluralsRes" -> return "androidx.annotation.PluralsRes"
204             "android.annotation.RawRes" -> return "androidx.annotation.RawRes"
205             "android.annotation.StringRes" -> return "androidx.annotation.StringRes"
206             "android.annotation.StyleRes" -> return "androidx.annotation.StyleRes"
207             "android.annotation.StyleableRes" -> return "androidx.annotation.StyleableRes"
208             "android.annotation.TransitionRes" -> return "androidx.annotation.TransitionRes"
209             "android.annotation.XmlRes" -> return "androidx.annotation.XmlRes"
210 
211             // Threading
212             "android.annotation.AnyThread" -> return "androidx.annotation.AnyThread"
213             "android.annotation.BinderThread" -> return "androidx.annotation.BinderThread"
214             "android.annotation.MainThread" -> return "androidx.annotation.MainThread"
215             "android.annotation.UiThread" -> return "androidx.annotation.UiThread"
216             "android.annotation.WorkerThread" -> return "androidx.annotation.WorkerThread"
217 
218             // Colors
219             "android.annotation.ColorInt" -> return "androidx.annotation.ColorInt"
220             "android.annotation.ColorLong" -> return "androidx.annotation.ColorLong"
221             "android.annotation.HalfFloat" -> return "androidx.annotation.HalfFloat"
222 
223             // Ranges and sizes
224             "android.annotation.FloatRange" -> return "androidx.annotation.FloatRange"
225             "android.annotation.IntRange" -> return "androidx.annotation.IntRange"
226             "android.annotation.Size" -> return "androidx.annotation.Size"
227             "android.annotation.Px" -> return "androidx.annotation.Px"
228             "android.annotation.Dimension" -> return "androidx.annotation.Dimension"
229 
230             // Environments
231             "android.annotation.RestrictedForEnvironment" ->
232                 return "androidx.annotation.RestrictedForEnvironment"
233 
234             // Null
235             // Preserve recently/newly nullable annotation as they need to be passed through to
236             // stubs. They will be treated as nullable/non-null just as if they were mapped to
237             // ANDROIDX_NULLABLE or ANDROIDX_NONNULL.
238             RECENTLY_NULLABLE -> return qualifiedName
239             RECENTLY_NONNULL -> return qualifiedName
240 
241             // Normalize the known nullable annotations to ANDROIDX_NULLABLE
242             ANDROIDX_NULLABLE,
243             ANDROID_NULLABLE,
244             "libcore.util.Nullable",
245             "org.jetbrains.annotations.Nullable" -> return ANDROIDX_NULLABLE
246 
247             // Normalize the known non-null annotations to ANDROIDX_NONNULL
248             ANDROIDX_NONNULL,
249             ANDROID_NONNULL,
250             "libcore.util.NonNull",
251             "org.jetbrains.annotations.NotNull" -> return ANDROIDX_NONNULL
252 
253             // Typedefs
254             "android.annotation.IntDef" -> return "androidx.annotation.IntDef"
255             "android.annotation.StringDef" -> return "androidx.annotation.StringDef"
256             "android.annotation.LongDef" -> return "androidx.annotation.LongDef"
257 
258             // Context Types
259             "android.annotation.UiContext" -> return "androidx.annotation.UiContext"
260             "android.annotation.DisplayContext" -> return "androidx.annotation.DisplayContext"
261             "android.annotation.NonUiContext" -> return "androidx.annotation.NonUiContext"
262 
263             // Misc
264             ANDROID_DEPRECATED_FOR_SDK -> return ANDROID_DEPRECATED_FOR_SDK
265             "android.annotation.CallSuper" -> return "androidx.annotation.CallSuper"
266             "android.annotation.CheckResult" -> return "androidx.annotation.CheckResult"
267             "android.annotation.Discouraged" -> return "androidx.annotation.Discouraged"
268             "android.annotation.RequiresPermission" ->
269                 return "androidx.annotation.RequiresPermission"
270             "android.annotation.RequiresPermission.Read" ->
271                 return "androidx.annotation.RequiresPermission.Read"
272             "android.annotation.RequiresPermission.Write" ->
273                 return "androidx.annotation.RequiresPermission.Write"
274 
275             // These aren't support annotations, but could/should be:
276             "android.annotation.CurrentTimeMillisLong",
277             "android.annotation.DurationMicrosLong",
278             "android.annotation.DurationMillisLong",
279             "android.annotation.ElapsedRealtimeLong",
280             "android.annotation.UserIdInt",
281             "android.annotation.BytesLong",
282 
283             // These aren't support annotations
284             "android.annotation.AppIdInt",
285             "android.annotation.SuppressAutoDoc",
286             ANDROID_SYSTEM_API,
287             ANDROID_TEST_API,
288             "android.annotation.CallbackExecutor",
289             "android.annotation.Condemned",
290             "android.annotation.Hide",
291             "android.annotation.Widget" -> return qualifiedName
292 
293             // Included for analysis, but should not be exported:
294             "android.annotation.BroadcastBehavior",
295             "android.annotation.SdkConstant",
296             "android.annotation.RequiresFeature",
297             "android.annotation.SystemService" -> return qualifiedName
298 
299             // Should not be mapped to a different package name:
300             "android.annotation.TargetApi",
301             "android.annotation.SuppressLint" -> return qualifiedName
302             ANDROID_FLAGGED_API -> return qualifiedName
303 
304             // This implementation only annotation shouldn't be used by metalava at all.
305             "dalvik.annotation.codegen.CovariantReturnType" -> return null
306 
307             // TODO(b/399105459): remove this workaround once there is full support for typealias
308             //  annotations from the classpath
309             "kotlin.jvm.JvmRepeatable" -> return "java.lang.annotation.Repeatable"
310             else -> {
311                 // Some new annotations added to the platform: assume they are support
312                 // annotations?
313                 return when {
314                     // Other third party nullness annotations?
315                     isNullableAnnotation(qualifiedName) -> ANDROIDX_NULLABLE
316                     isNonNullAnnotation(qualifiedName) -> ANDROIDX_NONNULL
317 
318                     // AndroidX annotations are all included, as is the built-in stuff like
319                     // @Retention
320                     qualifiedName.startsWith(ANDROIDX_ANNOTATION_PREFIX) -> return qualifiedName
321                     qualifiedName.startsWith(JAVA_LANG_PREFIX) -> return qualifiedName
322 
323                     // Unknown Android platform annotations
324                     qualifiedName.startsWith(ANDROID_ANNOTATION_PREFIX) -> {
325                         return qualifiedName
326                     }
327                     else -> qualifiedName
328                 }
329             }
330         }
331     }
332 
333     override fun normalizeOutputName(qualifiedName: String?, target: AnnotationTarget): String? {
334         qualifiedName ?: return null
335         if (passThroughAnnotation(qualifiedName)) {
336             return qualifiedName
337         }
338 
339         when (qualifiedName) {
340             ANDROIDX_NULLABLE ->
341                 return if (target == AnnotationTarget.SDK_STUBS_FILE) ANDROID_NULLABLE
342                 else qualifiedName
343             ANDROIDX_NONNULL ->
344                 return if (target == AnnotationTarget.SDK_STUBS_FILE) ANDROID_NONNULL
345                 else qualifiedName
346             RECENTLY_NULLABLE ->
347                 return if (target == AnnotationTarget.SDK_STUBS_FILE) qualifiedName
348                 else ANDROIDX_NULLABLE
349             RECENTLY_NONNULL ->
350                 return if (target == AnnotationTarget.SDK_STUBS_FILE) qualifiedName
351                 else ANDROIDX_NONNULL
352         }
353 
354         return qualifiedName
355     }
356 
357     private fun passThroughAnnotation(qualifiedName: String) =
358         config.passThroughAnnotations.contains(qualifiedName) ||
359             config.allShowAnnotations.matches(qualifiedName) ||
360             config.hideAnnotations.matches(qualifiedName)
361 
362     private val TYPEDEF_ANNOTATION_TARGETS =
363         if (
364             config.typedefMode == TypedefMode.INLINE || config.typedefMode == TypedefMode.NONE
365         ) // just here for compatibility purposes
366          ANNOTATION_EXTERNAL
367         else ANNOTATION_EXTERNAL_ONLY
368 
369     /**
370      * The applicable targets for the [annotation].
371      *
372      * Care must be taken to ensure that this only accesses [AnnotationItem.qualifiedName] and
373      * [AnnotationItem.resolve]. In particular, it must NOT access the attributes. That is because
374      * the result must be identical for all [AnnotationItem] instances of an annotation class.
375      */
376     internal fun computeTargets(annotation: AnnotationItem): Set<AnnotationTarget> {
377         val qualifiedName = annotation.qualifiedName
378         if (config.passThroughAnnotations.contains(qualifiedName)) {
379             return ANNOTATION_IN_ALL_STUBS
380         }
381         when (qualifiedName) {
382             // The typedef annotations are special: they should not be in the signature
383             // files, but we want to include them in the external annotations file such that
384             // tools
385             // can enforce them.
386             "android.annotation.IntDef",
387             "androidx.annotation.IntDef",
388             "android.annotation.StringDef",
389             "androidx.annotation.StringDef",
390             "android.annotation.LongDef",
391             "androidx.annotation.LongDef" -> return TYPEDEF_ANNOTATION_TARGETS
392             "android.annotation.RestrictedForEnvironment" -> return ANNOTATION_EXTERNAL
393 
394             // Not directly API relevant
395             "android.view.ViewDebug.ExportedProperty",
396             "android.view.ViewDebug.CapturedViewProperty" -> return ANNOTATION_STUBS_ONLY
397 
398             // Retained in the sdk/jar stub source code so that SdkConstant files can be
399             // extracted
400             // from those. This is useful for modularizing the main SDK stubs without having to
401             // add a separate module SDK artifact for sdk constants.
402             "android.annotation.SdkConstant" -> return ANNOTATION_SDK_STUBS_ONLY
403             ANDROID_FLAGGED_API -> {
404                 return annotation.apiFlag?.annotationTargets ?: ANNOTATION_IN_ALL_STUBS
405             }
406 
407             // Skip known annotations that we (a) never want in external annotations and (b) we
408             // are
409             // specially overwriting anyway in the stubs (and which are (c) not API significant)
410             "com.android.modules.annotation.MinSdk",
411             "java.lang.annotation.Native",
412             "java.lang.SuppressWarnings",
413             "java.lang.Override",
414             "kotlin.Suppress",
415             "androidx.annotation.experimental.UseExperimental",
416             "androidx.annotation.OptIn",
417             "kotlin.UseExperimental",
418             "kotlin.OptIn" -> return NO_ANNOTATION_TARGETS
419 
420             // These optimization-related annotations shouldn't be exported.
421             "dalvik.annotation.optimization.CriticalNative",
422             "dalvik.annotation.optimization.FastNative",
423             "dalvik.annotation.optimization.NeverCompile",
424             "dalvik.annotation.optimization.NeverInline",
425             "dalvik.annotation.optimization.ReachabilitySensitive" -> return NO_ANNOTATION_TARGETS
426 
427             // TODO(aurimas): consider using annotation directly instead of modifiers
428             ANDROID_DEPRECATED_FOR_SDK,
429             "kotlin.Deprecated" ->
430                 return NO_ANNOTATION_TARGETS // tracked separately as a pseudo-modifier
431             "java.lang.Deprecated", // tracked separately as a pseudo-modifier
432 
433             // Below this when-statement we perform the correct lookup: check API predicate, and
434             // check
435             // that retention is class or runtime, but we've hardcoded the answers here
436             // for some common annotations.
437 
438             "android.widget.RemoteViews.RemoteView",
439             "kotlin.annotation.Target",
440             "kotlin.annotation.Retention",
441             "kotlin.annotation.Repeatable",
442             "kotlin.annotation.MustBeDocumented",
443             "kotlin.DslMarker",
444             "kotlin.PublishedApi",
445             "kotlin.ExtensionFunctionType",
446             "java.lang.FunctionalInterface",
447             "java.lang.SafeVarargs",
448             "java.lang.annotation.Documented",
449             "java.lang.annotation.Inherited",
450             "java.lang.annotation.Repeatable",
451             "java.lang.annotation.Retention",
452             "java.lang.annotation.Target" -> return ANNOTATION_IN_ALL_STUBS
453 
454             // Metalava already tracks all the methods that get generated due to these
455             // annotations.
456             "kotlin.jvm.JvmOverloads",
457             "kotlin.jvm.JvmField",
458             JVM_STATIC,
459             "kotlin.jvm.JvmName" -> return NO_ANNOTATION_TARGETS
460         }
461 
462         // @android.annotation.Nullable and NonNullable specially recognized annotations by the
463         // Kotlin
464         // compiler 1.3 and above: they always go in the stubs.
465         if (
466             qualifiedName == ANDROID_NULLABLE ||
467                 qualifiedName == ANDROID_NONNULL ||
468                 qualifiedName == ANDROIDX_NULLABLE ||
469                 qualifiedName == ANDROIDX_NONNULL
470         ) {
471             return ANNOTATION_IN_ALL_STUBS
472         }
473 
474         if (qualifiedName.startsWith("android.annotation.")) {
475             // internal annotations not mapped to androidx: things like @SystemApi. Skip from
476             // stubs, external annotations, signature files, etc.
477             return NO_ANNOTATION_TARGETS
478         }
479 
480         // @RecentlyNullable and @RecentlyNonNull are specially recognized annotations by the
481         // Kotlin
482         // compiler: they always go in the stubs.
483         if (qualifiedName == RECENTLY_NULLABLE || qualifiedName == RECENTLY_NONNULL) {
484             return ANNOTATION_IN_ALL_STUBS
485         }
486 
487         // Determine the retention of the annotation: source retention annotations go
488         // in the external annotations file, class and runtime annotations go in
489         // the stubs files (except for the androidx annotations which are not included
490         // in the SDK and therefore cannot be referenced from it due to apt's unfortunate
491         // habit of loading all annotation classes it encounters.)
492 
493         if (qualifiedName.startsWith("androidx.annotation.")) {
494             if (qualifiedName == ANDROIDX_NULLABLE || qualifiedName == ANDROIDX_NONNULL) {
495                 // Right now, nullness annotations (other than @RecentlyNullable and
496                 // @RecentlyNonNull)
497                 // have to go in external annotations since they aren't in the class path for
498                 // annotation processors. However, we do want them showing up in the
499                 // documentation using
500                 // their real annotation names.
501                 return ANNOTATION_IN_DOC_STUBS_AND_EXTERNAL
502             }
503 
504             return ANNOTATION_EXTERNAL
505         }
506 
507         // See if the annotation is pointing to an annotation class that is part of the API; if
508         // not, skip it.
509         val cls = annotation.resolve() ?: return NO_ANNOTATION_TARGETS
510         if (!config.apiPredicate.test(cls)) {
511             if (config.typedefMode != TypedefMode.NONE) {
512                 if (cls.modifiers.hasAnnotation(AnnotationItem::isTypeDefAnnotation)) {
513                     return ANNOTATION_SIGNATURE_ONLY
514                 }
515             }
516 
517             return NO_ANNOTATION_TARGETS
518         }
519 
520         if (cls.isAnnotationType()) {
521             val retention = cls.getRetention()
522             if (
523                 retention == AnnotationRetention.RUNTIME ||
524                     retention == AnnotationRetention.CLASS ||
525                     retention == AnnotationRetention.BINARY
526             ) {
527                 return ANNOTATION_IN_ALL_STUBS
528             }
529         }
530 
531         return ANNOTATION_EXTERNAL
532     }
533 
534     override fun isShowAnnotationName(annotationName: String): Boolean =
535         config.allShowAnnotations.matchesAnnotationName(annotationName)
536 
537     /** Check whether this has been configured in a way that could cause items to be reverted. */
538     private fun couldRevertItems(): Boolean = config.apiFlags != null
539 
540     override fun hasAnyStubPurposesAnnotations(): Boolean {
541         // This checks if items can be reverted because they can behave like
542         // `--show-for-stub-purposes-annotation` if a reverted Item was added in an extended API.
543         // e.g. if a change to item `X` from the public API was reverted then the
544         // previously released version `X'` will need to be written out to the stubs for the system
545         // API, just as if it was annotated with an annotation from
546         // `--show-for-stub-purposes-annotation`.
547         return config.showForStubPurposesAnnotations.isNotEmpty() || couldRevertItems()
548     }
549 
550     override fun hasHideAnnotations(modifiers: ModifierList): Boolean {
551         // If there are no hide annotations and items cannot be reverted then this can never return
552         // true. Reverted items can behave as if they are hidden it they are newly added.
553         if (config.hideAnnotations.isEmpty() && !couldRevertItems()) {
554             return false
555         }
556         return modifiers.hasAnnotation(AnnotationItem::isHideAnnotation)
557     }
558 
559     override fun hasSuppressCompatibilityMetaAnnotations(modifiers: ModifierList): Boolean {
560         if (config.suppressCompatibilityMetaAnnotations.isEmpty()) {
561             return false
562         }
563         return modifiers.hasAnnotation(AnnotationItem::isSuppressCompatibilityAnnotation)
564     }
565 
566     override fun getShowabilityForItem(item: SelectableItem): Showability {
567         // Iterates over the annotations on the item and computes the showability for the item by
568         // combining the showability of each annotation. The basic rules are:
569         // * `show=true` beats `show=false`
570         // * `recurse=true` beats `recurse=false`
571         // * `forStubsOnly=false` beats `forStubsOnly=true`
572 
573         // The resulting showability of the item.
574         var itemShowability = Showability.NO_EFFECT
575 
576         for (annotation in item.modifiers.annotations()) {
577             val showability = annotation.showability
578             if (showability == Showability.NO_EFFECT) {
579                 // NO_EFFECT has no effect on the result so just ignore it.
580                 continue
581             }
582             itemShowability = itemShowability.combineWith(showability)
583         }
584 
585         if (item is MethodItem) {
586             // If any of a method's super methods are part of a unstable API that needs to be
587             // reverted then treat the method as if it is too.
588             val revertUnstableApi =
589                 item.superMethods().any { methodItem ->
590                     methodItem.showability.revertUnstableApi() &&
591                         // Ignore overridden methods that are not part of the API being generated if
592                         // there is no previously released API as that will always result in the
593                         // overriding method being removed which can cause problems.
594                         !(methodItem.origin != ClassOrigin.COMMAND_LINE &&
595                             previouslyReleasedCodebase == null)
596                 }
597             if (revertUnstableApi) {
598                 itemShowability = itemShowability.combineWith(REVERT_UNSTABLE_API)
599             }
600         }
601 
602         val containingClass = item.containingClass()
603         if (containingClass != null) {
604             if (containingClass.showability.revertUnstableApi()) {
605                 itemShowability = itemShowability.combineWith(REVERT_UNSTABLE_API)
606             }
607         }
608 
609         // If the item is to be reverted then find the [Item] to which it will be reverted, if any,
610         // and incorporate that into the [Showability].
611         if (itemShowability == REVERT_UNSTABLE_API) {
612             val revertItem = findRevertItem(item)
613 
614             // If the [revertItem] cannot be found then there is no need to modify the item
615             // showability as it is already in the correct state.
616             if (revertItem != null) {
617                 val forStubsOnly =
618                     if (revertItem.emit) {
619                         // The reverted item is in the API surface currently being generated, not
620                         // one that it extends, so it should always be shown. In that case
621                         // forStubsOnly will have no effect whatever the value so this uses
622                         // `NO_EFFECT` to indicate that.
623                         ShowOrHide.NO_EFFECT
624                     } else {
625                         // The item is not in the API surface being generated, so must be in one
626                         // that it extends so make sure to show it for stubs.
627                         ShowOrHide.SHOW
628                     }
629 
630                 // Update the item showability to revert to the [revertItem]. This intentionally
631                 // does not modify it to use `SHOW` or `HIDE` but keeps it using
632                 // `REVERT_UNSTABLE_API` so that it can be propagated down onto overriding methods
633                 // and nested members if applicable.
634                 itemShowability =
635                     itemShowability.copy(
636                         forStubsOnly = forStubsOnly,
637                         // Incorporate the item to be reverted into the [Showability].
638                         revertItem = revertItem,
639                     )
640 
641                 // The codebase contains items which are to be reverted.
642                 item.codebase.markContainsRevertedItem()
643             }
644         }
645 
646         return itemShowability
647     }
648 
649     /**
650      * Local cache of the previously released codebase to avoid calling the provider for every
651      * affected item.
652      */
653     private val previouslyReleasedCodebase by
654         lazy(LazyThreadSafetyMode.NONE) { config.previouslyReleasedCodebaseProvider() }
655 
656     /**
657      * Find the item to which [item] will be reverted.
658      *
659      * Searches the previously released API (if available).
660      */
661     private fun findRevertItem(item: SelectableItem): SelectableItem? {
662         return previouslyReleasedCodebase?.let { codebase ->
663             item.findCorrespondingItemIn(codebase)
664         }
665     }
666 
667     override val typedefMode: TypedefMode = config.typedefMode
668 }
669 
670 /**
671  * Extension of [AnnotationInfo] that supports initializing properties based on the
672  * [DefaultAnnotationManager.Config].
673  *
674  * The properties are initialized lazily to avoid doing more work than necessary.
675  */
676 private class LazyAnnotationInfo(
677     private val annotationManager: DefaultAnnotationManager,
678     private val config: Config,
679     private val annotationItem: AnnotationItem,
680 ) : AnnotationInfo {
681 
682     private val qualifiedName = annotationItem.qualifiedName
683 
684     override val targets by
<lambda>null685         lazy(LazyThreadSafetyMode.NONE) { annotationManager.computeTargets(annotationItem) }
686 
687     override val typeNullability = computeTypeNullability(qualifiedName)
688 
689     /** Compute lazily to avoid doing any more work than strictly necessary. */
690     override val showability by
<lambda>null691         lazy(LazyThreadSafetyMode.NONE) {
692             // The showAnnotations filter includes all the annotation patterns that are matched by
693             // the first two filters plus 0 or more additional patterns. Excluding the patterns that
694             // are purposely duplicated in showAnnotations the filters should not overlap, i.e. an
695             // AnnotationItem should not be matched by multiple filters. However, the filters could
696             // use the same annotation class (with different attributes). e.g. showAnnotations could
697             // match `@SystemApi(client=MODULE_LIBRARIES)` and showForStubPurposesAnnotations could
698             // match `@SystemApi(client=PRIVILEGED_APPS)`.
699             //
700             // Compare from most likely to match to least likely to match.
701             when {
702                 config.showAnnotations.matches(annotationItem) -> SHOW
703                 config.showForStubPurposesAnnotations.matches(annotationItem) -> SHOW_FOR_STUBS
704                 config.showSingleAnnotations.matches(annotationItem) -> SHOW_SINGLE
705                 config.hideAnnotations.matches(annotationItem) -> HIDE
706                 else -> {
707                     // Check flags before using default
708                     apiFlag?.showability ?: Showability.NO_EFFECT
709                 }
710             }
711         }
712 
<lambda>null713     override val apiFlag by lazy(LazyThreadSafetyMode.NONE) { getFlagForAnnotation(annotationItem) }
714 
getFlagForAnnotationnull715     private fun getFlagForAnnotation(annotationItem: AnnotationItem): ApiFlag? {
716         if (annotationItem.qualifiedName != ANDROID_FLAGGED_API) return null
717         val apiFlags = config.apiFlags ?: return null
718         val valueAttribute =
719             annotationItem.attributes.find { it.name == ANNOTATION_ATTR_VALUE } ?: return null
720         val flagName = valueAttribute.legacyValue.value() as String
721         return apiFlags[flagName]
722     }
723 
724     companion object {
725         /**
726          * The annotation will cause the annotated item (and any enclosed items unless overridden by
727          * a closer annotation) to be shown.
728          */
729         val SHOW =
730             Showability(
731                 show = ShowOrHide.SHOW,
732                 recursive = ShowOrHide.SHOW,
733                 forStubsOnly = ShowOrHide.NO_EFFECT,
734             )
735 
736         /**
737          * The annotation will cause the annotated item (and any enclosed items unless overridden by
738          * a closer annotation) to be shown in the stubs only.
739          */
740         val SHOW_FOR_STUBS =
741             Showability(
742                 show = ShowOrHide.NO_EFFECT,
743                 recursive = ShowOrHide.NO_EFFECT,
744                 forStubsOnly = ShowOrHide.SHOW,
745             )
746 
747         /** The annotation will cause the annotated item (but not enclosed items) to be shown. */
748         val SHOW_SINGLE =
749             Showability(
750                 show = ShowOrHide.SHOW,
751                 recursive = ShowOrHide.NO_EFFECT,
752                 forStubsOnly = ShowOrHide.NO_EFFECT,
753             )
754 
755         /**
756          * The annotation will cause the annotated item (and any enclosed items unless overridden by
757          * a closer annotation) to not be shown.
758          */
759         val HIDE =
760             Showability(
761                 show = ShowOrHide.HIDE,
762                 recursive = ShowOrHide.HIDE,
763                 forStubsOnly = ShowOrHide.NO_EFFECT,
764             )
765     }
766 
767     /** Resolve the [AnnotationItem] to a [ClassItem] lazily. */
768     private val annotationClass by lazy(LazyThreadSafetyMode.NONE, annotationItem::resolve)
769 
770     /** Flag to detect whether the [checkResolvedAnnotationClass] is in a cycle. */
771     private var isCheckingResolvedAnnotationClass = false
772 
773     /**
774      * Check to see whether the resolved annotation class matches the supplied predicate.
775      *
776      * If the annotation class could not be resolved or the annotation is part of a cycle, e.g.
777      * `java.lang.annotation.Retention` is annotated with itself, then returns false, otherwise it
778      * returns the result of applying the supplied predicate to the resolved class.
779      */
checkResolvedAnnotationClassnull780     private fun checkResolvedAnnotationClass(test: (ClassItem) -> Boolean): Boolean {
781         if (isCheckingResolvedAnnotationClass) {
782             return false
783         }
784 
785         try {
786             isCheckingResolvedAnnotationClass = true
787 
788             // Try and resolve this to the class to see if it has been annotated with hide meta
789             // annotations. If it could not be resolved then assume it has not been annotated.
790             val resolved = annotationClass ?: return false
791 
792             // Return the result of applying the test to the resolved class.
793             return test(resolved)
794         } finally {
795             isCheckingResolvedAnnotationClass = false
796         }
797     }
798 
799     /**
800      * If true then this annotation will suppress compatibility checking on annotated items.
801      *
802      * This is true if this annotation is
803      */
804     override val suppressCompatibility by
<lambda>null805         lazy(LazyThreadSafetyMode.NONE) {
806             qualifiedName == SUPPRESS_COMPATIBILITY_ANNOTATION_QUALIFIED ||
807                 config.suppressCompatibilityMetaAnnotations.contains(qualifiedName) ||
808                 checkResolvedAnnotationClass { it.hasSuppressCompatibilityMetaAnnotation() }
809         }
810 }
811