• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 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
18 
19 import com.android.SdkConstants
20 import com.android.SdkConstants.ATTR_VALUE
21 import com.android.SdkConstants.INT_DEF_ANNOTATION
22 import com.android.SdkConstants.LONG_DEF_ANNOTATION
23 import com.android.SdkConstants.STRING_DEF_ANNOTATION
24 import com.android.tools.lint.annotations.Extractor.ANDROID_INT_DEF
25 import com.android.tools.lint.annotations.Extractor.ANDROID_LONG_DEF
26 import com.android.tools.lint.annotations.Extractor.ANDROID_STRING_DEF
27 import com.android.tools.metalava.ANDROIDX_ANNOTATION_PREFIX
28 import com.android.tools.metalava.ANDROIDX_NONNULL
29 import com.android.tools.metalava.ANDROIDX_NULLABLE
30 import com.android.tools.metalava.ANDROID_NONNULL
31 import com.android.tools.metalava.ANDROID_NULLABLE
32 import com.android.tools.metalava.ApiPredicate
33 import com.android.tools.metalava.JAVA_LANG_PREFIX
34 import com.android.tools.metalava.Options
35 import com.android.tools.metalava.RECENTLY_NONNULL
36 import com.android.tools.metalava.RECENTLY_NULLABLE
37 import com.android.tools.metalava.model.psi.PsiBasedCodebase
38 import com.android.tools.metalava.options
39 import com.intellij.psi.PsiCallExpression
40 import com.intellij.psi.PsiField
41 import com.intellij.psi.PsiModifierListOwner
42 import com.intellij.psi.PsiReference
43 import org.jetbrains.kotlin.psi.KtObjectDeclaration
44 import org.jetbrains.uast.UElement
45 import java.util.function.Predicate
46 
isNullableAnnotationnull47 fun isNullableAnnotation(qualifiedName: String): Boolean {
48     return qualifiedName.endsWith("Nullable")
49 }
50 
isNonNullAnnotationnull51 fun isNonNullAnnotation(qualifiedName: String): Boolean {
52     return qualifiedName.endsWith("NonNull") ||
53         qualifiedName.endsWith("NotNull") ||
54         qualifiedName.endsWith("Nonnull")
55 }
56 
isJvmSyntheticAnnotationnull57 fun isJvmSyntheticAnnotation(qualifiedName: String): Boolean {
58     return qualifiedName == "kotlin.jvm.JvmSynthetic"
59 }
60 
61 interface AnnotationItem {
62     val codebase: Codebase
63 
64     /** Fully qualified name of the annotation */
65     val qualifiedName: String?
66 
67     /** Fully qualified name of the annotation (prior to name mapping) */
68     val originalName: String?
69 
70     /** Generates source code for this annotation (using fully qualified names) */
toSourcenull71     fun toSource(
72         target: AnnotationTarget = AnnotationTarget.SIGNATURE_FILE,
73         showDefaultAttrs: Boolean = true
74     ): String
75 
76     /** The applicable targets for this annotation */
77     val targets: Set<AnnotationTarget>
78 
79     /** Attributes of the annotation (may be empty) */
80     val attributes: List<AnnotationAttribute>
81 
82     /** True if this annotation represents @Nullable or @NonNull (or some synonymous annotation) */
83     fun isNullnessAnnotation(): Boolean {
84         return isNullable() || isNonNull()
85     }
86 
87     /** True if this annotation represents @Nullable (or some synonymous annotation) */
isNullablenull88     fun isNullable(): Boolean {
89         return isNullableAnnotation(qualifiedName ?: return false)
90     }
91 
92     /** True if this annotation represents @NonNull (or some synonymous annotation) */
isNonNullnull93     fun isNonNull(): Boolean {
94         return isNonNullAnnotation(qualifiedName ?: return false)
95     }
96 
97     /** True if this annotation represents @JvmSynthetic */
isJvmSyntheticnull98     fun isJvmSynthetic(): Boolean {
99         return isJvmSyntheticAnnotation(qualifiedName ?: return false)
100     }
101 
102     /** True if this annotation represents @IntDef, @LongDef or @StringDef */
isTypeDefAnnotationnull103     fun isTypeDefAnnotation(): Boolean {
104         val name = qualifiedName ?: return false
105         if (!(name.endsWith("Def"))) {
106             return false
107         }
108         return (
109             INT_DEF_ANNOTATION.isEquals(name) ||
110                 STRING_DEF_ANNOTATION.isEquals(name) ||
111                 LONG_DEF_ANNOTATION.isEquals(name) ||
112                 ANDROID_INT_DEF == name ||
113                 ANDROID_STRING_DEF == name ||
114                 ANDROID_LONG_DEF == name
115             )
116     }
117 
118     /**
119      * True if this annotation represents a @ParameterName annotation (or some synonymous annotation).
120      * The parameter name should be the default attribute or "value".
121      */
isParameterNamenull122     fun isParameterName(): Boolean {
123         return qualifiedName?.endsWith(".ParameterName") ?: return false
124     }
125 
126     /**
127      * True if this annotation represents a @DefaultValue annotation (or some synonymous annotation).
128      * The default value should be the default attribute or "value".
129      */
isDefaultValuenull130     fun isDefaultValue(): Boolean {
131         return qualifiedName?.endsWith(".DefaultValue") ?: return false
132     }
133 
134     /** Returns the given named attribute if specified */
findAttributenull135     fun findAttribute(name: String?): AnnotationAttribute? {
136         val actualName = name ?: ATTR_VALUE
137         return attributes.firstOrNull { it.name == actualName }
138     }
139 
140     /** Find the class declaration for the given annotation */
resolvenull141     fun resolve(): ClassItem? {
142         return codebase.findClass(qualifiedName ?: return null)
143     }
144 
145     /** If this annotation has a typedef annotation associated with it, return it */
findTypedefAnnotationnull146     fun findTypedefAnnotation(): AnnotationItem? {
147         val className = originalName ?: return null
148         return codebase.findClass(className)?.modifiers?.annotations()?.firstOrNull { it.isTypeDefAnnotation() }
149     }
150 
151     /** Returns the retention of this annotation */
152     val retention: AnnotationRetention
153         get() {
154             val name = qualifiedName
155             if (name != null) {
156                 val cls = codebase.findClass(name) ?: (codebase as? PsiBasedCodebase)?.findOrCreateClass(name)
157                 if (cls != null) {
158                     if (cls.isAnnotationType()) {
159                         return cls.getRetention()
160                     }
161                 }
162             }
163 
164             return AnnotationRetention.CLASS
165         }
166 
167     companion object {
168         /** The simple name of an annotation, which is the annotation name (not qualified name) prefixed by @ */
simpleNamenull169         fun simpleName(item: AnnotationItem): String {
170             return item.qualifiedName?.let { "@${it.substringAfterLast('.')}" }.orEmpty()
171         }
172 
173         /**
174          * Maps an annotation name to the name to be used in signatures/stubs/external annotation files.
175          * Annotations that should not be exported are mapped to null.
176          */
mapNamenull177         fun mapName(
178             codebase: Codebase,
179             qualifiedName: String?,
180             filter: Predicate<Item>? = null,
181             target: AnnotationTarget = AnnotationTarget.SIGNATURE_FILE
182         ): String? {
183             qualifiedName ?: return null
184             if (options.passThroughAnnotations.contains(qualifiedName)) {
185                 return qualifiedName
186             }
187             if (options.excludeAnnotations.contains(qualifiedName)) {
188                 return null
189             }
190             when (qualifiedName) {
191                 // Resource annotations
192                 "android.annotation.AnimRes" -> return "androidx.annotation.AnimRes"
193                 "android.annotation.AnimatorRes" -> return "androidx.annotation.AnimatorRes"
194                 "android.annotation.AnyRes" -> return "androidx.annotation.AnyRes"
195                 "android.annotation.ArrayRes" -> return "androidx.annotation.ArrayRes"
196                 "android.annotation.AttrRes" -> return "androidx.annotation.AttrRes"
197                 "android.annotation.BoolRes" -> return "androidx.annotation.BoolRes"
198                 "android.annotation.ColorRes" -> return "androidx.annotation.ColorRes"
199                 "android.annotation.DimenRes" -> return "androidx.annotation.DimenRes"
200                 "android.annotation.DrawableRes" -> return "androidx.annotation.DrawableRes"
201                 "android.annotation.FontRes" -> return "androidx.annotation.FontRes"
202                 "android.annotation.FractionRes" -> return "androidx.annotation.FractionRes"
203                 "android.annotation.IdRes" -> return "androidx.annotation.IdRes"
204                 "android.annotation.IntegerRes" -> return "androidx.annotation.IntegerRes"
205                 "android.annotation.InterpolatorRes" -> return "androidx.annotation.InterpolatorRes"
206                 "android.annotation.LayoutRes" -> return "androidx.annotation.LayoutRes"
207                 "android.annotation.MenuRes" -> return "androidx.annotation.MenuRes"
208                 "android.annotation.PluralsRes" -> return "androidx.annotation.PluralsRes"
209                 "android.annotation.RawRes" -> return "androidx.annotation.RawRes"
210                 "android.annotation.StringRes" -> return "androidx.annotation.StringRes"
211                 "android.annotation.StyleRes" -> return "androidx.annotation.StyleRes"
212                 "android.annotation.StyleableRes" -> return "androidx.annotation.StyleableRes"
213                 "android.annotation.TransitionRes" -> return "androidx.annotation.TransitionRes"
214                 "android.annotation.XmlRes" -> return "androidx.annotation.XmlRes"
215 
216                 // Threading
217                 "android.annotation.AnyThread" -> return "androidx.annotation.AnyThread"
218                 "android.annotation.BinderThread" -> return "androidx.annotation.BinderThread"
219                 "android.annotation.MainThread" -> return "androidx.annotation.MainThread"
220                 "android.annotation.UiThread" -> return "androidx.annotation.UiThread"
221                 "android.annotation.WorkerThread" -> return "androidx.annotation.WorkerThread"
222 
223                 // Colors
224                 "android.annotation.ColorInt" -> return "androidx.annotation.ColorInt"
225                 "android.annotation.ColorLong" -> return "androidx.annotation.ColorLong"
226                 "android.annotation.HalfFloat" -> return "androidx.annotation.HalfFloat"
227 
228                 // Ranges and sizes
229                 "android.annotation.FloatRange" -> return "androidx.annotation.FloatRange"
230                 "android.annotation.IntRange" -> return "androidx.annotation.IntRange"
231                 "android.annotation.Size" -> return "androidx.annotation.Size"
232                 "android.annotation.Px" -> return "androidx.annotation.Px"
233                 "android.annotation.Dimension" -> return "androidx.annotation.Dimension"
234 
235                 // Null
236                 // We only change recently/newly nullable annotation in stubs
237                 RECENTLY_NULLABLE -> return if (target == AnnotationTarget.SDK_STUBS_FILE) qualifiedName else ANDROIDX_NULLABLE
238                 RECENTLY_NONNULL -> return if (target == AnnotationTarget.SDK_STUBS_FILE) qualifiedName else ANDROIDX_NONNULL
239 
240                 ANDROIDX_NULLABLE,
241                 ANDROID_NULLABLE,
242                 "libcore.util.Nullable",
243                 "org.jetbrains.annotations.Nullable" -> return nullableAnnotationName(target)
244 
245                 ANDROIDX_NONNULL,
246                 ANDROID_NONNULL,
247                 "libcore.util.NonNull",
248                 "org.jetbrains.annotations.NotNull" -> return nonNullAnnotationName(target)
249 
250                 // Typedefs
251                 "android.annotation.IntDef" -> return "androidx.annotation.IntDef"
252                 "android.annotation.StringDef" -> return "androidx.annotation.StringDef"
253                 "android.annotation.LongDef" -> return "androidx.annotation.LongDef"
254 
255                 // Context Types
256                 "android.annotation.UiContext" -> return "androidx.annotation.UiContext"
257                 "android.annotation.DisplayContext" -> return "androidx.annotation.DisplayContext"
258                 "android.annotation.NonUiContext" -> return "androidx.annotation.NonUiContext"
259 
260                 // Misc
261                 "android.annotation.DeprecatedForSdk" -> return "java.lang.Deprecated"
262                 "android.annotation.CallSuper" -> return "androidx.annotation.CallSuper"
263                 "android.annotation.CheckResult" -> return "androidx.annotation.CheckResult"
264                 "android.annotation.Discouraged" -> return "androidx.annotation.Discouraged"
265                 "android.annotation.RequiresPermission" -> return "androidx.annotation.RequiresPermission"
266                 "android.annotation.RequiresPermission.Read" -> return "androidx.annotation.RequiresPermission.Read"
267                 "android.annotation.RequiresPermission.Write" -> return "androidx.annotation.RequiresPermission.Write"
268 
269                 // These aren't support annotations, but could/should be:
270                 "android.annotation.CurrentTimeMillisLong",
271                 "android.annotation.DurationMillisLong",
272                 "android.annotation.ElapsedRealtimeLong",
273                 "android.annotation.UserIdInt",
274                 "android.annotation.BytesLong",
275 
276                 // These aren't support annotations
277                 "android.annotation.AppIdInt",
278                 "android.annotation.SuppressAutoDoc",
279                 "android.annotation.SystemApi",
280                 "android.annotation.TestApi",
281                 "android.annotation.CallbackExecutor",
282                 "android.annotation.Condemned",
283                 "android.annotation.Hide",
284 
285                 "android.annotation.Widget" -> {
286                     // Remove, unless (a) public or (b) specifically included in --showAnnotations
287                     return if (options.showAnnotations.matches(qualifiedName)) {
288                         qualifiedName
289                     } else if (filter != null) {
290                         val cls = codebase.findClass(qualifiedName)
291                         if (cls != null && filter.test(cls)) {
292                             qualifiedName
293                         } else {
294                             null
295                         }
296                     } else {
297                         qualifiedName
298                     }
299                 }
300 
301                 // Included for analysis, but should not be exported:
302                 "android.annotation.BroadcastBehavior",
303                 "android.annotation.SdkConstant",
304                 "android.annotation.RequiresFeature",
305                 "android.annotation.SystemService" -> return qualifiedName
306 
307                 // Should not be mapped to a different package name:
308                 "android.annotation.TargetApi",
309                 "android.annotation.SuppressLint" -> return qualifiedName
310 
311                 else -> {
312                     // Some new annotations added to the platform: assume they are support annotations?
313                     return when {
314                         // Special Kotlin annotations recognized by the compiler: map to supported package name
315                         qualifiedName.endsWith(".ParameterName") || qualifiedName.endsWith(".DefaultValue") ->
316                             "kotlin.annotations.jvm.internal${qualifiedName.substring(qualifiedName.lastIndexOf('.'))}"
317 
318                         // Other third party nullness annotations?
319                         isNullableAnnotation(qualifiedName) -> nullableAnnotationName(target)
320                         isNonNullAnnotation(qualifiedName) -> nonNullAnnotationName(target)
321 
322                         // AndroidX annotations are all included, as is the built-in stuff like @Retention
323                         qualifiedName.startsWith(ANDROIDX_ANNOTATION_PREFIX) -> return qualifiedName
324                         qualifiedName.startsWith(JAVA_LANG_PREFIX) -> return qualifiedName
325 
326                         // Unknown Android platform annotations
327                         qualifiedName.startsWith("android.annotation.") -> {
328                             // Remove, unless specifically included in --showAnnotations
329                             return if (options.showAnnotations.matches(qualifiedName)) {
330                                 qualifiedName
331                             } else {
332                                 null
333                             }
334                         }
335 
336                         else -> {
337                             // Remove, unless (a) public or (b) specifically included in --showAnnotations
338                             return if (options.showAnnotations.matches(qualifiedName)) {
339                                 qualifiedName
340                             } else if (filter != null) {
341                                 val cls = codebase.findClass(qualifiedName)
342                                 if (cls != null && filter.test(cls)) {
343                                     qualifiedName
344                                 } else {
345                                     null
346                                 }
347                             } else {
348                                 qualifiedName
349                             }
350                         }
351                     }
352                 }
353             }
354         }
355 
nullableAnnotationNamenull356         private fun nullableAnnotationName(target: AnnotationTarget) =
357             if (target == AnnotationTarget.SDK_STUBS_FILE) ANDROID_NULLABLE else ANDROIDX_NULLABLE
358 
359         private fun nonNullAnnotationName(target: AnnotationTarget) =
360             if (target == AnnotationTarget.SDK_STUBS_FILE) ANDROID_NONNULL else ANDROIDX_NONNULL
361 
362         private val TYPEDEF_ANNOTATION_TARGETS =
363             if (options.typedefMode == Options.TypedefMode.INLINE ||
364                 options.typedefMode == Options.TypedefMode.NONE
365             ) // just here for compatibility purposes
366                 ANNOTATION_EXTERNAL
367             else
368                 ANNOTATION_EXTERNAL_ONLY
369 
370         /** The applicable targets for this annotation */
371         fun computeTargets(
372             annotation: AnnotationItem,
373             classFinder: (String) -> ClassItem?
374         ): Set<AnnotationTarget> {
375             val qualifiedName = annotation.qualifiedName ?: return NO_ANNOTATION_TARGETS
376             if (options.passThroughAnnotations.contains(qualifiedName)) {
377                 return ANNOTATION_IN_ALL_STUBS
378             }
379             when (qualifiedName) {
380 
381                 // The typedef annotations are special: they should not be in the signature
382                 // files, but we want to include them in the external annotations file such that tools
383                 // can enforce them.
384                 "android.annotation.IntDef",
385                 "androidx.annotation.IntDef",
386                 "android.annotation.StringDef",
387                 "androidx.annotation.StringDef",
388                 "android.annotation.LongDef",
389                 "androidx.annotation.LongDef" -> return TYPEDEF_ANNOTATION_TARGETS
390 
391                 // Not directly API relevant
392                 "android.view.ViewDebug.ExportedProperty",
393                 "android.view.ViewDebug.CapturedViewProperty" -> return ANNOTATION_STUBS_ONLY
394 
395                 // Retained in the sdk/jar stub source code so that SdkConstant files can be extracted
396                 // from those. This is useful for modularizing the main SDK stubs without having to
397                 // add a separate module SDK artifact for sdk constants.
398                 "android.annotation.SdkConstant" -> return ANNOTATION_SDK_STUBS_ONLY
399 
400                 // Skip known annotations that we (a) never want in external annotations and (b) we are
401                 // specially overwriting anyway in the stubs (and which are (c) not API significant)
402                 "com.android.modules.annotation.MinSdk",
403                 "java.lang.annotation.Native",
404                 "java.lang.SuppressWarnings",
405                 "java.lang.Override",
406                 "kotlin.Suppress",
407                 "androidx.annotation.experimental.UseExperimental",
408                 "androidx.annotation.OptIn",
409                 "kotlin.UseExperimental",
410                 "kotlin.OptIn" -> return NO_ANNOTATION_TARGETS
411 
412                 // TODO(aurimas): consider using annotation directly instead of modifiers
413                 "kotlin.Deprecated" -> return NO_ANNOTATION_TARGETS // tracked separately as a pseudo-modifier
414                 "android.annotation.DeprecatedForSdk",
415                 "java.lang.Deprecated", // tracked separately as a pseudo-modifier
416 
417                 // Below this when-statement we perform the correct lookup: check API predicate, and check
418                 // that retention is class or runtime, but we've hardcoded the answers here
419                 // for some common annotations.
420 
421                 "android.widget.RemoteViews.RemoteView",
422 
423                 "kotlin.annotation.Target",
424                 "kotlin.annotation.Retention",
425                 "kotlin.annotation.Repeatable",
426                 "kotlin.annotation.MustBeDocumented",
427                 "kotlin.DslMarker",
428                 "kotlin.PublishedApi",
429                 "kotlin.ExtensionFunctionType",
430 
431                 "java.lang.FunctionalInterface",
432                 "java.lang.SafeVarargs",
433                 "java.lang.annotation.Documented",
434                 "java.lang.annotation.Inherited",
435                 "java.lang.annotation.Repeatable",
436                 "java.lang.annotation.Retention",
437                 "java.lang.annotation.Target" -> return ANNOTATION_IN_ALL_STUBS
438 
439                 // Metalava already tracks all the methods that get generated due to these annotations.
440                 "kotlin.jvm.JvmOverloads",
441                 "kotlin.jvm.JvmField",
442                 "kotlin.jvm.JvmStatic",
443                 "kotlin.jvm.JvmName" -> return NO_ANNOTATION_TARGETS
444             }
445 
446             // @android.annotation.Nullable and NonNullable specially recognized annotations by the Kotlin
447             // compiler 1.3 and above: they always go in the stubs.
448             if (qualifiedName == ANDROID_NULLABLE ||
449                 qualifiedName == ANDROID_NONNULL ||
450                 qualifiedName == ANDROIDX_NULLABLE ||
451                 qualifiedName == ANDROIDX_NONNULL
452             ) {
453                 return ANNOTATION_IN_ALL_STUBS
454             }
455 
456             if (qualifiedName.startsWith("android.annotation.")) {
457                 // internal annotations not mapped to androidx: things like @SystemApi. Skip from
458                 // stubs, external annotations, signature files, etc.
459                 return NO_ANNOTATION_TARGETS
460             }
461 
462             // @RecentlyNullable and @RecentlyNonNull are specially recognized annotations by the Kotlin
463             // compiler: they always go in the stubs.
464             if (qualifiedName == RECENTLY_NULLABLE ||
465                 qualifiedName == RECENTLY_NONNULL
466             ) {
467                 return ANNOTATION_IN_ALL_STUBS
468             }
469 
470             // Determine the retention of the annotation: source retention annotations go
471             // in the external annotations file, class and runtime annotations go in
472             // the stubs files (except for the androidx annotations which are not included
473             // in the SDK and therefore cannot be referenced from it due to apt's unfortunate
474             // habit of loading all annotation classes it encounters.)
475 
476             if (qualifiedName.startsWith("androidx.annotation.")) {
477                 if (options.includeSourceRetentionAnnotations) {
478                     return ANNOTATION_IN_ALL_STUBS
479                 }
480 
481                 if (qualifiedName == ANDROIDX_NULLABLE || qualifiedName == ANDROIDX_NONNULL) {
482                     // Right now, nullness annotations (other than @RecentlyNullable and @RecentlyNonNull)
483                     // have to go in external annotations since they aren't in the class path for
484                     // annotation processors. However, we do want them showing up in the documentation using
485                     // their real annotation names.
486                     return ANNOTATION_IN_DOC_STUBS_AND_EXTERNAL
487                 }
488 
489                 return ANNOTATION_EXTERNAL
490             }
491 
492             // See if the annotation is pointing to an annotation class that is part of the API; if not, skip it.
493             val cls = classFinder(qualifiedName) ?: return NO_ANNOTATION_TARGETS
494             if (!ApiPredicate().test(cls)) {
495                 if (options.typedefMode != Options.TypedefMode.NONE) {
496                     if (cls.modifiers.annotations().any { it.isTypeDefAnnotation() }) {
497                         return ANNOTATION_SIGNATURE_ONLY
498                     }
499                 }
500 
501                 return NO_ANNOTATION_TARGETS
502             }
503 
504             if (cls.isAnnotationType()) {
505                 val retention = cls.getRetention()
506                 if (retention == AnnotationRetention.RUNTIME || retention == AnnotationRetention.CLASS) {
507                     return ANNOTATION_IN_ALL_STUBS
508                 }
509             }
510 
511             return ANNOTATION_EXTERNAL
512         }
513 
514         /**
515          * Given a "full" annotation name, shortens it by removing redundant package names.
516          * This is intended to be used to reduce clutter in signature files.
517          *
518          * For example, this method will convert `@androidx.annotation.Nullable` to just
519          * `@Nullable`, and `@androidx.annotation.IntRange(from=20)` to `IntRange(from=20)`.
520          */
shortenAnnotationnull521         fun shortenAnnotation(source: String): String {
522             return when {
523                 source == "@java.lang.Deprecated" -> "@Deprecated"
524                 source.startsWith("android.annotation.", 1) -> {
525                     "@" + source.substring("@android.annotation.".length)
526                 }
527                 source.startsWith(ANDROIDX_ANNOTATION_PREFIX, 1) -> {
528                     "@" + source.substring("@androidx.annotation.".length)
529                 }
530                 else -> source
531             }
532         }
533 
534         /**
535          * Reverses the [shortenAnnotation] method. Intended for use when reading in signature files
536          * that contain shortened type references.
537          */
unshortenAnnotationnull538         fun unshortenAnnotation(source: String): String {
539             return when {
540                 source == "@Deprecated" -> "@java.lang.Deprecated"
541                 // These 3 annotations are in the android.annotation. package, not androidx.annotation
542                 source.startsWith("@SystemService") ||
543                     source.startsWith("@TargetApi") ||
544                     source.startsWith("@SuppressLint") ->
545                     "@android.annotation." + source.substring(1)
546                 // If the first character of the name (after "@") is lower-case, then
547                 // assume it's a package name, so no need to shorten it.
548                 source.startsWith("@") && source[1].isLowerCase() -> source
549                 else -> {
550                     "@androidx.annotation." + source.substring(1)
551                 }
552             }
553         }
554 
555         /**
556          * If the given element has an *implicit* nullness, return it. This returns
557          * true for implicitly nullable elements, such as the parameter to the equals
558          * method, false for implicitly non null elements (such as annotation type
559          * members), and null if there is no implicit nullness.
560          */
getImplicitNullnessnull561         fun getImplicitNullness(item: Item): Boolean? {
562             var nullable: Boolean? = null
563 
564             // Is this a Kotlin object declaration (such as a companion object) ?
565             // If so, it is always non null.
566             val sourcePsi = item.psi()
567             if (sourcePsi is UElement && sourcePsi.sourcePsi is KtObjectDeclaration) {
568                 nullable = false
569             }
570 
571             // Constant field not initialized to null?
572             if (item is FieldItem &&
573                 (item.isEnumConstant() || item.modifiers.isFinal() && item.initialValue(false) != null)
574             ) {
575                 // Assigned to constant: not nullable
576                 nullable = false
577             } else if (item is FieldItem && item.modifiers.isFinal()) {
578                 // If we're looking at a final field, look at the right hand side
579                 // of the field to the field initialization. If that right hand side
580                 // for example represents a method call, and the method we're calling
581                 // is annotated with @NonNull, then the field (since it is final) will
582                 // always be @NonNull as well.
583                 val initializer = (item.psi() as? PsiField)?.initializer
584                 if (initializer != null && initializer is PsiReference) {
585                     val resolved = initializer.resolve()
586                     if (resolved is PsiModifierListOwner &&
587                         resolved.annotations.any {
588                             isNonNullAnnotation(it.qualifiedName ?: "")
589                         }
590                     ) {
591                         nullable = false
592                     }
593                 } else if (initializer != null && initializer is PsiCallExpression) {
594                     val resolved = initializer.resolveMethod()
595                     if (resolved != null &&
596                         resolved.annotations.any {
597                             isNonNullAnnotation(it.qualifiedName ?: "")
598                         }
599                     ) {
600                         nullable = false
601                     }
602                 }
603             } else if (item.synthetic && (
604                 item is MethodItem && item.isEnumSyntheticMethod() ||
605                     item is ParameterItem && item.containingMethod().isEnumSyntheticMethod()
606                 )
607             ) {
608                 // Workaround the fact that the Kotlin synthetic enum methods
609                 // do not have nullness information
610                 nullable = false
611             }
612 
613             // Annotation type members cannot be null
614             if (item is MemberItem && item.containingClass().isAnnotationType()) {
615                 nullable = false
616             }
617 
618             // Equals and toString have known nullness
619             if (item is MethodItem && item.name() == "toString" && item.parameters().isEmpty()) {
620                 nullable = false
621             } else if (item is ParameterItem && item.containingMethod().name() == "equals" &&
622                 item.containingMethod().parameters().size == 1
623             ) {
624                 nullable = true
625             }
626 
627             return nullable
628         }
629     }
630 }
631 
632 /** Default implementation of an annotation item */
633 abstract class DefaultAnnotationItem(override val codebase: Codebase) : AnnotationItem {
<lambda>null634     override val targets: Set<AnnotationTarget> by lazy {
635         AnnotationItem.computeTargets(this, codebase::findClass)
636     }
637 }
638 
639 /** An attribute of an annotation, such as "value" */
640 interface AnnotationAttribute {
641     /** The name of the annotation */
642     val name: String
643     /** The annotation value */
644     val value: AnnotationAttributeValue
645 
646     /**
647      * Return all leaf values; this flattens the complication of handling
648      * {@code @SuppressLint("warning")} and {@code @SuppressLint({"warning1","warning2"})
649      */
leafValuesnull650     fun leafValues(): List<AnnotationAttributeValue> {
651         val result = mutableListOf<AnnotationAttributeValue>()
652         AnnotationAttributeValue.addValues(value, result)
653         return result
654     }
655 }
656 
657 /** An annotation value */
658 interface AnnotationAttributeValue {
659     /** Generates source code for this annotation value */
toSourcenull660     fun toSource(): String
661 
662     /** The value of the annotation */
663     fun value(): Any?
664 
665     /** If the annotation declaration references a field (or class etc), return the resolved class */
666     fun resolve(): Item?
667 
668     companion object {
669         fun addValues(value: AnnotationAttributeValue, into: MutableList<AnnotationAttributeValue>) {
670             if (value is AnnotationArrayAttributeValue) {
671                 for (v in value.values) {
672                     addValues(v, into)
673                 }
674             } else if (value is AnnotationSingleAttributeValue) {
675                 into.add(value)
676             }
677         }
678     }
679 }
680 
681 /** An annotation value (for a single item, not an array) */
682 interface AnnotationSingleAttributeValue : AnnotationAttributeValue {
683     /** The annotation value, expressed as source code */
684     val valueSource: String
685     /** The annotation value */
686     val value: Any?
687 
valuenull688     override fun value() = value
689 }
690 
691 /** An annotation value for an array of items */
692 interface AnnotationArrayAttributeValue : AnnotationAttributeValue {
693     /** The annotation values */
694     val values: List<AnnotationAttributeValue>
695 
696     override fun resolve(): Item? {
697         error("resolve() should not be called on an array value")
698     }
699 
700     override fun value() = values.mapNotNull { it.value() }.toTypedArray()
701 }
702 
703 class DefaultAnnotationAttribute(
704     override val name: String,
705     override val value: DefaultAnnotationValue
706 ) : AnnotationAttribute {
707     companion object {
createnull708         fun create(name: String, value: String): DefaultAnnotationAttribute {
709             return DefaultAnnotationAttribute(name, DefaultAnnotationValue.create(value))
710         }
711 
createListnull712         fun createList(source: String): List<AnnotationAttribute> {
713             val list = mutableListOf<AnnotationAttribute>() // TODO: default size = 2
714             var begin = 0
715             var index = 0
716             val length = source.length
717             while (index < length) {
718                 val c = source[index]
719                 if (c == '{') {
720                     index = findEnd(source, index + 1, length, '}')
721                 } else if (c == '"') {
722                     index = findEnd(source, index + 1, length, '"')
723                 } else if (c == ',') {
724                     addAttribute(list, source, begin, index)
725                     index++
726                     begin = index
727                     continue
728                 } else if (c == ' ' && index == begin) {
729                     begin++
730                 }
731 
732                 index++
733             }
734 
735             if (begin < length) {
736                 addAttribute(list, source, begin, length)
737             }
738 
739             return list
740         }
741 
findEndnull742         private fun findEnd(source: String, from: Int, to: Int, sentinel: Char): Int {
743             var i = from
744             while (i < to) {
745                 val c = source[i]
746                 if (c == '\\') {
747                     i++
748                 } else if (c == sentinel) {
749                     return i
750                 }
751                 i++
752             }
753             return to
754         }
755 
addAttributenull756         private fun addAttribute(list: MutableList<AnnotationAttribute>, source: String, from: Int, to: Int) {
757             var split = source.indexOf('=', from)
758             if (split >= to) {
759                 split = -1
760             }
761             val name: String
762             val value: String
763             val valueBegin: Int
764             val valueEnd: Int
765             if (split == -1) {
766                 valueBegin = split + 1
767                 valueEnd = to
768                 name = "value"
769             } else {
770                 name = source.substring(from, split).trim()
771                 valueBegin = split + 1
772                 valueEnd = to
773             }
774             value = source.substring(valueBegin, valueEnd).trim()
775             list.add(DefaultAnnotationAttribute.create(name, value))
776         }
777     }
778 
toStringnull779     override fun toString(): String {
780         return "DefaultAnnotationAttribute(name='$name', value=$value)"
781     }
782 }
783 
784 abstract class DefaultAnnotationValue : AnnotationAttributeValue {
785     companion object {
createnull786         fun create(value: String): DefaultAnnotationValue {
787             return if (value.startsWith("{")) { // Array
788                 DefaultAnnotationArrayAttributeValue(value)
789             } else {
790                 DefaultAnnotationSingleAttributeValue(value)
791             }
792         }
793     }
794 
toStringnull795     override fun toString(): String = toSource()
796 }
797 
798 class DefaultAnnotationSingleAttributeValue(override val valueSource: String) :
799     DefaultAnnotationValue(),
800     AnnotationSingleAttributeValue {
801     @Suppress("IMPLICIT_CAST_TO_ANY")
802     override val value = when {
803         valueSource == SdkConstants.VALUE_TRUE -> true
804         valueSource == SdkConstants.VALUE_FALSE -> false
805         valueSource.startsWith("\"") -> valueSource.removeSurrounding("\"")
806         valueSource.startsWith('\'') -> valueSource.removeSurrounding("'")[0]
807         else -> try {
808             if (valueSource.contains(".")) {
809                 valueSource.toDouble()
810             } else {
811                 valueSource.toLong()
812             }
813         } catch (e: NumberFormatException) {
814             valueSource
815         }
816     }
817 
818     override fun resolve(): Item? = null
819 
820     override fun toSource() = valueSource
821 }
822 
823 class DefaultAnnotationArrayAttributeValue(val value: String) :
824     DefaultAnnotationValue(),
825     AnnotationArrayAttributeValue {
826     init {
<lambda>null827         assert(value.startsWith("{") && value.endsWith("}")) { value }
828     }
829 
<lambda>null830     override val values = value.substring(1, value.length - 1).split(",").map {
831         DefaultAnnotationValue.create(it.trim())
832     }.toList()
833 
toSourcenull834     override fun toSource() = value
835 }
836