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_DEPRECATED_FOR_SDK 21 import com.android.tools.metalava.ATTR_ALLOW_IN 22 import com.android.tools.metalava.ATTR_OTHERWISE 23 import com.android.tools.metalava.model.AnnotationItem 24 import com.android.tools.metalava.model.Codebase 25 import com.android.tools.metalava.model.DefaultModifierList 26 import com.android.tools.metalava.model.ModifierList 27 import com.android.tools.metalava.model.MutableModifierList 28 import com.android.tools.metalava.options 29 import com.intellij.psi.PsiDocCommentOwner 30 import com.intellij.psi.PsiModifier 31 import com.intellij.psi.PsiModifierList 32 import com.intellij.psi.PsiModifierListOwner 33 import com.intellij.psi.PsiPrimitiveType 34 import com.intellij.psi.PsiReferenceExpression 35 import com.intellij.psi.impl.light.LightModifierList 36 import org.jetbrains.annotations.NotNull 37 import org.jetbrains.annotations.Nullable 38 import org.jetbrains.kotlin.asJava.elements.KtLightModifierList 39 import org.jetbrains.kotlin.asJava.elements.KtLightNullabilityAnnotation 40 import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithVisibility 41 import org.jetbrains.kotlin.descriptors.EffectiveVisibility 42 import org.jetbrains.kotlin.descriptors.effectiveVisibility 43 import org.jetbrains.kotlin.lexer.KtTokens 44 import org.jetbrains.kotlin.psi.KtAnnotated 45 import org.jetbrains.kotlin.psi.KtElement 46 import org.jetbrains.kotlin.psi.KtModifierList 47 import org.jetbrains.kotlin.psi.KtModifierListOwner 48 import org.jetbrains.kotlin.psi.KtNamedFunction 49 import org.jetbrains.kotlin.psi.KtPropertyAccessor 50 import org.jetbrains.kotlin.psi.psiUtil.hasFunModifier 51 import org.jetbrains.kotlin.psi.psiUtil.visibilityModifier 52 import org.jetbrains.kotlin.resolve.BindingContext 53 import org.jetbrains.uast.UAnnotated 54 import org.jetbrains.uast.UAnnotation 55 import org.jetbrains.uast.UElement 56 import org.jetbrains.uast.UMethod 57 import org.jetbrains.uast.UVariable 58 59 class PsiModifierItem( 60 codebase: Codebase, 61 flags: Int = PACKAGE_PRIVATE, 62 annotations: MutableList<AnnotationItem>? = null 63 ) : DefaultModifierList(codebase, flags, annotations), ModifierList, MutableModifierList { 64 companion object { 65 fun create( 66 codebase: PsiBasedCodebase, 67 element: PsiModifierListOwner, 68 documentation: String? 69 ): PsiModifierItem { 70 val modifiers = 71 if (element is UAnnotated) { 72 create(codebase, element, element) 73 } else { 74 create(codebase, element) 75 } 76 if (documentation?.contains("@deprecated") == true || 77 // Check for @Deprecated annotation 78 ((element as? PsiDocCommentOwner)?.isDeprecated == true) || 79 // Check for @Deprecated on sourcePsi 80 isDeprecatedFromSourcePsi(element) 81 ) { 82 modifiers.setDeprecated(true) 83 } 84 85 return modifiers 86 } 87 88 private fun isDeprecatedFromSourcePsi(element: PsiModifierListOwner): Boolean { 89 return ((element as? UElement)?.sourcePsi as? KtAnnotated)?.annotationEntries?.any { 90 it.shortName?.toString() == "Deprecated" 91 } ?: false 92 } 93 94 private fun computeFlag( 95 codebase: PsiBasedCodebase, 96 element: PsiModifierListOwner, 97 modifierList: PsiModifierList 98 ): Int { 99 var flags = 0 100 if (modifierList.hasModifierProperty(PsiModifier.STATIC)) { 101 flags = flags or STATIC 102 } 103 if (modifierList.hasModifierProperty(PsiModifier.ABSTRACT)) { 104 flags = flags or ABSTRACT 105 } 106 if (modifierList.hasModifierProperty(PsiModifier.FINAL)) { 107 flags = flags or FINAL 108 } 109 if (modifierList.hasModifierProperty(PsiModifier.NATIVE)) { 110 flags = flags or NATIVE 111 } 112 if (modifierList.hasModifierProperty(PsiModifier.SYNCHRONIZED)) { 113 flags = flags or SYNCHRONIZED 114 } 115 if (modifierList.hasModifierProperty(PsiModifier.STRICTFP)) { 116 flags = flags or STRICT_FP 117 } 118 if (modifierList.hasModifierProperty(PsiModifier.TRANSIENT)) { 119 flags = flags or TRANSIENT 120 } 121 if (modifierList.hasModifierProperty(PsiModifier.VOLATILE)) { 122 flags = flags or VOLATILE 123 } 124 if (modifierList.hasModifierProperty(PsiModifier.DEFAULT)) { 125 flags = flags or DEFAULT 126 } 127 128 // Look for special Kotlin keywords 129 var ktModifierList: KtModifierList? = null 130 val sourcePsi = (element as? UElement)?.sourcePsi 131 if (modifierList is KtLightModifierList<*>) { 132 ktModifierList = modifierList.kotlinOrigin 133 } else if (modifierList is LightModifierList && element is UMethod) { 134 if (sourcePsi is KtModifierListOwner) { 135 ktModifierList = sourcePsi.modifierList 136 } 137 } 138 var visibilityFlags = when { 139 modifierList.hasModifierProperty(PsiModifier.PUBLIC) -> PUBLIC 140 modifierList.hasModifierProperty(PsiModifier.PROTECTED) -> PROTECTED 141 modifierList.hasModifierProperty(PsiModifier.PRIVATE) -> PRIVATE 142 ktModifierList != null -> when { 143 ktModifierList.hasModifier(KtTokens.PRIVATE_KEYWORD) -> PRIVATE 144 ktModifierList.hasModifier(KtTokens.PROTECTED_KEYWORD) -> PROTECTED 145 ktModifierList.hasModifier(KtTokens.INTERNAL_KEYWORD) -> INTERNAL 146 else -> PUBLIC 147 } 148 else -> PACKAGE_PRIVATE 149 } 150 if (ktModifierList != null) { 151 if (ktModifierList.hasModifier(KtTokens.INTERNAL_KEYWORD)) { 152 // Reset visibilityFlags to INTERNAL if the internal modifier is explicitly 153 // present on the element 154 visibilityFlags = INTERNAL 155 } else if ( 156 ktModifierList.hasModifier(KtTokens.OVERRIDE_KEYWORD) && 157 ktModifierList.visibilityModifier() == null && 158 sourcePsi is KtElement 159 ) { 160 // Reset visibilityFlags to INTERNAL if the element has no explicit visibility 161 // modifier, but overrides an internal declaration. Adapted from 162 // org.jetbrains.kotlin.asJava.classes.UltraLightMembersCreator.isInternal 163 val descriptor = codebase.bindingContext(sourcePsi) 164 .get(BindingContext.DECLARATION_TO_DESCRIPTOR, sourcePsi) 165 166 if (descriptor is DeclarationDescriptorWithVisibility) { 167 val effectiveVisibility = 168 descriptor.visibility.effectiveVisibility(descriptor, false) 169 170 if (effectiveVisibility == EffectiveVisibility.Internal) { 171 visibilityFlags = INTERNAL 172 } 173 } 174 } 175 if (ktModifierList.hasModifier(KtTokens.VARARG_KEYWORD)) { 176 flags = flags or VARARG 177 } 178 if (ktModifierList.hasModifier(KtTokens.SEALED_KEYWORD)) { 179 flags = flags or SEALED 180 } 181 if (ktModifierList.hasModifier(KtTokens.INFIX_KEYWORD)) { 182 flags = flags or INFIX 183 } 184 if (ktModifierList.hasModifier(KtTokens.CONST_KEYWORD)) { 185 flags = flags or CONST 186 } 187 if (ktModifierList.hasModifier(KtTokens.OPERATOR_KEYWORD)) { 188 flags = flags or OPERATOR 189 } 190 if (ktModifierList.hasModifier(KtTokens.INLINE_KEYWORD)) { 191 flags = flags or INLINE 192 193 // Workaround for b/117565118: 194 val func = sourcePsi as? KtNamedFunction 195 if (func != null && 196 (func.typeParameterList?.text ?: "").contains("reified") && 197 !ktModifierList.hasModifier(KtTokens.PRIVATE_KEYWORD) && 198 !ktModifierList.hasModifier(KtTokens.INTERNAL_KEYWORD) 199 ) { 200 // Switch back from private to public 201 visibilityFlags = PUBLIC 202 } 203 } 204 if (ktModifierList.hasModifier(KtTokens.VALUE_KEYWORD)) { 205 flags = flags or VALUE 206 } 207 if (ktModifierList.hasModifier(KtTokens.SUSPEND_KEYWORD)) { 208 flags = flags or SUSPEND 209 } 210 if (ktModifierList.hasModifier(KtTokens.COMPANION_KEYWORD)) { 211 flags = flags or COMPANION 212 } 213 if (ktModifierList.hasFunModifier()) { 214 flags = flags or FUN 215 } 216 if (ktModifierList.hasModifier(KtTokens.DATA_KEYWORD)) { 217 flags = flags or DATA 218 } 219 } 220 // Methods that are property accessors inherit visibility from the source element 221 if (element is UMethod && (element.sourceElement is KtPropertyAccessor)) { 222 val sourceElement = element.sourceElement 223 if (sourceElement is KtModifierListOwner) { 224 val sourceModifierList = sourceElement.modifierList 225 if (sourceModifierList != null) { 226 if (sourceModifierList.hasModifier(KtTokens.INTERNAL_KEYWORD)) { 227 visibilityFlags = INTERNAL 228 } 229 } 230 } 231 } 232 233 // Merge in the visibility flags. 234 flags = flags or visibilityFlags 235 236 return flags 237 } 238 239 private fun create(codebase: PsiBasedCodebase, element: PsiModifierListOwner): PsiModifierItem { 240 val modifierList = element.modifierList ?: return PsiModifierItem(codebase) 241 var flags = computeFlag(codebase, element, modifierList) 242 243 val psiAnnotations = modifierList.annotations 244 return if (psiAnnotations.isEmpty()) { 245 PsiModifierItem(codebase, flags) 246 } else { 247 val annotations: MutableList<AnnotationItem> = 248 // psi sometimes returns duplicate annotations, using distinct() to counter that. 249 psiAnnotations.distinct().map { 250 val qualifiedName = it.qualifiedName 251 // Consider also supporting com.android.internal.annotations.VisibleForTesting? 252 if (qualifiedName == ANDROIDX_VISIBLE_FOR_TESTING) { 253 val otherwise = it.findAttributeValue(ATTR_OTHERWISE) 254 val ref = when { 255 otherwise is PsiReferenceExpression -> otherwise.referenceName ?: "" 256 otherwise != null -> otherwise.text 257 else -> "" 258 } 259 flags = getVisibilityFlag(ref, flags) 260 } 261 262 PsiAnnotationItem.create(codebase, it, qualifiedName) 263 }.filter { !it.isDeprecatedForSdk() }.toMutableList() 264 PsiModifierItem(codebase, flags, annotations) 265 } 266 } 267 268 private fun create( 269 codebase: PsiBasedCodebase, 270 element: PsiModifierListOwner, 271 annotated: UAnnotated 272 ): PsiModifierItem { 273 val modifierList = element.modifierList ?: return PsiModifierItem(codebase) 274 val uAnnotations = annotated.uAnnotations 275 276 var flags = computeFlag(codebase, element, modifierList) 277 278 return if (uAnnotations.isEmpty()) { 279 val psiAnnotations = modifierList.annotations 280 if (psiAnnotations.isNotEmpty()) { 281 val annotations: MutableList<AnnotationItem> = 282 psiAnnotations.map { PsiAnnotationItem.create(codebase, it) }.toMutableList() 283 PsiModifierItem(codebase, flags, annotations) 284 } else { 285 PsiModifierItem(codebase, flags) 286 } 287 } else { 288 val isPrimitiveVariable = element is UVariable && element.type is PsiPrimitiveType 289 290 val annotations: MutableList<AnnotationItem> = uAnnotations 291 // Uast sometimes puts nullability annotations on primitives!? 292 .filter { 293 !isPrimitiveVariable || 294 it.qualifiedName == null || 295 !it.isKotlinNullabilityAnnotation 296 } 297 .map { 298 299 val qualifiedName = it.qualifiedName 300 if (qualifiedName == ANDROIDX_VISIBLE_FOR_TESTING) { 301 val otherwise = it.findAttributeValue(ATTR_OTHERWISE) 302 val ref = when { 303 otherwise is PsiReferenceExpression -> otherwise.referenceName ?: "" 304 otherwise != null -> otherwise.asSourceString() 305 else -> "" 306 } 307 flags = getVisibilityFlag(ref, flags) 308 } 309 310 UAnnotationItem.create(codebase, it, qualifiedName) 311 }.filter { !it.isDeprecatedForSdk() }.toMutableList() 312 313 if (!isPrimitiveVariable) { 314 val psiAnnotations = modifierList.annotations 315 if (psiAnnotations.isNotEmpty() && annotations.none { it.isNullnessAnnotation() }) { 316 val ktNullAnnotation = psiAnnotations.firstOrNull { it is KtLightNullabilityAnnotation<*> } 317 ktNullAnnotation?.let { 318 annotations.add(PsiAnnotationItem.create(codebase, it)) 319 } 320 } 321 } 322 323 PsiModifierItem(codebase, flags, annotations) 324 } 325 } 326 327 /** Returns whether this is a `@DeprecatedForSdk` annotation **that should be skipped**. */ 328 private fun AnnotationItem.isDeprecatedForSdk(): Boolean { 329 if (originalName != ANDROID_DEPRECATED_FOR_SDK) { 330 return false 331 } 332 333 val allowIn = findAttribute(ATTR_ALLOW_IN) ?: return false 334 335 for (api in allowIn.leafValues()) { 336 val annotationName = api.value() as? String ?: continue 337 if (options.showAnnotations.matchesAnnotationName(annotationName)) { 338 return true 339 } 340 } 341 342 return false 343 } 344 345 private val NOT_NULL = NotNull::class.qualifiedName 346 private val NULLABLE = Nullable::class.qualifiedName 347 348 private val UAnnotation.isKotlinNullabilityAnnotation: Boolean 349 get() = qualifiedName == NOT_NULL || qualifiedName == NULLABLE 350 351 /** Modifies the modifier flags based on the VisibleForTesting otherwise constants */ 352 private fun getVisibilityFlag(ref: String, flags: Int): Int { 353 val visibilityFlags = if (ref.endsWith("PROTECTED")) { 354 PROTECTED 355 } else if (ref.endsWith("PACKAGE_PRIVATE")) { 356 PACKAGE_PRIVATE 357 } else if (ref.endsWith("PRIVATE") || ref.endsWith("NONE")) { 358 PRIVATE 359 } else { 360 flags and VISIBILITY_MASK 361 } 362 363 return (flags and VISIBILITY_MASK.inv()) or visibilityFlags 364 } 365 366 fun create(codebase: PsiBasedCodebase, original: PsiModifierItem): PsiModifierItem { 367 val originalAnnotations = original.annotations ?: return PsiModifierItem(codebase, original.flags) 368 val copy: MutableList<AnnotationItem> = ArrayList(originalAnnotations.size) 369 originalAnnotations.mapTo(copy) { item -> 370 when (item) { 371 is PsiAnnotationItem -> PsiAnnotationItem.create(codebase, item) 372 is UAnnotationItem -> UAnnotationItem.create(codebase, item) 373 else -> { 374 throw Exception("Unexpected annotation type ${item::class.qualifiedName}") 375 } 376 } 377 } 378 return PsiModifierItem(codebase, original.flags, copy) 379 } 380 } 381 } 382