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