1 /* <lambda>null2 * 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.ApiVariantSelectors 20 import com.android.tools.metalava.model.BaseModifierList 21 import com.android.tools.metalava.model.ClassItem 22 import com.android.tools.metalava.model.ClassKind 23 import com.android.tools.metalava.model.ExceptionTypeItem 24 import com.android.tools.metalava.model.ItemDocumentationFactory 25 import com.android.tools.metalava.model.MethodItem 26 import com.android.tools.metalava.model.TypeItem 27 import com.android.tools.metalava.model.TypeParameterList 28 import com.android.tools.metalava.model.VisibilityLevel 29 import com.android.tools.metalava.model.item.DefaultMethodItem 30 import com.android.tools.metalava.model.item.ParameterItemsFactory 31 import com.android.tools.metalava.model.psi.PsiCallableItem.Companion.parameterList 32 import com.android.tools.metalava.model.psi.PsiCallableItem.Companion.throwsTypes 33 import com.android.tools.metalava.model.type.MethodFingerprint 34 import com.android.tools.metalava.reporter.FileLocation 35 import com.intellij.psi.PsiAnnotationMethod 36 import com.intellij.psi.PsiMethod 37 import com.intellij.psi.PsiParameter 38 import org.jetbrains.kotlin.psi.KtNamedFunction 39 import org.jetbrains.kotlin.psi.KtParameter 40 import org.jetbrains.kotlin.psi.KtProperty 41 import org.jetbrains.kotlin.psi.KtPropertyAccessor 42 import org.jetbrains.uast.UAnnotation 43 import org.jetbrains.uast.UAnnotationMethod 44 import org.jetbrains.uast.UMethod 45 import org.jetbrains.uast.kotlin.KotlinUMethodWithFakeLightDelegateBase 46 import org.jetbrains.uast.toUElement 47 48 internal class PsiMethodItem( 49 override val codebase: PsiBasedCodebase, 50 override val psiMethod: PsiMethod, 51 fileLocation: FileLocation = PsiFileLocation(psiMethod), 52 // Takes ClassItem as this may be duplicated from a PsiBasedCodebase on the classpath into a 53 // TextClassItem. 54 containingClass: ClassItem, 55 name: String, 56 modifiers: BaseModifierList, 57 documentationFactory: ItemDocumentationFactory, 58 returnType: TypeItem, 59 parameterItemsFactory: ParameterItemsFactory, 60 typeParameterList: TypeParameterList, 61 throwsTypes: List<ExceptionTypeItem>, 62 ) : 63 DefaultMethodItem( 64 codebase = codebase, 65 fileLocation = fileLocation, 66 itemLanguage = psiMethod.itemLanguage, 67 modifiers = modifiers, 68 documentationFactory = documentationFactory, 69 variantSelectorsFactory = ApiVariantSelectors.MUTABLE_FACTORY, 70 name = name, 71 containingClass = containingClass, 72 typeParameterList = typeParameterList, 73 returnType = returnType, 74 parameterItemsFactory = parameterItemsFactory, 75 throwsTypes = throwsTypes, 76 callableBodyFactory = { PsiCallableBody(it as PsiCallableItem) }, 77 ), 78 PsiCallableItem { 79 80 override var property: PsiPropertyItem? = null 81 isExtensionMethodnull82 override fun isExtensionMethod(): Boolean { 83 if (isKotlin()) { 84 val ktParameters = 85 ((psiMethod as? UMethod)?.sourcePsi as? KtNamedFunction)?.valueParameters 86 ?: return false 87 return ktParameters.size < parameters().size 88 } 89 90 return false 91 } 92 isKotlinPropertynull93 override fun isKotlinProperty(): Boolean { 94 return psiMethod is UMethod && 95 (psiMethod.sourcePsi is KtProperty || 96 psiMethod.sourcePsi is KtPropertyAccessor || 97 psiMethod.sourcePsi is KtParameter && 98 (psiMethod.sourcePsi as KtParameter).hasValOrVar()) 99 } 100 legacyDefaultValuenull101 override fun legacyDefaultValue(): String { 102 return when (psiMethod) { 103 is UAnnotationMethod -> { 104 psiMethod.uastDefaultValue?.let { codebase.printer.toSourceString(it) } ?: "" 105 } 106 is PsiAnnotationMethod -> { 107 psiMethod.defaultValue?.let { codebase.printer.toSourceExpression(it, this) } 108 ?: super.legacyDefaultValue() 109 } 110 else -> super.legacyDefaultValue() 111 } 112 } 113 duplicatenull114 override fun duplicate(targetContainingClass: ClassItem): PsiMethodItem { 115 // If duplicating within the same codebase type then map the type variables, otherwise do 116 // not. That is because this can end up substituting a `TypeItem` implementation of one 117 // type in place of a `PsiTypeItem` which can cause casting issues, e.g. in 118 // `PsiParameterItem` which expects its type as `PsiTypeItem`. Falling back to not mapping 119 // will not cause any significant issues as that is what was done before. 120 // TODO(b/324196754): Fix this. It is not clear if this causes problems outside tests, it 121 // does not seem to break Android build. 122 val typeVariableMap = 123 if (codebase.javaClass === targetContainingClass.codebase.javaClass) 124 targetContainingClass.mapTypeVariables(containingClass()) 125 else emptyMap() 126 127 return PsiMethodItem( 128 codebase, 129 psiMethod, 130 fileLocation, 131 targetContainingClass, 132 name(), 133 modifiers, 134 documentation::duplicate, 135 returnType.convertType(typeVariableMap), 136 { methodItem -> 137 parameters().map { 138 (it as PsiParameterItem).duplicate(methodItem, typeVariableMap) 139 } 140 }, 141 typeParameterList, 142 throwsTypes(), 143 ) 144 .also { duplicated -> 145 duplicated.inheritedFrom = containingClass() 146 147 duplicated.updateCopiedMethodState() 148 } 149 } 150 151 /* Call corresponding PSI utility method -- if I can find it! 152 override fun matches(other: MethodItem): Boolean { 153 if (other !is PsiMethodItem) { 154 return super.matches(other) 155 } 156 157 // TODO: Find better API: this also checks surrounding class which we don't want! 158 return psiMethod.isEquivalentTo(other.psiMethod) 159 } 160 */ 161 162 companion object { 163 /** 164 * Create a [PsiMethodItem]. 165 * 166 * The [containingClass] is not necessarily a [PsiClassItem] as this is used to implement 167 * [MethodItem.duplicate] as well as create [PsiMethodItem] from the underlying Psi source 168 * model. 169 */ createnull170 internal fun create( 171 codebase: PsiBasedCodebase, 172 containingClass: ClassItem, 173 psiMethod: PsiMethod, 174 enclosingClassTypeItemFactory: PsiTypeItemFactory, 175 psiParameters: List<PsiParameter> = psiMethod.psiParameters, 176 ): PsiMethodItem { 177 assert(!psiMethod.isConstructor) 178 // UAST workaround: @JvmName for UMethod with fake LC PSI 179 // TODO: https://youtrack.jetbrains.com/issue/KTIJ-25133 180 val name = 181 if (psiMethod is KotlinUMethodWithFakeLightDelegateBase<*>) { 182 psiMethod.sourcePsi 183 ?.annotationEntries 184 ?.find { annoEntry -> 185 val text = annoEntry.typeReference?.text ?: return@find false 186 JvmName::class.qualifiedName?.contains(text) == true 187 } 188 ?.toUElement(UAnnotation::class.java) 189 ?.takeIf { 190 // Above `find` deliberately avoids resolution and uses verbatim text. 191 // Below, we need annotation value anyway, but just double-check 192 // if the converted annotation is indeed the resolved @JvmName 193 it.qualifiedName == JvmName::class.qualifiedName 194 } 195 ?.findAttributeValue("name") 196 ?.evaluate() as? String 197 ?: psiMethod.name 198 } else { 199 psiMethod.name 200 } 201 val modifiers = PsiModifierItem.create(codebase, psiMethod) 202 203 if (containingClass.classKind == ClassKind.INTERFACE) { 204 // All interface methods are implicitly public (except in Java 1.9, where they can 205 // be private. 206 if (!modifiers.isPrivate()) { 207 modifiers.setVisibilityLevel(VisibilityLevel.PUBLIC) 208 } 209 } 210 211 if (modifiers.isFinal() && containingClass.modifiers.isFinal()) { 212 // The containing class is final, so it is implied that every method is final as 213 // well. 214 // No need to apply 'final' to each method. (We do it here rather than just in the 215 // signature emit code since we want to make sure that the signature comparison 216 // methods with super methods also consider this method non-final.) 217 modifiers.setFinal(false) 218 } 219 220 // Create the TypeParameterList for this before wrapping any of the other types used by 221 // it as they may reference a type parameter in the list. 222 val (typeParameterList, methodTypeItemFactory) = 223 PsiTypeParameterList.create( 224 codebase, 225 enclosingClassTypeItemFactory, 226 "method $name", 227 psiMethod 228 ) 229 val fingerprint = MethodFingerprint(psiMethod.name, psiMethod.parameters.size) 230 val isAnnotationElement = containingClass.isAnnotationType() && !modifiers.isStatic() 231 val returnType = 232 methodTypeItemFactory.getMethodReturnType( 233 underlyingReturnType = PsiTypeInfo(psiMethod.returnType!!, psiMethod), 234 itemAnnotations = modifiers.annotations(), 235 fingerprint = fingerprint, 236 isAnnotationElement = isAnnotationElement, 237 ) 238 239 val method = 240 PsiMethodItem( 241 codebase = codebase, 242 psiMethod = psiMethod, 243 containingClass = containingClass, 244 name = name, 245 documentationFactory = PsiItemDocumentation.factory(psiMethod, codebase), 246 modifiers = modifiers, 247 returnType = returnType, 248 parameterItemsFactory = { containingCallable -> 249 parameterList( 250 codebase, 251 psiMethod, 252 containingCallable as PsiCallableItem, 253 methodTypeItemFactory, 254 psiParameters, 255 ) 256 }, 257 typeParameterList = typeParameterList, 258 throwsTypes = throwsTypes(psiMethod, methodTypeItemFactory), 259 ) 260 261 return method 262 } 263 } 264 } 265