1 /* <lambda>null2 * 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.DefaultModifierList 20 import com.android.tools.metalava.model.FieldItem 21 import com.android.tools.metalava.model.PropertyItem 22 import com.android.tools.metalava.model.TypeItem 23 import com.intellij.psi.PsiMethod 24 import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget 25 import org.jetbrains.kotlin.psi.KtPropertyAccessor 26 import org.jetbrains.uast.UAnnotation 27 import org.jetbrains.uast.toUElement 28 29 class PsiPropertyItem 30 private constructor( 31 codebase: PsiBasedCodebase, 32 private val psiMethod: PsiMethod, 33 containingClass: PsiClassItem, 34 name: String, 35 modifiers: DefaultModifierList, 36 documentation: String, 37 private val fieldType: PsiTypeItem, 38 override val getter: PsiMethodItem, 39 override val setter: PsiMethodItem?, 40 override val constructorParameter: PsiParameterItem?, 41 override val backingField: PsiFieldItem? 42 ) : 43 PsiMemberItem( 44 codebase = codebase, 45 modifiers = modifiers, 46 documentation = documentation, 47 element = psiMethod, 48 containingClass = containingClass, 49 name = name, 50 ), 51 PropertyItem { 52 53 override fun type(): TypeItem = fieldType 54 55 override fun psi() = psiMethod 56 57 override fun equals(other: Any?): Boolean { 58 if (this === other) { 59 return true 60 } 61 return other is FieldItem && 62 name == other.name() && 63 containingClass == other.containingClass() 64 } 65 66 override fun hashCode(): Int { 67 return name.hashCode() 68 } 69 70 companion object { 71 /** 72 * Creates a new property item, given a [name], [type] and relationships to other items. 73 * 74 * Kotlin's properties consist of up to four other declarations: Their accessor functions, 75 * primary constructor parameter, and a backing field. These relationships are useful for 76 * resolving documentation and exposing the model correctly in Kotlin stubs. 77 * 78 * Metalava currently requires all properties to have a [getter]. It does not currently 79 * support private, `const val`, or [JvmField] properties. Mutable `var` properties usually 80 * have a [setter], but properties with a private default setter may use direct field access 81 * instead. 82 * 83 * Properties declared in the primary constructor of a class have an associated 84 * [constructorParameter]. This relationship is important for resolving docs which may exist 85 * on the constructor parameter. 86 * 87 * Most properties on classes without a custom getter have a [backingField] to hold their 88 * value. This is private except for [JvmField] properties. 89 */ 90 internal fun create( 91 codebase: PsiBasedCodebase, 92 containingClass: PsiClassItem, 93 name: String, 94 type: PsiTypeItem, 95 getter: PsiMethodItem, 96 setter: PsiMethodItem? = null, 97 constructorParameter: PsiParameterItem? = null, 98 backingField: PsiFieldItem? = null 99 ): PsiPropertyItem { 100 val psiMethod = getter.psiMethod 101 val documentation = 102 when (val sourcePsi = getter.sourcePsi) { 103 is KtPropertyAccessor -> 104 javadoc(sourcePsi.property, codebase.allowReadingComments) 105 else -> javadoc(sourcePsi ?: psiMethod, codebase.allowReadingComments) 106 } 107 val modifiers = modifiers(codebase, psiMethod, documentation) 108 // Alas, annotations whose target is property won't be bound to anywhere in LC/UAST, 109 // if the property doesn't need a backing field. Same for unspecified use-site target. 110 // To preserve such annotations, our last resort is to examine source PSI directly. 111 if (backingField == null) { 112 val ktProperty = (getter.sourcePsi as? KtPropertyAccessor)?.property 113 val annotations = 114 ktProperty?.annotationEntries?.mapNotNull { 115 val useSiteTarget = it.useSiteTarget?.getAnnotationUseSiteTarget() 116 if ( 117 useSiteTarget == null || 118 useSiteTarget == AnnotationUseSiteTarget.PROPERTY 119 ) { 120 it.toUElement() as? UAnnotation 121 } else null 122 } 123 annotations?.forEach { uAnnotation -> 124 val annotationItem = UAnnotationItem.create(codebase, uAnnotation) 125 if (annotationItem !in modifiers.annotations()) { 126 modifiers.addAnnotation(annotationItem) 127 } 128 } 129 } 130 val property = 131 PsiPropertyItem( 132 codebase = codebase, 133 psiMethod = psiMethod, 134 containingClass = containingClass, 135 name = name, 136 documentation = documentation, 137 modifiers = modifiers, 138 fieldType = type, 139 getter = getter, 140 setter = setter, 141 constructorParameter = constructorParameter, 142 backingField = backingField 143 ) 144 getter.property = property 145 setter?.property = property 146 constructorParameter?.property = property 147 backingField?.property = property 148 return property 149 } 150 } 151 } 152