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.BaseModifierList
20 import com.android.tools.metalava.model.CallableItem
21 import com.android.tools.metalava.model.ClassTypeItem
22 import com.android.tools.metalava.model.MutableModifierList
23 import com.android.tools.metalava.model.TypeItem
24 import com.android.tools.metalava.model.TypeParameterBindings
25 import com.android.tools.metalava.model.VisibilityLevel
26 import com.android.tools.metalava.model.item.DefaultParameterItem
27 import com.android.tools.metalava.model.item.ParameterDefaultValue
28 import com.android.tools.metalava.model.item.ParameterDefaultValueFactory
29 import com.android.tools.metalava.model.item.PublicNameProvider
30 import com.android.tools.metalava.model.type.MethodFingerprint
31 import com.intellij.psi.PsiEllipsisType
32 import com.intellij.psi.PsiParameter
33 import org.jetbrains.kotlin.analysis.api.analyze
34 import org.jetbrains.kotlin.psi.KtParameter
35 import org.jetbrains.uast.UParameter
36
37 internal class PsiParameterItem
38 internal constructor(
39 override val codebase: PsiBasedCodebase,
40 internal val psiParameter: PsiParameter,
41 modifiers: BaseModifierList,
42 name: String,
43 publicNameProvider: PublicNameProvider,
44 containingCallable: PsiCallableItem,
45 parameterIndex: Int,
46 type: TypeItem,
47 defaultValueFactory: ParameterDefaultValueFactory,
48 ) :
49 DefaultParameterItem(
50 codebase = codebase,
51 fileLocation = PsiFileLocation.fromPsiElement(psiParameter),
52 itemLanguage = psiParameter.itemLanguage,
53 modifiers = modifiers,
54 name = name,
55 publicNameProvider = publicNameProvider,
56 containingCallable = containingCallable,
57 parameterIndex = parameterIndex,
58 type = type,
59 defaultValueFactory = defaultValueFactory,
60 ),
61 PsiItem {
62
63 override var property: PsiPropertyItem? = null
64
psinull65 override fun psi() = psiParameter
66
67 // Note receiver parameter used to be named $receiver in previous UAST versions, now it is
68 // $this$functionName
69 internal fun isReceiver(): Boolean = parameterIndex == 0 && name().startsWith("\$this\$")
70
71 override fun isSamCompatibleOrKotlinLambda(): Boolean {
72 // Method is defined in Java source
73 if (isJava()) {
74 // Check the parameter type to see if it is defined in Kotlin or not.
75 // Interfaces defined in Kotlin do not support SAM conversion, but `fun` interfaces do.
76 // This is a best-effort check, since external dependencies (bytecode) won't appear to
77 // be Kotlin, and won't have a `fun` modifier visible. To resolve this, we could parse
78 // the kotlin.metadata annotation on the bytecode declaration (and special case
79 // kotlin.jvm.functions.Function* since the actual Kotlin lambda type can always be used
80 // with trailing lambda syntax), but in reality the amount of Java methods with a Kotlin
81 // interface with a single abstract method from an external dependency should be
82 // minimal, so just checking source will make this easier to maintain in the future.
83 val cls = type().asClass()
84 if (cls != null && cls.isKotlin()) {
85 return cls.isInterface() && cls.modifiers.isFunctional()
86 }
87 // Note: this will return `true` if the interface is defined in Kotlin, hence why we
88 // need the prior check as well
89 return type().let { it is ClassTypeItem && it.isFunctionalType() }
90 // Method is defined in Kotlin source
91 } else {
92 // For Kotlin declarations we can re-use the existing utilities for calculating whether
93 // a type is SAM convertible or not, which should handle external dependencies better
94 // and avoid any divergence from the actual compiler behaviour, if there are changes.
95 val parameter = (psi() as? UParameter)?.sourcePsi as? KtParameter ?: return false
96 analyze(parameter) {
97 val ktType = parameter.symbol.returnType
98 val isSamType = ktType.isFunctionalInterface
99 val isFunctionalType =
100 ktType.isFunctionType ||
101 ktType.isSuspendFunctionType ||
102 ktType.isKFunctionType ||
103 ktType.isKSuspendFunctionType
104 return isSamType || isFunctionalType
105 }
106 }
107 }
108
duplicatenull109 override fun duplicate(
110 containingCallable: CallableItem,
111 typeVariableMap: TypeParameterBindings
112 ) =
113 PsiParameterItem(
114 codebase = codebase,
115 psiParameter = psiParameter,
116 modifiers = modifiers,
117 name = name(),
118 publicNameProvider = publicNameProvider,
119 containingCallable = containingCallable as PsiCallableItem,
120 parameterIndex = parameterIndex,
121 type = type().convertType(typeVariableMap) as PsiTypeItem,
122 defaultValueFactory = defaultValue::duplicate,
123 )
124
125 companion object {
126 internal fun create(
127 codebase: PsiBasedCodebase,
128 containingCallable: PsiCallableItem,
129 fingerprint: MethodFingerprint,
130 psiParameter: PsiParameter,
131 parameterIndex: Int,
132 enclosingMethodTypeItemFactory: PsiTypeItemFactory,
133 ): PsiParameterItem {
134 val name = psiParameter.name
135 val modifiers = createParameterModifiers(codebase, psiParameter)
136 val psiType = codebase.psiAssembler.getPsiTypeForPsiParameter(psiParameter)
137 val type =
138 enclosingMethodTypeItemFactory.getMethodParameterType(
139 underlyingParameterType = PsiTypeInfo(psiType, psiParameter),
140 itemAnnotations = modifiers.annotations(),
141 fingerprint = fingerprint,
142 parameterIndex = parameterIndex,
143 isVarArg = psiType is PsiEllipsisType,
144 )
145 val parameter =
146 PsiParameterItem(
147 codebase = codebase,
148 psiParameter = psiParameter,
149 modifiers = modifiers,
150 name = name,
151 publicNameProvider = { (it as PsiParameterItem).getPublicName() },
152 containingCallable = containingCallable,
153 parameterIndex = parameterIndex,
154 // Need to down cast as [isSamCompatibleOrKotlinLambda] needs access to the
155 // underlying PsiType.
156 type = type as PsiTypeItem,
157 defaultValueFactory = {
158 if (it.isKotlin()) PsiParameterDefaultValue(it as PsiParameterItem)
159 else ParameterDefaultValue.NONE
160 },
161 )
162 return parameter
163 }
164
165 private fun createParameterModifiers(
166 codebase: PsiBasedCodebase,
167 psiParameter: PsiParameter
168 ): MutableModifierList {
169 val modifiers = PsiModifierItem.create(codebase, psiParameter)
170 // Method parameters don't have a visibility level; they are visible to anyone that can
171 // call their method. However, Kotlin constructors sometimes appear to specify the
172 // visibility of a constructor parameter by putting visibility inside the constructor
173 // signature. This is really to indicate that the matching property should have the
174 // mentioned visibility.
175 // If the method parameter seems to specify a visibility level, we correct it back to
176 // the default, here, to ensure we don't attempt to incorrectly emit this information
177 // into a signature file.
178 modifiers.setVisibilityLevel(VisibilityLevel.PACKAGE_PRIVATE)
179 return modifiers
180 }
181 }
182 }
183
184 /** Get the public name of this parameter. */
getPublicNamenull185 internal fun PsiParameterItem.getPublicName(): String? {
186 if (psiParameter.isKotlin()) {
187 // Omit names of some special parameters in Kotlin. None of these parameters may be set
188 // through Kotlin keyword arguments, so there's no need to track their names for
189 // compatibility. This also helps avoid signature file churn if PSI or the compiler change
190 // what name they're using for these parameters.
191
192 // Receiver parameter of extension function
193 if (isReceiver()) {
194 return null
195 }
196 // Property setter parameter
197 if (possibleContainingMethod()?.isKotlinProperty() == true) {
198 return null
199 }
200 // Continuation parameter of suspend function
201 if (
202 containingCallable().modifiers.isSuspend() &&
203 "kotlin.coroutines.Continuation" == type().asClass()?.qualifiedName() &&
204 containingCallable().parameters().size - 1 == parameterIndex
205 ) {
206 return null
207 }
208 return name()
209 }
210
211 return null
212 }
213