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