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