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