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