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