• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.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.PsiMethod
29 import com.intellij.psi.PsiModifier
30 import com.intellij.psi.PsiModifierList
31 import com.intellij.psi.PsiModifierListOwner
32 import com.intellij.psi.PsiReferenceExpression
33 import com.intellij.psi.PsiPrimitiveType
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.KtNamedFunction
38 import org.jetbrains.uast.UAnnotated
39 import org.jetbrains.uast.UMethod
40 import org.jetbrains.uast.UVariable
41 import org.jetbrains.uast.kotlin.KotlinNullabilityUAnnotation
42 
43 class PsiModifierItem(
44     codebase: Codebase,
45     flags: Int = 0,
46     annotations: MutableList<AnnotationItem>? = null
47 ) : DefaultModifierList(codebase, flags, annotations), ModifierList, MutableModifierList {
48     companion object {
createnull49         fun create(codebase: PsiBasedCodebase, element: PsiModifierListOwner, documentation: String?): PsiModifierItem {
50             val modifiers =
51                 if (element is UAnnotated) {
52                     create(codebase, element, element)
53                 } else {
54                     create(codebase, element)
55                 }
56             if (documentation?.contains("@deprecated") == true ||
57                 // Check for @Deprecated annotation
58                 ((element as? PsiDocCommentOwner)?.isDeprecated == true)
59             ) {
60                 modifiers.setDeprecated(true)
61             }
62 
63             return modifiers
64         }
65 
computeFlagnull66         private fun computeFlag(element: PsiModifierListOwner, modifierList: PsiModifierList): Int {
67             var flags = 0
68             if (modifierList.hasModifierProperty(PsiModifier.PUBLIC)) {
69                 flags = flags or PUBLIC
70             }
71             if (modifierList.hasModifierProperty(PsiModifier.PROTECTED)) {
72                 flags = flags or PROTECTED
73             }
74             if (modifierList.hasModifierProperty(PsiModifier.PRIVATE)) {
75                 flags = flags or PRIVATE
76             }
77             if (modifierList.hasModifierProperty(PsiModifier.STATIC)) {
78                 flags = flags or STATIC
79             }
80             if (modifierList.hasModifierProperty(PsiModifier.ABSTRACT)) {
81                 flags = flags or ABSTRACT
82             }
83             if (modifierList.hasModifierProperty(PsiModifier.FINAL)) {
84                 flags = flags or FINAL
85             }
86             if (modifierList.hasModifierProperty(PsiModifier.NATIVE)) {
87                 flags = flags or NATIVE
88             }
89             if (modifierList.hasModifierProperty(PsiModifier.SYNCHRONIZED)) {
90                 flags = flags or SYNCHRONIZED
91             }
92             if (modifierList.hasModifierProperty(PsiModifier.STRICTFP)) {
93                 flags = flags or STRICT_FP
94             }
95             if (modifierList.hasModifierProperty(PsiModifier.TRANSIENT)) {
96                 flags = flags or TRANSIENT
97             }
98             if (modifierList.hasModifierProperty(PsiModifier.VOLATILE)) {
99                 flags = flags or VOLATILE
100             }
101             if (modifierList.hasModifierProperty(PsiModifier.DEFAULT)) {
102                 flags = flags or DEFAULT
103             }
104 
105             // Look for special Kotlin keywords
106             if (modifierList is KtLightModifierList<*>) {
107                 val ktModifierList = modifierList.kotlinOrigin
108                 if (ktModifierList != null) {
109                     if (ktModifierList.hasModifier(KtTokens.VARARG_KEYWORD)) {
110                         flags = flags or VARARG
111                     }
112                     if (ktModifierList.hasModifier(KtTokens.SEALED_KEYWORD)) {
113                         flags = flags or SEALED
114                     }
115                     if (ktModifierList.hasModifier(KtTokens.INTERNAL_KEYWORD)) {
116                         // Also remove public flag which at the UAST levels it promotes these
117                         // methods to, e.g. "internal myVar" gets turned into
118                         //    public final boolean getMyHiddenVar$lintWithKotlin()
119                         flags = (flags or INTERNAL) and PUBLIC.inv()
120                     }
121                     if (ktModifierList.hasModifier(KtTokens.INFIX_KEYWORD)) {
122                         flags = flags or INFIX
123                     }
124                     if (ktModifierList.hasModifier(KtTokens.OPERATOR_KEYWORD)) {
125                         flags = flags or OPERATOR
126                     }
127                     if (ktModifierList.hasModifier(KtTokens.INLINE_KEYWORD)) {
128                         flags = flags or INLINE
129 
130                         // Workaround for b/117565118:
131                         if ((flags or PRIVATE) != 0 && element is PsiMethod) {
132                             val t =
133                                 ((element as? UMethod)?.sourcePsi as? KtNamedFunction)?.typeParameterList?.text ?: ""
134                             if (t.contains("reified") &&
135                                 !ktModifierList.hasModifier(KtTokens.PRIVATE_KEYWORD) &&
136                                 !ktModifierList.hasModifier(KtTokens.INTERNAL_KEYWORD)
137                             ) {
138                                 // Switch back from private to public
139                                 flags = (flags and PRIVATE.inv()) or PUBLIC
140                             }
141                         }
142                     }
143                     if (ktModifierList.hasModifier(KtTokens.SUSPEND_KEYWORD)) {
144                         flags = flags or SUSPEND
145 
146                         // Workaround for b/117565118:
147                         // Switch back from private to public
148                         flags = (flags and PRIVATE.inv()) or PUBLIC
149                     }
150                 }
151             }
152 
153             return flags
154         }
155 
createnull156         private fun create(codebase: PsiBasedCodebase, element: PsiModifierListOwner): PsiModifierItem {
157             val modifierList = element.modifierList ?: return PsiModifierItem(codebase)
158 
159             var flags = computeFlag(element, modifierList)
160 
161             val psiAnnotations = modifierList.annotations
162             return if (psiAnnotations.isEmpty()) {
163                 PsiModifierItem(codebase, flags)
164             } else {
165                 val annotations: MutableList<AnnotationItem> =
166                     psiAnnotations.map {
167                         val qualifiedName = it.qualifiedName
168                         // Consider also supporting com.android.internal.annotations.VisibleForTesting?
169                         if (qualifiedName == ANDROIDX_VISIBLE_FOR_TESTING ||
170                             qualifiedName == ANDROID_SUPPORT_VISIBLE_FOR_TESTING) {
171                             val otherwise = it.findAttributeValue(ATTR_OTHERWISE)
172                             val ref = when {
173                                 otherwise is PsiReferenceExpression -> otherwise.referenceName ?: ""
174                                 otherwise != null -> otherwise.text
175                                 else -> ""
176                             }
177                             flags = getVisibilityFlag(ref, flags)
178                         }
179 
180                         PsiAnnotationItem.create(codebase, it, qualifiedName)
181                     }.toMutableList()
182                 PsiModifierItem(codebase, flags, annotations)
183             }
184         }
185 
createnull186         private fun create(
187             codebase: PsiBasedCodebase,
188             element: PsiModifierListOwner,
189             annotated: UAnnotated
190         ): PsiModifierItem {
191             val modifierList = element.modifierList ?: return PsiModifierItem(codebase)
192             var flags = computeFlag(element, modifierList)
193             val uAnnotations = annotated.annotations
194 
195             return if (uAnnotations.isEmpty()) {
196                 val psiAnnotations = modifierList.annotations
197                 if (!psiAnnotations.isEmpty()) {
198                     val annotations: MutableList<AnnotationItem> =
199                         psiAnnotations.map { PsiAnnotationItem.create(codebase, it) }.toMutableList()
200                     PsiModifierItem(codebase, flags, annotations)
201                 } else {
202                     PsiModifierItem(codebase, flags)
203                 }
204             } else {
205                 val isPrimitiveVariable = element is UVariable && element.type is PsiPrimitiveType
206 
207                 val annotations: MutableList<AnnotationItem> = uAnnotations
208                     // Uast sometimes puts nullability annotations on primitives!?
209                     .filter { !isPrimitiveVariable || it !is KotlinNullabilityUAnnotation }
210                     .map {
211 
212                         val qualifiedName = it.qualifiedName
213                         if (qualifiedName == ANDROIDX_VISIBLE_FOR_TESTING ||
214                             qualifiedName == ANDROID_SUPPORT_VISIBLE_FOR_TESTING) {
215                             val otherwise = it.findAttributeValue(ATTR_OTHERWISE)
216                             val ref = when {
217                                 otherwise is PsiReferenceExpression -> otherwise.referenceName ?: ""
218                                 otherwise != null -> otherwise.asSourceString()
219                                 else -> ""
220                             }
221                             flags = getVisibilityFlag(ref, flags)
222                         }
223 
224                         UAnnotationItem.create(codebase, it, qualifiedName)
225                     }.toMutableList()
226 
227                 if (!isPrimitiveVariable) {
228                     val psiAnnotations = modifierList.annotations
229                     if (psiAnnotations.isNotEmpty() && annotations.none { it.isNullnessAnnotation() }) {
230                         val ktNullAnnotation = psiAnnotations.firstOrNull { it is KtLightNullabilityAnnotation }
231                         ktNullAnnotation?.let {
232                             annotations.add(PsiAnnotationItem.create(codebase, it))
233                         }
234                     }
235                 }
236 
237                 PsiModifierItem(codebase, flags, annotations)
238             }
239         }
240 
241         /** Modifies the modifier flags based on the VisibleForTesting otherwise constants */
getVisibilityFlagnull242         private fun getVisibilityFlag(ref: String, flags: Int): Int {
243             return if (ref.endsWith("PROTECTED")) {
244                 (flags and PUBLIC.inv() and PRIVATE.inv() and INTERNAL.inv()) or PROTECTED
245             } else if (ref.endsWith("PACKAGE_PRIVATE")) {
246                 (flags and PUBLIC.inv() and PRIVATE.inv() and INTERNAL.inv() and PROTECTED.inv())
247             } else if (ref.endsWith("PRIVATE") || ref.endsWith("NONE")) {
248                 (flags and PUBLIC.inv() and PROTECTED.inv() and INTERNAL.inv()) or PRIVATE
249             } else {
250                 flags
251             }
252         }
253 
createnull254         fun create(codebase: PsiBasedCodebase, original: PsiModifierItem): PsiModifierItem {
255             val originalAnnotations = original.annotations ?: return PsiModifierItem(codebase, original.flags)
256             val copy: MutableList<AnnotationItem> = ArrayList(originalAnnotations.size)
257             originalAnnotations.mapTo(copy) { PsiAnnotationItem.create(codebase, it as PsiAnnotationItem) }
258             return PsiModifierItem(codebase, original.flags, copy)
259         }
260     }
261 }
262