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.model.MethodItem 20 import com.android.tools.metalava.model.ParameterItem 21 import com.android.tools.metalava.model.TypeItem 22 import com.android.tools.metalava.model.psi.CodePrinter.Companion.constantToSource 23 import com.intellij.psi.PsiParameter 24 import org.jetbrains.kotlin.psi.KtConstantExpression 25 import org.jetbrains.kotlin.psi.KtNamedFunction 26 import org.jetbrains.kotlin.psi.KtParameter 27 import org.jetbrains.uast.UExpression 28 import org.jetbrains.uast.UastFacade 29 import org.jetbrains.uast.kotlin.declarations.KotlinUMethod 30 31 class PsiParameterItem( 32 override val codebase: PsiBasedCodebase, 33 private val psiParameter: PsiParameter, 34 private val name: String, 35 override val parameterIndex: Int, 36 modifiers: PsiModifierItem, 37 documentation: String, 38 private val type: PsiTypeItem 39 ) : PsiItem( 40 codebase = codebase, 41 modifiers = modifiers, 42 documentation = documentation, 43 element = psiParameter 44 ), ParameterItem { 45 lateinit var containingMethod: PsiMethodItem 46 namenull47 override fun name(): String = name 48 49 override fun publicName(): String? { 50 if (isKotlin(psiParameter)) { 51 // Don't print out names for extension function receiver parameters 52 if (isReceiver()) { 53 return null 54 } 55 return name 56 } else { 57 // Java: Look for @ParameterName annotation 58 val annotation = modifiers.annotations().firstOrNull { it.isParameterName() } 59 if (annotation != null) { 60 return annotation.attributes().firstOrNull()?.value?.value()?.toString() 61 } 62 } 63 64 return null 65 } 66 hasDefaultValuenull67 override fun hasDefaultValue(): Boolean { 68 return if (isKotlin(psiParameter)) { 69 getKtParameter()?.hasDefaultValue() ?: false && defaultValue() != INVALID_VALUE 70 } else { 71 // Java: Look for @ParameterName annotation 72 modifiers.annotations().any { it.isDefaultValue() } 73 } 74 } 75 76 // Note receiver parameter used to be named $receiver in previous UAST versions, now it is $this$functionName isReceivernull77 private fun isReceiver(): Boolean = parameterIndex == 0 && name.startsWith("\$this\$") 78 79 private fun getKtParameter(): KtParameter? { 80 val ktParameters = 81 ((containingMethod.psiMethod as? KotlinUMethod)?.sourcePsi as? KtNamedFunction)?.valueParameters 82 ?: return null 83 84 // Perform matching based on parameter names, because indices won't work in the 85 // presence of @JvmOverloads where UAST generates multiple permutations of the 86 // method from the same KtParameters array. 87 88 // Quick lookup first which usually works (lined up from the end to account 89 // for receivers for extension methods etc) 90 val rem = containingMethod.parameters().size - parameterIndex 91 val index = ktParameters.size - rem 92 if (index >= 0) { 93 val parameter = ktParameters[index] 94 if (parameter.name == name) { 95 return parameter 96 } 97 } 98 99 for (parameter in ktParameters) { 100 if (parameter.name == name) { 101 return parameter 102 } 103 } 104 105 // Fallback to handle scenario where the real parameter names are hidden by 106 // UAST (see UastKotlinPsiParameter which replaces parameter names to p$index) 107 if (index >= 0) { 108 val parameter = ktParameters[index] 109 if (!isReceiver()) { 110 return parameter 111 } 112 } 113 114 return null 115 } 116 117 override val synthetic: Boolean get() = containingMethod.isEnumSyntheticMethod() 118 119 private var defaultValue: String? = null 120 defaultValuenull121 override fun defaultValue(): String? { 122 if (defaultValue == null) { 123 defaultValue = computeDefaultValue() 124 } 125 return defaultValue 126 } 127 computeDefaultValuenull128 private fun computeDefaultValue(): String? { 129 if (isKotlin(psiParameter)) { 130 val ktParameter = getKtParameter() ?: return null 131 if (ktParameter.hasDefaultValue()) { 132 val defaultValue = ktParameter.defaultValue ?: return null 133 if (defaultValue is KtConstantExpression) { 134 return defaultValue.text 135 } 136 137 val defaultExpression: UExpression = UastFacade.convertElement( 138 defaultValue, null, 139 UExpression::class.java 140 ) as? UExpression ?: return INVALID_VALUE 141 val constant = defaultExpression.evaluate() 142 return if (constant != null && constant !is Pair<*, *>) { 143 constantToSource(constant) 144 } else { 145 // Expression: Compute from UAST rather than just using the source text 146 // such that we can ensure references are fully qualified etc. 147 codebase.printer.toSourceString(defaultExpression) 148 } 149 } 150 151 return INVALID_VALUE 152 } else { 153 // Java: Look for @ParameterName annotation 154 val annotation = modifiers.annotations().firstOrNull { it.isDefaultValue() } 155 if (annotation != null) { 156 return annotation.attributes().firstOrNull()?.value?.value()?.toString() 157 } 158 } 159 160 return null 161 } 162 typenull163 override fun type(): TypeItem = type 164 override fun containingMethod(): MethodItem = containingMethod 165 166 override fun equals(other: Any?): Boolean { 167 if (this === other) { 168 return true 169 } 170 return other is ParameterItem && parameterIndex == other.parameterIndex && containingMethod == other.containingMethod() 171 } 172 hashCodenull173 override fun hashCode(): Int { 174 return parameterIndex 175 } 176 toStringnull177 override fun toString(): String = "parameter ${name()}" 178 179 override fun isVarArgs(): Boolean { 180 return psiParameter.isVarArgs || modifiers.isVarArg() 181 } 182 183 companion object { createnull184 fun create( 185 codebase: PsiBasedCodebase, 186 psiParameter: PsiParameter, 187 parameterIndex: Int 188 ): PsiParameterItem { 189 val name = psiParameter.name 190 val commentText = "" // no javadocs on individual parameters 191 val modifiers = modifiers(codebase, psiParameter, commentText) 192 val type = codebase.getType(psiParameter.type) 193 val parameter = PsiParameterItem( 194 codebase = codebase, 195 psiParameter = psiParameter, 196 name = name, 197 parameterIndex = parameterIndex, 198 documentation = commentText, 199 modifiers = modifiers, 200 type = type 201 ) 202 parameter.modifiers.setOwner(parameter) 203 return parameter 204 } 205 createnull206 fun create( 207 codebase: PsiBasedCodebase, 208 original: PsiParameterItem 209 ): PsiParameterItem { 210 val parameter = PsiParameterItem( 211 codebase = codebase, 212 psiParameter = original.psiParameter, 213 name = original.name, 214 parameterIndex = original.parameterIndex, 215 documentation = original.documentation, 216 modifiers = PsiModifierItem.create(codebase, original.modifiers), 217 type = PsiTypeItem.create(codebase, original.type) 218 ) 219 parameter.modifiers.setOwner(parameter) 220 return parameter 221 } 222 createnull223 fun create( 224 codebase: PsiBasedCodebase, 225 original: List<ParameterItem> 226 ): List<PsiParameterItem> { 227 return original.map { create(codebase, it as PsiParameterItem) } 228 } 229 230 /** 231 * Private marker return value from [#computeDefaultValue] signifying that the parameter 232 * has a default value but we were unable to compute a suitable static string representation for it 233 */ 234 private const val INVALID_VALUE = "__invalid_value__" 235 } 236 }