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