xref: /aosp_15_r20/tools/metalava/metalava-model/src/main/java/com/android/tools/metalava/model/item/DefaultMethodItem.kt (revision 115816f9299ab6ddd6b9673b81f34e707f6bacab)
1 /*
<lambda>null2  * Copyright (C) 2023 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.item
18 
19 import com.android.tools.metalava.model.ApiVariantSelectorsFactory
20 import com.android.tools.metalava.model.BaseModifierList
21 import com.android.tools.metalava.model.CallableBodyFactory
22 import com.android.tools.metalava.model.ClassItem
23 import com.android.tools.metalava.model.Codebase
24 import com.android.tools.metalava.model.ExceptionTypeItem
25 import com.android.tools.metalava.model.ItemDocumentationFactory
26 import com.android.tools.metalava.model.ItemLanguage
27 import com.android.tools.metalava.model.MethodItem
28 import com.android.tools.metalava.model.TypeItem
29 import com.android.tools.metalava.model.TypeParameterList
30 import com.android.tools.metalava.reporter.FileLocation
31 
32 open class DefaultMethodItem(
33     codebase: Codebase,
34     fileLocation: FileLocation,
35     itemLanguage: ItemLanguage,
36     modifiers: BaseModifierList,
37     documentationFactory: ItemDocumentationFactory,
38     variantSelectorsFactory: ApiVariantSelectorsFactory,
39     name: String,
40     containingClass: ClassItem,
41     typeParameterList: TypeParameterList,
42     returnType: TypeItem,
43     parameterItemsFactory: ParameterItemsFactory,
44     throwsTypes: List<ExceptionTypeItem>,
45     callableBodyFactory: CallableBodyFactory,
46     private val annotationDefault: String = "",
47 ) :
48     DefaultCallableItem(
49         codebase,
50         fileLocation,
51         itemLanguage,
52         modifiers,
53         documentationFactory,
54         variantSelectorsFactory,
55         name,
56         containingClass,
57         typeParameterList,
58         returnType,
59         parameterItemsFactory,
60         throwsTypes,
61         callableBodyFactory,
62     ),
63     MethodItem {
64 
65     final override var inheritedFrom: ClassItem? = null
66 
67     override fun isExtensionMethod(): Boolean = false // java does not support extension methods
68 
69     override fun defaultValue() = annotationDefault
70 
71     private lateinit var superMethodList: List<MethodItem>
72 
73     /**
74      * Super methods for a given method M with containing class C are calculated as follows:
75      * 1) Superclass Search: Traverse the class hierarchy, starting from C's direct superclass, and
76      *    add the first method that matches M's signature to the list.
77      * 2) Interface Supermethod Search: For each direct interface implemented by C, check if it
78      *    contains a method matching M's signature. If found, return that method. If not,
79      *    recursively apply this method to the direct interfaces of the current interface.
80      *
81      * Note: This method's implementation is based on MethodItem.matches method which only checks
82      * that name and parameter list types match. Parameter names, Return types and Throws list types
83      * are not matched
84      */
85     final override fun superMethods(): List<MethodItem> {
86         return if (containingClass().frozen) {
87             if (!::superMethodList.isInitialized) {
88                 superMethodList = computeSuperMethods()
89             }
90             superMethodList
91         } else {
92             computeSuperMethods()
93         }
94     }
95 
96     @Deprecated("This property should not be accessed directly.")
97     final override var _requiresOverride: Boolean? = null
98 
99     override fun duplicate(targetContainingClass: ClassItem): MethodItem {
100         val typeVariableMap = targetContainingClass.mapTypeVariables(containingClass())
101 
102         return DefaultMethodItem(
103                 codebase = codebase,
104                 fileLocation = fileLocation,
105                 itemLanguage = itemLanguage,
106                 modifiers = modifiers,
107                 documentationFactory = documentation::duplicate,
108                 variantSelectorsFactory = variantSelectors::duplicate,
109                 name = name(),
110                 containingClass = targetContainingClass,
111                 typeParameterList = typeParameterList,
112                 returnType = returnType.convertType(typeVariableMap),
113                 parameterItemsFactory = { containingCallable ->
114                     // Duplicate the parameters
115                     parameters.map { it.duplicate(containingCallable, typeVariableMap) }
116                 },
117                 throwsTypes = throwsTypes,
118                 annotationDefault = annotationDefault,
119                 callableBodyFactory = body::duplicate,
120             )
121             .also { duplicated ->
122                 duplicated.inheritedFrom = containingClass()
123 
124                 duplicated.updateCopiedMethodState()
125             }
126     }
127 
128     /**
129      * Compute the super methods of this method.
130      *
131      * A super method is a method from a super class or super interface that is directly overridden
132      * by this method.
133      */
134     private fun computeSuperMethods(): List<MethodItem> {
135         // Methods that are not overrideable will have no super methods.
136         if (!isOverrideable()) {
137             return emptyList()
138         }
139 
140         // TODO(b/321216636): Remove this awful hack.
141         // For some reason `psiMethod.findSuperMethods()` would return an empty list for this
142         // specific method. That is incorrect as it clearly overrides a method in `DrawScope` in the
143         // same package. However, it is unclear what makes this method distinct from any other
144         // method including overloaded methods in the same class that also override methods
145         // in`DrawScope`. Returning a correct non-empty list for that method results in the method
146         // being removed from an API signature file even though the super method is abstract and
147         // this is concrete. That is because AndroidX does not yet set
148         // `add-additional-overrides=yes`. When it does then this hack can be removed.
149         if (
150             containingClass().qualifiedName() ==
151                 "androidx.compose.ui.graphics.drawscope.CanvasDrawScope" &&
152                 name() == "drawImage" &&
153                 toString() ==
154                     "method androidx.compose.ui.graphics.drawscope.CanvasDrawScope.drawImage(androidx.compose.ui.graphics.ImageBitmap, long, long, long, long, float, androidx.compose.ui.graphics.drawscope.DrawStyle, androidx.compose.ui.graphics.ColorFilter, int)"
155         ) {
156             return emptyList()
157         }
158 
159         // Ideally, the search for super methods would start from this method's ClassItem.
160         // Unfortunately, due to legacy reasons for methods that were inherited from another
161         // ClassItem it is necessary to start the search from the original ClassItem. That is
162         // because the psi model's implementation behaved this way and the code that is built of top
163         // of superMethods, like the code to determine if overriding methods should be elided from
164         // the API signature file relied on that behavior.
165         val startingClass = inheritedFrom ?: containingClass()
166         return buildSet { appendSuperMethods(this, startingClass) }.toList()
167     }
168 
169     /**
170      * Append the super methods of this method from the [cls] hierarchy to the [methods] set.
171      *
172      * @param methods the mutable, order preserving set of super [MethodItem].
173      * @param cls the [ClassItem] whose super class and implemented interfaces will be searched for
174      *   matching methods.
175      */
176     private fun appendSuperMethods(methods: MutableSet<MethodItem>, cls: ClassItem) {
177         // Method from SuperClass or its ancestors
178         cls.superClass()?.let { superClass ->
179             // Search for a matching method in the super class.
180             val superMethod = superClass.findMethod(this)
181             if (superMethod == null) {
182                 // No matching method was found so continue searching in the super class.
183                 appendSuperMethods(methods, superClass)
184             } else {
185                 // Matching does not check modifiers match so make sure that the matched method is
186                 // overrideable.
187                 if (superMethod.isOverrideable()) {
188                     methods.add(superMethod)
189                 }
190             }
191         }
192 
193         // Methods implemented from direct interfaces or its ancestors
194         appendSuperMethodsFromInterfaces(methods, cls)
195     }
196 
197     /**
198      * Append the super methods of this method from the interface hierarchy of [cls] to the
199      * [methods] set.
200      *
201      * @param methods the mutable, order preserving set of super [MethodItem].
202      * @param cls the [ClassItem] whose implemented interfaces will be searched for matching
203      *   methods.
204      */
205     private fun appendSuperMethodsFromInterfaces(methods: MutableSet<MethodItem>, cls: ClassItem) {
206         for (itf in cls.interfaceTypes()) {
207             val itfClass = itf.asClass() ?: continue
208 
209             // Find the method in the interface.
210             itfClass.findMethod(this)?.let { superMethod ->
211                 // A matching method was found so add it to the super methods if it is overrideable.
212                 if (superMethod.isOverrideable()) {
213                     methods.add(superMethod)
214                 }
215             }
216             // A method could not be found in this interface so search its interfaces.
217             ?: appendSuperMethodsFromInterfaces(methods, itfClass)
218         }
219     }
220 
221     /**
222      * Update the state of a [MethodItem] that has been copied from one [ClassItem] to another.
223      *
224      * This will update the [MethodItem] on which it is called to ensure that it is consistent with
225      * the [ClassItem] to which it now belongs. Called from the implementations of
226      * [MethodItem.duplicate].
227      */
228     protected fun updateCopiedMethodState() {
229         if (modifiers.isDefault() && !containingClass().isInterface()) {
230             mutateModifiers { setDefault(false) }
231         }
232     }
233 }
234 
235 /**
236  * Check to see if the method is overrideable.
237  *
238  * Private and static methods cannot be overridden.
239  */
MethodItemnull240 private fun MethodItem.isOverrideable(): Boolean = !modifiers.isPrivate() && !modifiers.isStatic()
241