1 /* 2 * Copyright (C) 2024 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.ParameterItem 20 import com.android.tools.metalava.model.item.ParameterDefaultValue 21 import org.jetbrains.kotlin.analysis.api.KaExperimentalApi 22 import org.jetbrains.kotlin.analysis.api.analyze 23 import org.jetbrains.kotlin.analysis.api.symbols.KaFunctionSymbol 24 import org.jetbrains.kotlin.analysis.api.symbols.KaNamedFunctionSymbol 25 import org.jetbrains.kotlin.analysis.api.symbols.KaParameterSymbol 26 import org.jetbrains.kotlin.analysis.api.symbols.KaValueParameterSymbol 27 import org.jetbrains.kotlin.psi.KtConstantExpression 28 import org.jetbrains.kotlin.psi.KtFunction 29 import org.jetbrains.kotlin.psi.KtParameter 30 import org.jetbrains.kotlin.psi.psiUtil.hasActualModifier 31 import org.jetbrains.uast.UExpression 32 import org.jetbrains.uast.UMethod 33 import org.jetbrains.uast.UastFacade 34 35 internal class PsiParameterDefaultValue(private val item: PsiParameterItem) : 36 ParameterDefaultValue { 37 duplicatenull38 override fun duplicate(parameter: ParameterItem) = 39 PsiParameterDefaultValue(parameter as PsiParameterItem) 40 41 private var hasDefaultValue: Boolean? = null 42 43 override fun hasDefaultValue(): Boolean { 44 if (hasDefaultValue == null) { 45 hasDefaultValue = item.computeHasDefaultValue() 46 } 47 return hasDefaultValue!! 48 } 49 50 @OptIn(KaExperimentalApi::class) computeHasDefaultValuenull51 private fun PsiParameterItem.computeHasDefaultValue(): Boolean { 52 if (psiParameter.isKotlin()) { 53 val psiCallableItem = item.containingCallable() as PsiCallableItem 54 val ktFunction = 55 ((psiCallableItem.psi() as? UMethod)?.sourcePsi as? KtFunction) ?: return false 56 57 analyze(ktFunction) { 58 val function = 59 if (ktFunction.hasActualModifier()) { 60 ktFunction.symbol.getExpectsForActual().singleOrNull() 61 } else { 62 ktFunction.symbol 63 } 64 if (function !is KaFunctionSymbol) return false 65 val symbol = getKtParameterSymbol(function) ?: return false 66 if (symbol is KaValueParameterSymbol && symbol.hasDefaultValue) { 67 val defaultValue = (symbol.psi as? KtParameter)?.defaultValue ?: return false 68 if (defaultValue is KtConstantExpression) { 69 return true 70 } 71 72 return UastFacade.convertElement(defaultValue, null, UExpression::class.java) is 73 UExpression 74 } 75 } 76 } 77 78 return false 79 } 80 getKtParameterSymbolnull81 private fun PsiParameterItem.getKtParameterSymbol( 82 functionSymbol: KaFunctionSymbol 83 ): KaParameterSymbol? { 84 if (isReceiver()) { 85 return functionSymbol.receiverParameter 86 } 87 88 // Perform matching based on parameter names, because indices won't work in the 89 // presence of @JvmOverloads where UAST generates multiple permutations of the 90 // method from the same KtParameters array. 91 val parameters = functionSymbol.valueParameters 92 93 val index = if (functionSymbol.isExtension) parameterIndex - 1 else parameterIndex 94 val isSuspend = (functionSymbol as? KaNamedFunctionSymbol)?.isSuspend == true 95 if (isSuspend && index >= parameters.size) { 96 // suspend functions have continuation as a last parameter, which is not 97 // defined in the symbol 98 return null 99 } 100 101 // Quick lookup first which usually works 102 if (index >= 0) { 103 val parameter = parameters[index] 104 if (parameter.name.asString() == name()) { 105 return parameter 106 } 107 } 108 109 for (parameter in parameters) { 110 if (parameter.name.asString() == name()) { 111 return parameter 112 } 113 } 114 115 // Fallback to handle scenario where the real parameter names are hidden by 116 // UAST (see UastKotlinPsiParameter which replaces parameter names to p$index) 117 if (index >= 0) { 118 val parameter = parameters[index] 119 if (!isReceiver()) { 120 return parameter 121 } 122 } 123 124 return null 125 } 126 } 127