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