1 /* <lambda>null2 * Copyright (C) 2018 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.SdkConstants.ATTR_VALUE 20 import com.android.tools.lint.detector.api.ConstantEvaluator 21 import com.android.tools.metalava.model.AnnotationArrayAttributeValue 22 import com.android.tools.metalava.model.AnnotationAttribute 23 import com.android.tools.metalava.model.AnnotationAttributeValue 24 import com.android.tools.metalava.model.AnnotationItem 25 import com.android.tools.metalava.model.AnnotationSingleAttributeValue 26 import com.android.tools.metalava.model.AnnotationTarget 27 import com.android.tools.metalava.model.ClassItem 28 import com.android.tools.metalava.model.DefaultAnnotationItem 29 import com.android.tools.metalava.model.Item 30 import com.intellij.psi.PsiAnnotationMethod 31 import com.intellij.psi.PsiClass 32 import com.intellij.psi.PsiExpression 33 import com.intellij.psi.PsiField 34 import com.intellij.psi.PsiLiteral 35 import com.intellij.psi.PsiMethod 36 import com.intellij.psi.impl.JavaConstantExpressionEvaluator 37 import org.jetbrains.kotlin.asJava.elements.KtLightNullabilityAnnotation 38 import org.jetbrains.uast.UAnnotation 39 import org.jetbrains.uast.UBinaryExpression 40 import org.jetbrains.uast.UCallExpression 41 import org.jetbrains.uast.UClassLiteralExpression 42 import org.jetbrains.uast.UElement 43 import org.jetbrains.uast.UExpression 44 import org.jetbrains.uast.ULiteralExpression 45 import org.jetbrains.uast.UQualifiedReferenceExpression 46 import org.jetbrains.uast.UReferenceExpression 47 import org.jetbrains.uast.util.isArrayInitializer 48 49 class UAnnotationItem private constructor( 50 override val codebase: PsiBasedCodebase, 51 val uAnnotation: UAnnotation, 52 override val originalName: String? 53 ) : DefaultAnnotationItem(codebase) { 54 override val qualifiedName: String? = AnnotationItem.mapName(originalName) 55 56 override fun toString(): String = toSource() 57 58 override fun toSource(target: AnnotationTarget, showDefaultAttrs: Boolean): String { 59 val sb = StringBuilder(60) 60 appendAnnotation(codebase, sb, uAnnotation, originalName, target, showDefaultAttrs) 61 return sb.toString() 62 } 63 64 override fun resolve(): ClassItem? { 65 return codebase.findOrCreateClass(originalName ?: return null) 66 } 67 68 override fun isNonNull(): Boolean { 69 if (uAnnotation.javaPsi is KtLightNullabilityAnnotation<*> && 70 originalName == "" 71 ) { 72 // Hack/workaround: some UAST annotation nodes do not provide qualified name :=( 73 return true 74 } 75 return super.isNonNull() 76 } 77 78 override val attributes: List<UAnnotationAttribute> by lazy { 79 uAnnotation.attributeValues.map { attribute -> 80 UAnnotationAttribute(codebase, attribute.name ?: ATTR_VALUE, attribute.expression) 81 }.toList() 82 } 83 84 override val targets: Set<AnnotationTarget> by lazy { 85 AnnotationItem.computeTargets(this, codebase::findOrCreateClass) 86 } 87 88 companion object { 89 fun create(codebase: PsiBasedCodebase, uAnnotation: UAnnotation, qualifiedName: String? = uAnnotation.qualifiedName): UAnnotationItem { 90 return UAnnotationItem(codebase, uAnnotation, qualifiedName) 91 } 92 93 fun create(codebase: PsiBasedCodebase, original: UAnnotationItem): UAnnotationItem { 94 return UAnnotationItem(codebase, original.uAnnotation, original.originalName) 95 } 96 97 private fun getAttributes(annotation: UAnnotation, showDefaultAttrs: Boolean): 98 List<Pair<String?, UExpression?>> { 99 val annotationClass = annotation.javaPsi?.nameReferenceElement?.resolve() as? PsiClass 100 val list = mutableListOf<Pair<String?, UExpression?>>() 101 if (annotationClass != null && showDefaultAttrs) { 102 for (method in annotationClass.methods) { 103 if (method !is PsiAnnotationMethod) { 104 continue 105 } 106 list.add(Pair(method.name, annotation.findAttributeValue(method.name))) 107 } 108 } else { 109 for (attr in annotation.attributeValues) { 110 list.add(Pair(attr.name, attr.expression)) 111 } 112 } 113 return list 114 } 115 116 private fun appendAnnotation( 117 codebase: PsiBasedCodebase, 118 sb: StringBuilder, 119 uAnnotation: UAnnotation, 120 originalName: String?, 121 target: AnnotationTarget, 122 showDefaultAttrs: Boolean 123 ) { 124 val qualifiedName = AnnotationItem.mapName(originalName, target) ?: return 125 126 val attributes = getAttributes(uAnnotation, showDefaultAttrs) 127 if (attributes.isEmpty()) { 128 sb.append("@$qualifiedName") 129 return 130 } 131 132 sb.append("@") 133 sb.append(qualifiedName) 134 sb.append("(") 135 if (attributes.size == 1 && (attributes[0].first == null || attributes[0].first == ATTR_VALUE)) { 136 // Special case: omit "value" if it's the only attribute 137 appendValue(codebase, sb, attributes[0].second, target, showDefaultAttrs) 138 } else { 139 var first = true 140 for (attribute in attributes) { 141 if (first) { 142 first = false 143 } else { 144 sb.append(", ") 145 } 146 sb.append(attribute.first ?: ATTR_VALUE) 147 sb.append('=') 148 appendValue(codebase, sb, attribute.second, target, showDefaultAttrs) 149 } 150 } 151 sb.append(")") 152 } 153 154 private fun appendValue( 155 codebase: PsiBasedCodebase, 156 sb: StringBuilder, 157 value: UExpression?, 158 target: AnnotationTarget, 159 showDefaultAttrs: Boolean 160 ) { 161 // Compute annotation string -- we don't just use value.text here 162 // because that may not use fully qualified names, e.g. the source may say 163 // @RequiresPermission(Manifest.permission.ACCESS_COARSE_LOCATION) 164 // and we want to compute 165 // @androidx.annotation.RequiresPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION) 166 when (value) { 167 null -> sb.append("null") 168 is ULiteralExpression -> sb.append(CodePrinter.constantToSource(value.value)) 169 is UQualifiedReferenceExpression -> { // the value is a Foo.BAR type of reference. 170 // expand `Foo` to fully qualified name `com.example.Foo` 171 appendQualifiedName(codebase, sb, value.receiver as UReferenceExpression) 172 // append accessor `.` 173 sb.append(value.accessType.name) 174 // append `BAR` 175 sb.append(value.selector.asRenderString()) 176 } 177 is UReferenceExpression -> { 178 // expand Foo to fully qualified name com.example.Foo 179 appendQualifiedName(codebase, sb, value) 180 } 181 is UBinaryExpression -> { 182 appendValue(codebase, sb, value.leftOperand, target, showDefaultAttrs) 183 sb.append(' ') 184 sb.append(value.operator.text) 185 sb.append(' ') 186 appendValue(codebase, sb, value.rightOperand, target, showDefaultAttrs) 187 } 188 is UCallExpression -> { 189 if (value.isArrayInitializer()) { 190 sb.append('{') 191 var first = true 192 for (initializer in value.valueArguments) { 193 if (first) { 194 first = false 195 } else { 196 sb.append(", ") 197 } 198 appendValue(codebase, sb, initializer, target, showDefaultAttrs) 199 } 200 sb.append('}') 201 } // TODO: support UCallExpression for other cases than array initializers 202 } 203 is UAnnotation -> { 204 appendAnnotation(codebase, sb, value, value.qualifiedName, target, showDefaultAttrs) 205 } 206 else -> { 207 val source = getConstantSource(value) 208 if (source != null) { 209 sb.append(source) 210 return 211 } 212 sb.append(value.sourcePsi?.text ?: value.asSourceString()) 213 } 214 } 215 } 216 217 private fun appendQualifiedName(codebase: PsiBasedCodebase, sb: StringBuilder, value: UReferenceExpression) { 218 when (val resolved = value.resolve()) { 219 is PsiField -> { 220 val containing = resolved.containingClass 221 if (containing != null) { 222 // If it's a field reference, see if it looks like the field is hidden; if 223 // so, inline the value 224 val cls = codebase.findOrCreateClass(containing) 225 val initializer = resolved.initializer 226 if (initializer != null) { 227 val fieldItem = cls.findField(resolved.name) 228 if (fieldItem == null || fieldItem.isHiddenOrRemoved()) { 229 // Use the literal value instead 230 val source = getConstantSource(initializer) 231 if (source != null) { 232 sb.append(source) 233 return 234 } 235 } 236 } 237 containing.qualifiedName?.let { 238 sb.append(it).append('.') 239 } 240 } 241 242 sb.append(resolved.name) 243 } 244 is PsiClass -> resolved.qualifiedName?.let { sb.append(it) } 245 else -> { 246 sb.append(value.sourcePsi?.text ?: value.asSourceString()) 247 } 248 } 249 } 250 251 private fun getConstantSource(value: UExpression): String? { 252 val constant = value.evaluate() 253 return CodePrinter.constantToExpression(constant) 254 } 255 256 private fun getConstantSource(value: PsiExpression): String? { 257 val constant = JavaConstantExpressionEvaluator.computeConstantExpression(value, false) 258 return CodePrinter.constantToExpression(constant) 259 } 260 } 261 } 262 263 class UAnnotationAttribute( 264 codebase: PsiBasedCodebase, 265 override val name: String, 266 psiValue: UExpression 267 ) : AnnotationAttribute { 268 override val value: AnnotationAttributeValue = UAnnotationValue.create( 269 codebase, psiValue 270 ) 271 } 272 273 abstract class UAnnotationValue : AnnotationAttributeValue { 274 companion object { createnull275 fun create(codebase: PsiBasedCodebase, value: UExpression): UAnnotationValue { 276 return if (value.isArrayInitializer()) { 277 UAnnotationArrayAttributeValue(codebase, value as UCallExpression) 278 } else { 279 UAnnotationSingleAttributeValue(codebase, value) 280 } 281 } 282 } 283 toStringnull284 override fun toString(): String = toSource() 285 } 286 287 class UAnnotationSingleAttributeValue( 288 private val codebase: PsiBasedCodebase, 289 private val psiValue: UExpression 290 ) : UAnnotationValue(), AnnotationSingleAttributeValue { 291 override val valueSource: String = getText(psiValue) 292 override val value: Any? 293 get() { 294 if (psiValue is ULiteralExpression) { 295 val value = psiValue.value 296 if (value != null) { 297 return value 298 } else if (psiValue.isNull) { 299 return null 300 } 301 } 302 if (psiValue is PsiLiteral) { 303 return psiValue.value ?: getText(psiValue).removeSurrounding("\"") 304 } 305 306 val value = ConstantEvaluator.evaluate(null, psiValue) 307 if (value != null) { 308 return value 309 } 310 311 if (psiValue is UClassLiteralExpression) { 312 // The value of a class literal expression like String.class or String::class 313 // is the fully qualified name, java.lang.String 314 val type = psiValue.type 315 if (type != null) { 316 return type.canonicalText 317 } 318 } 319 320 return getText(psiValue).removeSurrounding("\"") 321 } 322 323 override fun value(): Any? = value 324 325 override fun toSource(): String = getText(psiValue) 326 327 override fun resolve(): Item? { 328 if (psiValue is UReferenceExpression) { 329 when (val resolved = psiValue.resolve()) { 330 is PsiField -> return codebase.findField(resolved) 331 is PsiClass -> return codebase.findOrCreateClass(resolved) 332 is PsiMethod -> return codebase.findMethod(resolved) 333 } 334 } 335 return null 336 } 337 } 338 339 class UAnnotationArrayAttributeValue(codebase: PsiBasedCodebase, private val value: UCallExpression) : 340 UAnnotationValue(), AnnotationArrayAttributeValue { <lambda>null341 override val values = value.valueArguments.map { 342 create(codebase, it) 343 }.toList() 344 toSourcenull345 override fun toSource(): String = getText(value) 346 } 347 348 private fun getText(element: UElement): String { 349 return element.sourcePsi?.text ?: element.asSourceString() 350 } 351