• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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