• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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.ApiVariantSelectors
20 import com.android.tools.metalava.model.BaseModifierList
21 import com.android.tools.metalava.model.ClassItem
22 import com.android.tools.metalava.model.FieldItem
23 import com.android.tools.metalava.model.ItemDocumentationFactory
24 import com.android.tools.metalava.model.MethodItem
25 import com.android.tools.metalava.model.ParameterItem
26 import com.android.tools.metalava.model.PropertyItem
27 import com.android.tools.metalava.model.TypeItem
28 import com.android.tools.metalava.model.TypeParameterList
29 import com.android.tools.metalava.model.item.DefaultPropertyItem
30 import org.jetbrains.kotlin.psi.KtDeclaration
31 import org.jetbrains.kotlin.psi.KtParameter
32 import org.jetbrains.kotlin.psi.KtProperty
33 import org.jetbrains.kotlin.psi.KtPropertyAccessor
34 import org.jetbrains.kotlin.psi.KtTypeParameterListOwner
35 
36 internal class PsiPropertyItem
37 private constructor(
38     override val codebase: PsiBasedCodebase,
39     private val ktDeclaration: KtDeclaration,
40     modifiers: BaseModifierList,
41     // This needs to be passed in because the documentation may come from the property, or it may
42     // come from the getter method.
43     documentationFactory: ItemDocumentationFactory,
44     name: String,
45     containingClass: ClassItem,
46     type: TypeItem,
47     getter: MethodItem?,
48     setter: MethodItem?,
49     constructorParameter: ParameterItem?,
50     backingField: FieldItem?,
51     receiver: TypeItem?,
52     typeParameterList: TypeParameterList,
53 ) :
54     DefaultPropertyItem(
55         codebase = codebase,
56         fileLocation = PsiFileLocation(ktDeclaration),
57         itemLanguage = ktDeclaration.itemLanguage,
58         modifiers = modifiers,
59         documentationFactory = documentationFactory,
60         variantSelectorsFactory = ApiVariantSelectors.MUTABLE_FACTORY,
61         name = name,
62         containingClass = containingClass,
63         type = type,
64         getter = getter,
65         setter = setter,
66         constructorParameter = constructorParameter,
67         backingField = backingField,
68         receiver = receiver,
69         typeParameterList = typeParameterList,
70     ),
71     PropertyItem,
72     PsiItem {
73 
psinull74     override fun psi() = ktDeclaration
75 
76     companion object {
77         /**
78          * Creates a new property item for the [ktDeclaration], given relationships to other items.
79          *
80          * Kotlin's properties consist of up to four other declarations: Their accessor functions,
81          * primary constructor parameter, and a backing field. These relationships are useful for
82          * resolving documentation and exposing the model correctly in Kotlin stubs.
83          *
84          * Most properties have a getter, but those that are available through field access in Java
85          * (e.g. `const val` and [JvmField] properties) or are not accessible from Java (e.g.
86          * private properties and non-constructor value class properties) do not.
87          *
88          * Mutable `var` properties usually have a setter, but properties with a private default
89          * setter may use direct field access instead.
90          *
91          * The [accessors] should contain the getter and setter, if they exist. It may also contain
92          * other accessors, like data class component methods.
93          *
94          * Properties declared in the primary constructor of a class have an associated
95          * [constructorParameter]. This relationship is important for resolving docs which may exist
96          * on the constructor parameter.
97          *
98          * Most properties on classes without a custom getter have a [backingField] to hold their
99          * value. This is private except for [JvmField] properties.
100          */
101         internal fun create(
102             codebase: PsiBasedCodebase,
103             ktDeclaration: KtDeclaration,
104             containingClass: ClassItem,
105             containingTypeItemFactory: PsiTypeItemFactory,
106             accessors: List<PsiMethodItem>,
107             constructorParameter: PsiParameterItem? = null,
108             backingField: PsiFieldItem? = null,
109         ): PsiPropertyItem? {
110             val name = ktDeclaration.name ?: return null
111 
112             val (typeParameterList, typeItemFactory) =
113                 PsiTypeParameterList.create(
114                     codebase,
115                     containingTypeItemFactory,
116                     "property $name",
117                     ktDeclaration as? KtTypeParameterListOwner
118                 )
119 
120             // Compute the type of the receiver, if there is one. This will be used to find the
121             // right accessors for the property.
122             val receiverType =
123                 (ktDeclaration as? KtProperty)?.receiverTypeReference?.let {
124                     typeItemFactory.getTypeForKtElement(it)
125                 }
126 
127             // Determine which accessors are the getter and setter.
128             val getter = findGetter(accessors, receiverType)
129             val setter = findSetter(accessors, receiverType)
130 
131             val type =
132                 getter?.returnType()
133                     ?: typeItemFactory.getTypeForKtElement(ktDeclaration) ?: return null
134             val modifiers =
135                 PsiModifierItem.createForProperty(codebase, ktDeclaration, getter, setter)
136             if (modifiers.isFinal() && containingClass.modifiers.isFinal()) {
137                 // The containing class is final, so it is implied that every property is final as
138                 // well. No need to apply 'final' to each property. (This is done for methods too.)
139                 modifiers.setFinal(false)
140             }
141 
142             val property =
143                 PsiPropertyItem(
144                     codebase = codebase,
145                     ktDeclaration = ktDeclaration,
146                     modifiers = modifiers,
147                     documentationFactory = PsiItemDocumentation.factory(ktDeclaration, codebase),
148                     name = name,
149                     containingClass = containingClass,
150                     type = type,
151                     getter = getter,
152                     setter = setter,
153                     constructorParameter = constructorParameter,
154                     backingField = backingField,
155                     receiver = receiverType,
156                     typeParameterList = typeParameterList,
157                 )
158             getter?.property = property
159             setter?.property = property
160             constructorParameter?.property = property
161             backingField?.property = property
162             return property
163         }
164 
165         /**
166          * Given [allAccessors] for a property ([PsiMethodItem]s] for which the source element is
167          * the same [KtProperty]/[KtParameter]), finds the getter.
168          */
169         private fun findGetter(
170             allAccessors: List<PsiMethodItem>,
171             propertyReceiverType: PsiTypeItem?
172         ): PsiMethodItem? {
173             return if (propertyReceiverType == null) {
174                 // No receiver, so the getter has no parameter. Make sure not to find a data class
175                 // component method.
176                 allAccessors.singleOrNull {
177                     it.parameters().isEmpty() && !it.name().startsWith("component")
178                 }
179             } else {
180                 // If there's a receiver, the getter should have the receiver type as its parameter.
181                 allAccessors.singleOrNull {
182                     it.parameters().singleOrNull()?.type() == propertyReceiverType
183                 }
184                 // Work around a psi bug where value class extension property accessors don't
185                 // include the receiver (b/385148821). This strategy does not always work, which is
186                 // why the one above is used in most cases: the getter for a property parameter's
187                 // source element will be a KtParameter, and the getter for a simple property
188                 // declaration with no custom getter declaration will be a KtProperty, not a
189                 // KtPropertyAccessor.
190                 ?: allAccessors.singleOrNull {
191                         (it.psiMethod.sourceElement as? KtPropertyAccessor)?.isGetter == true
192                     }
193             }
194         }
195 
196         /** Like [findGetter], but finds the property setter instead. */
197         private fun findSetter(
198             allAccessors: List<PsiMethodItem>,
199             propertyReceiverType: PsiTypeItem?
200         ): PsiMethodItem? {
201             return if (propertyReceiverType == null) {
202                 // No receiver, the setter has one parameter.
203                 allAccessors.singleOrNull { it.parameters().size == 1 }
204             } else {
205                 // The setter has a receiver parameter in addition to the normal setter parameter.
206                 allAccessors.singleOrNull {
207                     it.parameters().size == 2 && it.parameters()[0].type() == propertyReceiverType
208                 }
209                 // Work around a psi bug, see the equivalent [findGetter] case for details.
210                 ?: allAccessors.singleOrNull {
211                         (it.psiMethod.sourceElement as? KtPropertyAccessor)?.isSetter == true
212                     }
213             }
214         }
215     }
216 }
217