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