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