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