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