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