• 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_SUPPORT_VISIBLE_FOR_TESTING
21 import com.android.tools.metalava.ATTR_OTHERWISE
22 import com.android.tools.metalava.model.AnnotationItem
23 import com.android.tools.metalava.model.Codebase
24 import com.android.tools.metalava.model.DefaultModifierList
25 import com.android.tools.metalava.model.ModifierList
26 import com.android.tools.metalava.model.MutableModifierList
27 import com.intellij.psi.PsiDocCommentOwner
28 import com.intellij.psi.PsiModifier
29 import com.intellij.psi.PsiModifierList
30 import com.intellij.psi.PsiModifierListOwner
31 import com.intellij.psi.PsiPrimitiveType
32 import com.intellij.psi.PsiReferenceExpression
33 import com.intellij.psi.impl.light.LightModifierList
34 import org.jetbrains.kotlin.asJava.elements.KtLightModifierList
35 import org.jetbrains.kotlin.asJava.elements.KtLightNullabilityAnnotation
36 import org.jetbrains.kotlin.lexer.KtTokens
37 import org.jetbrains.kotlin.psi.KtModifierList
38 import org.jetbrains.kotlin.psi.KtModifierListOwner
39 import org.jetbrains.kotlin.psi.KtNamedFunction
40 import org.jetbrains.kotlin.psi.KtPropertyAccessor
41 import org.jetbrains.kotlin.psi.psiUtil.hasFunModifier
42 import org.jetbrains.uast.UAnnotated
43 import org.jetbrains.uast.UMethod
44 import org.jetbrains.uast.UVariable
45 import org.jetbrains.uast.kotlin.KotlinNullabilityUAnnotation
46 import org.jetbrains.uast.kotlin.declarations.KotlinUMethod
47 
48 class PsiModifierItem(
49     codebase: Codebase,
50     flags: Int = PACKAGE_PRIVATE,
51     annotations: MutableList<AnnotationItem>? = null
52 ) : DefaultModifierList(codebase, flags, annotations), ModifierList, MutableModifierList {
53     companion object {
54         fun create(codebase: PsiBasedCodebase, element: PsiModifierListOwner, documentation: String?): PsiModifierItem {
55             val modifiers =
56                 if (element is UAnnotated) {
57                     create(codebase, element, element)
58                 } else {
59                     create(codebase, element)
60                 }
61             if (documentation?.contains("@deprecated") == true ||
62                 // Check for @Deprecated annotation
63                 ((element as? PsiDocCommentOwner)?.isDeprecated == true)
64             ) {
65                 modifiers.setDeprecated(true)
66             }
67 
68             return modifiers
69         }
70 
71         private fun computeFlag(element: PsiModifierListOwner, modifierList: PsiModifierList): Int {
72             var visibilityFlags = when {
73                 modifierList.hasModifierProperty(PsiModifier.PUBLIC) -> PUBLIC
74                 modifierList.hasModifierProperty(PsiModifier.PROTECTED) -> PROTECTED
75                 modifierList.hasModifierProperty(PsiModifier.PRIVATE) -> PRIVATE
76                 else -> PACKAGE_PRIVATE
77             }
78             var flags = 0
79             if (modifierList.hasModifierProperty(PsiModifier.STATIC)) {
80                 flags = flags or STATIC
81             }
82             if (modifierList.hasModifierProperty(PsiModifier.ABSTRACT)) {
83                 flags = flags or ABSTRACT
84             }
85             if (modifierList.hasModifierProperty(PsiModifier.FINAL)) {
86                 flags = flags or FINAL
87             }
88             if (modifierList.hasModifierProperty(PsiModifier.NATIVE)) {
89                 flags = flags or NATIVE
90             }
91             if (modifierList.hasModifierProperty(PsiModifier.SYNCHRONIZED)) {
92                 flags = flags or SYNCHRONIZED
93             }
94             if (modifierList.hasModifierProperty(PsiModifier.STRICTFP)) {
95                 flags = flags or STRICT_FP
96             }
97             if (modifierList.hasModifierProperty(PsiModifier.TRANSIENT)) {
98                 flags = flags or TRANSIENT
99             }
100             if (modifierList.hasModifierProperty(PsiModifier.VOLATILE)) {
101                 flags = flags or VOLATILE
102             }
103             if (modifierList.hasModifierProperty(PsiModifier.DEFAULT)) {
104                 flags = flags or DEFAULT
105             }
106 
107             // Look for special Kotlin keywords
108             var ktModifierList: KtModifierList? = null
109             if (modifierList is KtLightModifierList<*>) {
110                 ktModifierList = modifierList.kotlinOrigin
111             } else if (modifierList is LightModifierList && element is KotlinUMethod) {
112                 ktModifierList = element.sourcePsi?.modifierList
113             }
114             if (ktModifierList != null) {
115                 if (ktModifierList.hasModifier(KtTokens.VARARG_KEYWORD)) {
116                     flags = flags or VARARG
117                 }
118                 if (ktModifierList.hasModifier(KtTokens.SEALED_KEYWORD)) {
119                     flags = flags or SEALED
120                 }
121                 if (ktModifierList.hasModifier(KtTokens.INTERNAL_KEYWORD)) {
122                     // Also remove public flag which at the UAST levels it promotes these
123                     // methods to, e.g. "internal myVar" gets turned into
124                     //    public final boolean getMyHiddenVar$lintWithKotlin()
125                     visibilityFlags = INTERNAL
126                 }
127                 if (ktModifierList.hasModifier(KtTokens.INFIX_KEYWORD)) {
128                     flags = flags or INFIX
129                 }
130                 if (ktModifierList.hasModifier(KtTokens.CONST_KEYWORD)) {
131                     flags = flags or CONST
132                 }
133                 if (ktModifierList.hasModifier(KtTokens.OPERATOR_KEYWORD)) {
134                     flags = flags or OPERATOR
135                 }
136                 if (ktModifierList.hasModifier(KtTokens.INLINE_KEYWORD)) {
137                     flags = flags or INLINE
138 
139                     // Workaround for b/117565118:
140                     val func = (element as? UMethod)?.sourcePsi as? KtNamedFunction
141                     if (func != null &&
142                         (func.typeParameterList?.text ?: "").contains("reified") &&
143                         !ktModifierList.hasModifier(KtTokens.PRIVATE_KEYWORD) &&
144                         !ktModifierList.hasModifier(KtTokens.INTERNAL_KEYWORD)
145                     ) {
146                         // Switch back from private to public
147                         visibilityFlags = PUBLIC
148                     }
149                 }
150                 if (ktModifierList.hasModifier(KtTokens.SUSPEND_KEYWORD)) {
151                     flags = flags or SUSPEND
152                 }
153                 if (ktModifierList.hasModifier(KtTokens.COMPANION_KEYWORD)) {
154                     flags = flags or COMPANION
155                 }
156                 if (ktModifierList.hasFunModifier()) {
157                     flags = flags or FUN
158                 }
159             }
160             // Methods that are property accessors inherit visibility from the source element
161             if (element is UMethod && (element.sourceElement is KtPropertyAccessor)) {
162                 val sourceElement = element.sourceElement
163                 if (sourceElement is KtModifierListOwner) {
164                     val sourceModifierList = sourceElement.modifierList
165                     if (sourceModifierList != null) {
166                         if (sourceModifierList.hasModifier(KtTokens.INTERNAL_KEYWORD)) {
167                             visibilityFlags = INTERNAL
168                         }
169                     }
170                 }
171             }
172 
173             // Merge in the visibility flags.
174             flags = flags or visibilityFlags
175 
176             return flags
177         }
178 
179         private fun create(codebase: PsiBasedCodebase, element: PsiModifierListOwner): PsiModifierItem {
180             val modifierList = element.modifierList ?: return PsiModifierItem(codebase)
181 
182             var flags = computeFlag(element, modifierList)
183 
184             val psiAnnotations = modifierList.annotations
185             return if (psiAnnotations.isEmpty()) {
186                 PsiModifierItem(codebase, flags)
187             } else {
188                 val annotations: MutableList<AnnotationItem> =
189                     // psi sometimes returns duplicate annotations, using distint() to counter that.
190                     psiAnnotations.distinct().map {
191                         val qualifiedName = it.qualifiedName
192                         // Consider also supporting com.android.internal.annotations.VisibleForTesting?
193                         if (qualifiedName == ANDROIDX_VISIBLE_FOR_TESTING ||
194                             qualifiedName == ANDROID_SUPPORT_VISIBLE_FOR_TESTING) {
195                             val otherwise = it.findAttributeValue(ATTR_OTHERWISE)
196                             val ref = when {
197                                 otherwise is PsiReferenceExpression -> otherwise.referenceName ?: ""
198                                 otherwise != null -> otherwise.text
199                                 else -> ""
200                             }
201                             flags = getVisibilityFlag(ref, flags)
202                         }
203 
204                         PsiAnnotationItem.create(codebase, it, qualifiedName)
205                     }.toMutableList()
206                 PsiModifierItem(codebase, flags, annotations)
207             }
208         }
209 
210         private fun create(
211             codebase: PsiBasedCodebase,
212             element: PsiModifierListOwner,
213             annotated: UAnnotated
214         ): PsiModifierItem {
215             val modifierList = element.modifierList ?: return PsiModifierItem(codebase)
216             var flags = computeFlag(element, modifierList)
217             val uAnnotations = annotated.uAnnotations
218 
219             return if (uAnnotations.isEmpty()) {
220                 val psiAnnotations = modifierList.annotations
221                 if (psiAnnotations.isNotEmpty()) {
222                     val annotations: MutableList<AnnotationItem> =
223                         psiAnnotations.map { PsiAnnotationItem.create(codebase, it) }.toMutableList()
224                     PsiModifierItem(codebase, flags, annotations)
225                 } else {
226                     PsiModifierItem(codebase, flags)
227                 }
228             } else {
229                 val isPrimitiveVariable = element is UVariable && element.type is PsiPrimitiveType
230 
231                 val annotations: MutableList<AnnotationItem> = uAnnotations
232                     // Uast sometimes puts nullability annotations on primitives!?
233                     .filter { !isPrimitiveVariable || it !is KotlinNullabilityUAnnotation }
234                     .map {
235 
236                         val qualifiedName = it.qualifiedName
237                         if (qualifiedName == ANDROIDX_VISIBLE_FOR_TESTING ||
238                             qualifiedName == ANDROID_SUPPORT_VISIBLE_FOR_TESTING) {
239                             val otherwise = it.findAttributeValue(ATTR_OTHERWISE)
240                             val ref = when {
241                                 otherwise is PsiReferenceExpression -> otherwise.referenceName ?: ""
242                                 otherwise != null -> otherwise.asSourceString()
243                                 else -> ""
244                             }
245                             flags = getVisibilityFlag(ref, flags)
246                         }
247 
248                         UAnnotationItem.create(codebase, it, qualifiedName)
249                     }.toMutableList()
250 
251                 if (!isPrimitiveVariable) {
252                     val psiAnnotations = modifierList.annotations
253                     if (psiAnnotations.isNotEmpty() && annotations.none { it.isNullnessAnnotation() }) {
254                         val ktNullAnnotation = psiAnnotations.firstOrNull { it is KtLightNullabilityAnnotation<*> }
255                         ktNullAnnotation?.let {
256                             annotations.add(PsiAnnotationItem.create(codebase, it))
257                         }
258                     }
259                 }
260 
261                 PsiModifierItem(codebase, flags, annotations)
262             }
263         }
264 
265         /** Modifies the modifier flags based on the VisibleForTesting otherwise constants */
266         private fun getVisibilityFlag(ref: String, flags: Int): Int {
267             val visibilityFlags = if (ref.endsWith("PROTECTED")) {
268                 PROTECTED
269             } else if (ref.endsWith("PACKAGE_PRIVATE")) {
270                 PACKAGE_PRIVATE
271             } else if (ref.endsWith("PRIVATE") || ref.endsWith("NONE")) {
272                 PRIVATE
273             } else {
274                 flags and VISIBILITY_MASK
275             }
276 
277             return (flags and VISIBILITY_MASK.inv()) or visibilityFlags
278         }
279 
280         fun create(codebase: PsiBasedCodebase, original: PsiModifierItem): PsiModifierItem {
281             val originalAnnotations = original.annotations ?: return PsiModifierItem(codebase, original.flags)
282             val copy: MutableList<AnnotationItem> = ArrayList(originalAnnotations.size)
283             originalAnnotations.mapTo(copy) { item ->
284                 when (item) {
285                     is PsiAnnotationItem -> PsiAnnotationItem.create(codebase, item)
286                     is UAnnotationItem -> UAnnotationItem.create(codebase, item)
287                     else -> {
288                         throw Exception("Unexpected annotation type ${item::class.qualifiedName}")
289                     }
290                 }
291             }
292             return PsiModifierItem(codebase, original.flags, copy)
293         }
294     }
295 }
296