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