• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.psi
18 
19 import com.android.tools.metalava.ANDROIDX_VISIBLE_FOR_TESTING
20 import com.android.tools.metalava.ANDROID_DEPRECATED_FOR_SDK
21 import com.android.tools.metalava.ATTR_ALLOW_IN
22 import com.android.tools.metalava.ATTR_OTHERWISE
23 import com.android.tools.metalava.model.AnnotationItem
24 import com.android.tools.metalava.model.Codebase
25 import com.android.tools.metalava.model.DefaultModifierList
26 import com.android.tools.metalava.model.ModifierList
27 import com.android.tools.metalava.model.MutableModifierList
28 import com.android.tools.metalava.options
29 import com.intellij.psi.PsiDocCommentOwner
30 import com.intellij.psi.PsiModifier
31 import com.intellij.psi.PsiModifierList
32 import com.intellij.psi.PsiModifierListOwner
33 import com.intellij.psi.PsiPrimitiveType
34 import com.intellij.psi.PsiReferenceExpression
35 import com.intellij.psi.impl.light.LightModifierList
36 import org.jetbrains.annotations.NotNull
37 import org.jetbrains.annotations.Nullable
38 import org.jetbrains.kotlin.asJava.elements.KtLightModifierList
39 import org.jetbrains.kotlin.asJava.elements.KtLightNullabilityAnnotation
40 import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithVisibility
41 import org.jetbrains.kotlin.descriptors.EffectiveVisibility
42 import org.jetbrains.kotlin.descriptors.effectiveVisibility
43 import org.jetbrains.kotlin.lexer.KtTokens
44 import org.jetbrains.kotlin.psi.KtAnnotated
45 import org.jetbrains.kotlin.psi.KtElement
46 import org.jetbrains.kotlin.psi.KtModifierList
47 import org.jetbrains.kotlin.psi.KtModifierListOwner
48 import org.jetbrains.kotlin.psi.KtNamedFunction
49 import org.jetbrains.kotlin.psi.KtPropertyAccessor
50 import org.jetbrains.kotlin.psi.psiUtil.hasFunModifier
51 import org.jetbrains.kotlin.psi.psiUtil.visibilityModifier
52 import org.jetbrains.kotlin.resolve.BindingContext
53 import org.jetbrains.uast.UAnnotated
54 import org.jetbrains.uast.UAnnotation
55 import org.jetbrains.uast.UElement
56 import org.jetbrains.uast.UMethod
57 import org.jetbrains.uast.UVariable
58 
59 class PsiModifierItem(
60     codebase: Codebase,
61     flags: Int = PACKAGE_PRIVATE,
62     annotations: MutableList<AnnotationItem>? = null
63 ) : DefaultModifierList(codebase, flags, annotations), ModifierList, MutableModifierList {
64     companion object {
65         fun create(
66             codebase: PsiBasedCodebase,
67             element: PsiModifierListOwner,
68             documentation: String?
69         ): PsiModifierItem {
70             val modifiers =
71                 if (element is UAnnotated) {
72                     create(codebase, element, element)
73                 } else {
74                     create(codebase, element)
75                 }
76             if (documentation?.contains("@deprecated") == true ||
77                 // Check for @Deprecated annotation
78                 ((element as? PsiDocCommentOwner)?.isDeprecated == true) ||
79                 // Check for @Deprecated on sourcePsi
80                 isDeprecatedFromSourcePsi(element)
81             ) {
82                 modifiers.setDeprecated(true)
83             }
84 
85             return modifiers
86         }
87 
88         private fun isDeprecatedFromSourcePsi(element: PsiModifierListOwner): Boolean {
89             return ((element as? UElement)?.sourcePsi as? KtAnnotated)?.annotationEntries?.any {
90                 it.shortName?.toString() == "Deprecated"
91             } ?: false
92         }
93 
94         private fun computeFlag(
95             codebase: PsiBasedCodebase,
96             element: PsiModifierListOwner,
97             modifierList: PsiModifierList
98         ): Int {
99             var flags = 0
100             if (modifierList.hasModifierProperty(PsiModifier.STATIC)) {
101                 flags = flags or STATIC
102             }
103             if (modifierList.hasModifierProperty(PsiModifier.ABSTRACT)) {
104                 flags = flags or ABSTRACT
105             }
106             if (modifierList.hasModifierProperty(PsiModifier.FINAL)) {
107                 flags = flags or FINAL
108             }
109             if (modifierList.hasModifierProperty(PsiModifier.NATIVE)) {
110                 flags = flags or NATIVE
111             }
112             if (modifierList.hasModifierProperty(PsiModifier.SYNCHRONIZED)) {
113                 flags = flags or SYNCHRONIZED
114             }
115             if (modifierList.hasModifierProperty(PsiModifier.STRICTFP)) {
116                 flags = flags or STRICT_FP
117             }
118             if (modifierList.hasModifierProperty(PsiModifier.TRANSIENT)) {
119                 flags = flags or TRANSIENT
120             }
121             if (modifierList.hasModifierProperty(PsiModifier.VOLATILE)) {
122                 flags = flags or VOLATILE
123             }
124             if (modifierList.hasModifierProperty(PsiModifier.DEFAULT)) {
125                 flags = flags or DEFAULT
126             }
127 
128             // Look for special Kotlin keywords
129             var ktModifierList: KtModifierList? = null
130             val sourcePsi = (element as? UElement)?.sourcePsi
131             if (modifierList is KtLightModifierList<*>) {
132                 ktModifierList = modifierList.kotlinOrigin
133             } else if (modifierList is LightModifierList && element is UMethod) {
134                 if (sourcePsi is KtModifierListOwner) {
135                     ktModifierList = sourcePsi.modifierList
136                 }
137             }
138             var visibilityFlags = when {
139                 modifierList.hasModifierProperty(PsiModifier.PUBLIC) -> PUBLIC
140                 modifierList.hasModifierProperty(PsiModifier.PROTECTED) -> PROTECTED
141                 modifierList.hasModifierProperty(PsiModifier.PRIVATE) -> PRIVATE
142                 ktModifierList != null -> when {
143                     ktModifierList.hasModifier(KtTokens.PRIVATE_KEYWORD) -> PRIVATE
144                     ktModifierList.hasModifier(KtTokens.PROTECTED_KEYWORD) -> PROTECTED
145                     ktModifierList.hasModifier(KtTokens.INTERNAL_KEYWORD) -> INTERNAL
146                     else -> PUBLIC
147                 }
148                 else -> PACKAGE_PRIVATE
149             }
150             if (ktModifierList != null) {
151                 if (ktModifierList.hasModifier(KtTokens.INTERNAL_KEYWORD)) {
152                     // Reset visibilityFlags to INTERNAL if the internal modifier is explicitly
153                     // present on the element
154                     visibilityFlags = INTERNAL
155                 } else if (
156                     ktModifierList.hasModifier(KtTokens.OVERRIDE_KEYWORD) &&
157                     ktModifierList.visibilityModifier() == null &&
158                     sourcePsi is KtElement
159                 ) {
160                     // Reset visibilityFlags to INTERNAL if the element has no explicit visibility
161                     // modifier, but overrides an internal declaration. Adapted from
162                     // org.jetbrains.kotlin.asJava.classes.UltraLightMembersCreator.isInternal
163                     val descriptor = codebase.bindingContext(sourcePsi)
164                         .get(BindingContext.DECLARATION_TO_DESCRIPTOR, sourcePsi)
165 
166                     if (descriptor is DeclarationDescriptorWithVisibility) {
167                         val effectiveVisibility =
168                             descriptor.visibility.effectiveVisibility(descriptor, false)
169 
170                         if (effectiveVisibility == EffectiveVisibility.Internal) {
171                             visibilityFlags = INTERNAL
172                         }
173                     }
174                 }
175                 if (ktModifierList.hasModifier(KtTokens.VARARG_KEYWORD)) {
176                     flags = flags or VARARG
177                 }
178                 if (ktModifierList.hasModifier(KtTokens.SEALED_KEYWORD)) {
179                     flags = flags or SEALED
180                 }
181                 if (ktModifierList.hasModifier(KtTokens.INFIX_KEYWORD)) {
182                     flags = flags or INFIX
183                 }
184                 if (ktModifierList.hasModifier(KtTokens.CONST_KEYWORD)) {
185                     flags = flags or CONST
186                 }
187                 if (ktModifierList.hasModifier(KtTokens.OPERATOR_KEYWORD)) {
188                     flags = flags or OPERATOR
189                 }
190                 if (ktModifierList.hasModifier(KtTokens.INLINE_KEYWORD)) {
191                     flags = flags or INLINE
192 
193                     // Workaround for b/117565118:
194                     val func = sourcePsi as? KtNamedFunction
195                     if (func != null &&
196                         (func.typeParameterList?.text ?: "").contains("reified") &&
197                         !ktModifierList.hasModifier(KtTokens.PRIVATE_KEYWORD) &&
198                         !ktModifierList.hasModifier(KtTokens.INTERNAL_KEYWORD)
199                     ) {
200                         // Switch back from private to public
201                         visibilityFlags = PUBLIC
202                     }
203                 }
204                 if (ktModifierList.hasModifier(KtTokens.VALUE_KEYWORD)) {
205                     flags = flags or VALUE
206                 }
207                 if (ktModifierList.hasModifier(KtTokens.SUSPEND_KEYWORD)) {
208                     flags = flags or SUSPEND
209                 }
210                 if (ktModifierList.hasModifier(KtTokens.COMPANION_KEYWORD)) {
211                     flags = flags or COMPANION
212                 }
213                 if (ktModifierList.hasFunModifier()) {
214                     flags = flags or FUN
215                 }
216                 if (ktModifierList.hasModifier(KtTokens.DATA_KEYWORD)) {
217                     flags = flags or DATA
218                 }
219             }
220             // Methods that are property accessors inherit visibility from the source element
221             if (element is UMethod && (element.sourceElement is KtPropertyAccessor)) {
222                 val sourceElement = element.sourceElement
223                 if (sourceElement is KtModifierListOwner) {
224                     val sourceModifierList = sourceElement.modifierList
225                     if (sourceModifierList != null) {
226                         if (sourceModifierList.hasModifier(KtTokens.INTERNAL_KEYWORD)) {
227                             visibilityFlags = INTERNAL
228                         }
229                     }
230                 }
231             }
232 
233             // Merge in the visibility flags.
234             flags = flags or visibilityFlags
235 
236             return flags
237         }
238 
239         private fun create(codebase: PsiBasedCodebase, element: PsiModifierListOwner): PsiModifierItem {
240             val modifierList = element.modifierList ?: return PsiModifierItem(codebase)
241             var flags = computeFlag(codebase, element, modifierList)
242 
243             val psiAnnotations = modifierList.annotations
244             return if (psiAnnotations.isEmpty()) {
245                 PsiModifierItem(codebase, flags)
246             } else {
247                 val annotations: MutableList<AnnotationItem> =
248                     // psi sometimes returns duplicate annotations, using distinct() to counter that.
249                     psiAnnotations.distinct().map {
250                         val qualifiedName = it.qualifiedName
251                         // Consider also supporting com.android.internal.annotations.VisibleForTesting?
252                         if (qualifiedName == ANDROIDX_VISIBLE_FOR_TESTING) {
253                             val otherwise = it.findAttributeValue(ATTR_OTHERWISE)
254                             val ref = when {
255                                 otherwise is PsiReferenceExpression -> otherwise.referenceName ?: ""
256                                 otherwise != null -> otherwise.text
257                                 else -> ""
258                             }
259                             flags = getVisibilityFlag(ref, flags)
260                         }
261 
262                         PsiAnnotationItem.create(codebase, it, qualifiedName)
263                     }.filter { !it.isDeprecatedForSdk() }.toMutableList()
264                 PsiModifierItem(codebase, flags, annotations)
265             }
266         }
267 
268         private fun create(
269             codebase: PsiBasedCodebase,
270             element: PsiModifierListOwner,
271             annotated: UAnnotated
272         ): PsiModifierItem {
273             val modifierList = element.modifierList ?: return PsiModifierItem(codebase)
274             val uAnnotations = annotated.uAnnotations
275 
276             var flags = computeFlag(codebase, element, modifierList)
277 
278             return if (uAnnotations.isEmpty()) {
279                 val psiAnnotations = modifierList.annotations
280                 if (psiAnnotations.isNotEmpty()) {
281                     val annotations: MutableList<AnnotationItem> =
282                         psiAnnotations.map { PsiAnnotationItem.create(codebase, it) }.toMutableList()
283                     PsiModifierItem(codebase, flags, annotations)
284                 } else {
285                     PsiModifierItem(codebase, flags)
286                 }
287             } else {
288                 val isPrimitiveVariable = element is UVariable && element.type is PsiPrimitiveType
289 
290                 val annotations: MutableList<AnnotationItem> = uAnnotations
291                     // Uast sometimes puts nullability annotations on primitives!?
292                     .filter {
293                         !isPrimitiveVariable ||
294                             it.qualifiedName == null ||
295                             !it.isKotlinNullabilityAnnotation
296                     }
297                     .map {
298 
299                         val qualifiedName = it.qualifiedName
300                         if (qualifiedName == ANDROIDX_VISIBLE_FOR_TESTING) {
301                             val otherwise = it.findAttributeValue(ATTR_OTHERWISE)
302                             val ref = when {
303                                 otherwise is PsiReferenceExpression -> otherwise.referenceName ?: ""
304                                 otherwise != null -> otherwise.asSourceString()
305                                 else -> ""
306                             }
307                             flags = getVisibilityFlag(ref, flags)
308                         }
309 
310                         UAnnotationItem.create(codebase, it, qualifiedName)
311                     }.filter { !it.isDeprecatedForSdk() }.toMutableList()
312 
313                 if (!isPrimitiveVariable) {
314                     val psiAnnotations = modifierList.annotations
315                     if (psiAnnotations.isNotEmpty() && annotations.none { it.isNullnessAnnotation() }) {
316                         val ktNullAnnotation = psiAnnotations.firstOrNull { it is KtLightNullabilityAnnotation<*> }
317                         ktNullAnnotation?.let {
318                             annotations.add(PsiAnnotationItem.create(codebase, it))
319                         }
320                     }
321                 }
322 
323                 PsiModifierItem(codebase, flags, annotations)
324             }
325         }
326 
327         /** Returns whether this is a `@DeprecatedForSdk` annotation **that should be skipped**. */
328         private fun AnnotationItem.isDeprecatedForSdk(): Boolean {
329             if (originalName != ANDROID_DEPRECATED_FOR_SDK) {
330                 return false
331             }
332 
333             val allowIn = findAttribute(ATTR_ALLOW_IN) ?: return false
334 
335             for (api in allowIn.leafValues()) {
336                 val annotationName = api.value() as? String ?: continue
337                 if (options.showAnnotations.matchesAnnotationName(annotationName)) {
338                     return true
339                 }
340             }
341 
342             return false
343         }
344 
345         private val NOT_NULL = NotNull::class.qualifiedName
346         private val NULLABLE = Nullable::class.qualifiedName
347 
348         private val UAnnotation.isKotlinNullabilityAnnotation: Boolean
349             get() = qualifiedName == NOT_NULL || qualifiedName == NULLABLE
350 
351         /** Modifies the modifier flags based on the VisibleForTesting otherwise constants */
352         private fun getVisibilityFlag(ref: String, flags: Int): Int {
353             val visibilityFlags = if (ref.endsWith("PROTECTED")) {
354                 PROTECTED
355             } else if (ref.endsWith("PACKAGE_PRIVATE")) {
356                 PACKAGE_PRIVATE
357             } else if (ref.endsWith("PRIVATE") || ref.endsWith("NONE")) {
358                 PRIVATE
359             } else {
360                 flags and VISIBILITY_MASK
361             }
362 
363             return (flags and VISIBILITY_MASK.inv()) or visibilityFlags
364         }
365 
366         fun create(codebase: PsiBasedCodebase, original: PsiModifierItem): PsiModifierItem {
367             val originalAnnotations = original.annotations ?: return PsiModifierItem(codebase, original.flags)
368             val copy: MutableList<AnnotationItem> = ArrayList(originalAnnotations.size)
369             originalAnnotations.mapTo(copy) { item ->
370                 when (item) {
371                     is PsiAnnotationItem -> PsiAnnotationItem.create(codebase, item)
372                     is UAnnotationItem -> UAnnotationItem.create(codebase, item)
373                     else -> {
374                         throw Exception("Unexpected annotation type ${item::class.qualifiedName}")
375                     }
376                 }
377             }
378             return PsiModifierItem(codebase, original.flags, copy)
379         }
380     }
381 }
382