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