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