• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
18 
19 import com.android.tools.metalava.model.text.TextCodebase
20 import com.android.tools.metalava.model.visitors.ItemVisitor
21 import com.android.tools.metalava.model.visitors.TypeVisitor
22 import java.util.function.Predicate
23 
24 interface MethodItem : MemberItem {
25     /**
26      * The property this method is an accessor for; inverse of [PropertyItem.getter] and
27      * [PropertyItem.setter]
28      */
29     val property: PropertyItem?
30         get() = null
31 
32     /** Whether this method is a constructor */
33     fun isConstructor(): Boolean
34 
35     /** The type of this field, or null for constructors */
36     fun returnType(): TypeItem?
37 
38     /** The list of parameters */
39     fun parameters(): List<ParameterItem>
40 
41     /** Returns true if this method is a Kotlin extension method */
42     fun isExtensionMethod(): Boolean
43 
44     /** Returns the super methods that this method is overriding */
45     fun superMethods(): List<MethodItem>
46 
47     override fun type(): TypeItem? = returnType()
48 
49     /** Returns the main documentation for the method (the documentation before any tags). */
50     fun findMainDocumentation(): String
51 
52     /**
53      * Like [internalName] but is the desc-portion of the internal signature,
54      * e.g. for the method "void create(int x, int y)" the internal name of
55      * the constructor is "create" and the desc is "(II)V"
56      */
57     fun internalDesc(voidConstructorTypes: Boolean = false): String {
58         val sb = StringBuilder()
59         sb.append("(")
60 
61         // Non-static inner classes get an implicit constructor parameter for the
62         // outer type
63         if (isConstructor() && containingClass().containingClass() != null &&
64             !containingClass().modifiers.isStatic()
65         ) {
66             sb.append(containingClass().containingClass()?.toType()?.internalName() ?: "")
67         }
68 
69         for (parameter in parameters()) {
70             sb.append(parameter.type().internalName())
71         }
72 
73         sb.append(")")
74         sb.append(if (voidConstructorTypes && isConstructor()) "V" else returnType()?.internalName() ?: "V")
75         return sb.toString()
76     }
77 
78     fun allSuperMethods(): Sequence<MethodItem> {
79         val original = superMethods().firstOrNull() ?: return emptySequence()
80         return generateSequence(original) { item ->
81             val superMethods = item.superMethods()
82             superMethods.firstOrNull()
83         }
84     }
85 
86     /** Any type parameters for the class, if any, as a source string (with fully qualified class names) */
87     fun typeParameterList(): TypeParameterList
88 
89     /** Returns the classes that are part of the type parameters of this method, if any */
90     fun typeArgumentClasses(): List<ClassItem> = codebase.unsupported()
91 
92     /** Types of exceptions that this method can throw */
93     fun throwsTypes(): List<ClassItem>
94 
95     /** Returns true if this class throws the given exception */
96     fun throws(qualifiedName: String): Boolean {
97         for (type in throwsTypes()) {
98             if (type.extends(qualifiedName)) {
99                 return true
100             }
101         }
102 
103         for (type in throwsTypes()) {
104             if (type.qualifiedName() == qualifiedName) {
105                 return true
106             }
107         }
108 
109         return false
110     }
111 
112     fun filteredThrowsTypes(predicate: Predicate<Item>): Collection<ClassItem> {
113         if (throwsTypes().isEmpty()) {
114             return emptyList()
115         }
116         return filteredThrowsTypes(predicate, LinkedHashSet())
117     }
118 
119     private fun filteredThrowsTypes(
120         predicate: Predicate<Item>,
121         classes: LinkedHashSet<ClassItem>
122     ): LinkedHashSet<ClassItem> {
123 
124         for (cls in throwsTypes()) {
125             if (predicate.test(cls) || cls.isTypeParameter) {
126                 classes.add(cls)
127             } else {
128                 // Excluded, but it may have super class throwables that are included; if so, include those
129                 var curr = cls.publicSuperClass()
130                 while (curr != null) {
131                     if (predicate.test(curr)) {
132                         classes.add(curr)
133                         break
134                     }
135                     curr = curr.publicSuperClass()
136                 }
137             }
138         }
139         return classes
140     }
141 
142     /**
143      * If this method is inherited from a hidden super class, but implements a method
144      * from a public interface, this property is set. This is necessary because these
145      * methods should not be listed in signature files (at least not in compatibility mode),
146      * whereas in stub files it's necessary for them to be included (otherwise subclasses
147      * may think the method required and not yet implemented, e.g. the class must be
148      * abstract.)
149      */
150     var inheritedMethod: Boolean
151 
152     /**
153      * If this method is inherited from a super class (typically via [duplicate]) this
154      * field points to the original class it was inherited from
155      */
156     var inheritedFrom: ClassItem?
157 
158     /**
159      * Duplicates this field item. Used when we need to insert inherited fields from
160      * interfaces etc.
161      */
162     fun duplicate(targetContainingClass: ClassItem): MethodItem
163 
164     fun findPredicateSuperMethod(predicate: Predicate<Item>): MethodItem? {
165         if (isConstructor()) {
166             return null
167         }
168 
169         val superMethods = superMethods()
170         for (method in superMethods) {
171             if (predicate.test(method)) {
172                 return method
173             }
174         }
175 
176         for (method in superMethods) {
177             val found = method.findPredicateSuperMethod(predicate)
178             if (found != null) {
179                 return found
180             }
181         }
182 
183         return null
184     }
185 
186     override fun accept(visitor: ItemVisitor) {
187         if (visitor.skip(this)) {
188             return
189         }
190 
191         visitor.visitItem(this)
192         if (isConstructor()) {
193             visitor.visitConstructor(this as ConstructorItem)
194         } else {
195             visitor.visitMethod(this)
196         }
197 
198         for (parameter in parameters()) {
199             parameter.accept(visitor)
200         }
201 
202         if (isConstructor()) {
203             visitor.afterVisitConstructor(this as ConstructorItem)
204         } else {
205             visitor.afterVisitMethod(this)
206         }
207         visitor.afterVisitItem(this)
208     }
209 
210     override fun acceptTypes(visitor: TypeVisitor) {
211         if (visitor.skip(this)) {
212             return
213         }
214 
215         if (!isConstructor()) {
216             val type = returnType()
217             if (type != null) { // always true when not a constructor
218                 visitor.visitType(type, this)
219             }
220         }
221 
222         for (parameter in parameters()) {
223             parameter.acceptTypes(visitor)
224         }
225 
226         for (exception in throwsTypes()) {
227             exception.acceptTypes(visitor)
228         }
229 
230         if (!isConstructor()) {
231             val type = returnType()
232             if (type != null) {
233                 visitor.visitType(type, this)
234             }
235         }
236     }
237 
238     companion object {
239         private fun compareMethods(o1: MethodItem, o2: MethodItem): Int {
240             val name1 = o1.name()
241             val name2 = o2.name()
242             if (name1 == name2) {
243                 val rankDelta = o1.sortingRank - o2.sortingRank
244                 if (rankDelta != 0) {
245                     return rankDelta
246                 }
247 
248                 // Compare by the rest of the signature to ensure stable output (we don't need to sort
249                 // by return value or modifiers or modifiers or throws-lists since methods can't be overloaded
250                 // by just those attributes
251                 val p1 = o1.parameters()
252                 val p2 = o2.parameters()
253                 val p1n = p1.size
254                 val p2n = p2.size
255                 for (i in 0 until minOf(p1n, p2n)) {
256                     val compareTypes =
257                         p1[i].type().toTypeString()
258                             .compareTo(p2[i].type().toTypeString(), ignoreCase = true)
259                     if (compareTypes != 0) {
260                         return compareTypes
261                     }
262                     // (Don't compare names; they're not part of the signatures)
263                 }
264                 return p1n.compareTo(p2n)
265             }
266 
267             return name1.compareTo(name2)
268         }
269 
270         val comparator: Comparator<MethodItem> = Comparator { o1, o2 -> compareMethods(o1, o2) }
271         val sourceOrderComparator: Comparator<MethodItem> = Comparator { o1, o2 ->
272             val delta = o1.sortingRank - o2.sortingRank
273             if (delta == 0) {
274                 // Within a source file all the items will have unique sorting ranks, but since
275                 // we copy methods in from hidden super classes it's possible for ranks to clash,
276                 // and in that case we'll revert to a signature based comparison
277                 comparator.compare(o1, o2)
278             } else {
279                 delta
280             }
281         }
282 
283         fun sameSignature(method: MethodItem, superMethod: MethodItem, compareRawTypes: Boolean = false): Boolean {
284             // If the return types differ, override it (e.g. parent implements clone(),
285             // subclass overrides with more specific return type)
286             if (method.returnType() != superMethod.returnType()) {
287                 return false
288             }
289 
290             if (method.deprecated != superMethod.deprecated && !method.deprecated) {
291                 return false
292             }
293 
294             // Compare modifier lists; note that here we need to
295             // skip modifiers that don't apply in compat mode if set
296             if (!method.modifiers.equivalentTo(superMethod.modifiers)) {
297                 return false
298             }
299 
300             val parameterList1 = method.parameters()
301             val parameterList2 = superMethod.parameters()
302 
303             if (parameterList1.size != parameterList2.size) {
304                 return false
305             }
306 
307             assert(parameterList1.size == parameterList2.size)
308             for (i in parameterList1.indices) {
309                 val p1 = parameterList1[i]
310                 val p2 = parameterList2[i]
311                 val pt1 = p1.type()
312                 val pt2 = p2.type()
313 
314                 if (compareRawTypes) {
315                     if (pt1.toErasedTypeString() != pt2.toErasedTypeString()) {
316                         return false
317                     }
318                 } else {
319                     if (pt1 != pt2) {
320                         return false
321                     }
322                 }
323 
324                 // TODO: Compare annotations to see for example whether
325                 // you've refined the nullness policy; if so, that should be included
326             }
327 
328             // Also compare throws lists
329             val throwsList12 = method.throwsTypes()
330             val throwsList2 = superMethod.throwsTypes()
331 
332             if (throwsList12.size != throwsList2.size) {
333                 return false
334             }
335 
336             assert(throwsList12.size == throwsList2.size)
337             for (i in throwsList12.indices) {
338                 val p1 = throwsList12[i]
339                 val p2 = throwsList2[i]
340                 val pt1 = p1.qualifiedName()
341                 val pt2 = p2.qualifiedName()
342                 if (pt1 != pt2) { // assumes throws lists are sorted!
343                     return false
344                 }
345             }
346 
347             return true
348         }
349     }
350 
351     fun formatParameters(): String? {
352         // TODO: Generalize, allow callers to control whether to include annotations, whether to erase types,
353         // whether to include names, etc
354         if (parameters().isEmpty()) {
355             return ""
356         }
357         val sb = StringBuilder()
358         for (parameter in parameters()) {
359             if (sb.isNotEmpty()) {
360                 sb.append(", ")
361             }
362             sb.append(parameter.type().toTypeString())
363         }
364 
365         return sb.toString()
366     }
367 
368     override fun requiresNullnessInfo(): Boolean {
369         return when {
370             modifiers.hasJvmSyntheticAnnotation() -> false
371             isConstructor() -> false
372             (returnType()?.primitive != true) -> true
373             parameters().any { !it.type().primitive } -> true
374             else -> false
375         }
376     }
377 
378     override fun hasNullnessInfo(): Boolean {
379         if (!requiresNullnessInfo()) {
380             return true
381         }
382 
383         if (!isConstructor() && returnType()?.primitive != true) {
384             if (!modifiers.hasNullnessInfo()) {
385                 return false
386             }
387         }
388 
389         @Suppress("LoopToCallChain") // The quickfix is wrong! (covered by AnnotationStatisticsTest)
390         for (parameter in parameters()) {
391             if (!parameter.hasNullnessInfo()) {
392                 return false
393             }
394         }
395 
396         return true
397     }
398 
399     fun isImplicitConstructor(): Boolean {
400         return isConstructor() && modifiers.isPublic() && parameters().isEmpty()
401     }
402 
403     /** Finds uncaught exceptions actually thrown inside this method (as opposed to ones
404      * declared in the signature) */
405     fun findThrownExceptions(): Set<ClassItem> = codebase.unsupported()
406 
407     /** If annotation method, returns the default value as a source expression */
408     fun defaultValue(): String = ""
409 
410     fun hasDefaultValue(): Boolean {
411         return defaultValue() != ""
412     }
413 
414     /**
415      * Returns true if this method is a signature match for the given method (e.g. can
416      * be overriding). This checks that the name and parameter lists match, but ignores
417      * differences in parameter names, return value types and throws list types.
418      */
419     fun matches(other: MethodItem): Boolean {
420         if (this === other) return true
421 
422         if (name() != other.name()) {
423             return false
424         }
425 
426         val parameters1 = parameters()
427         val parameters2 = other.parameters()
428 
429         if (parameters1.size != parameters2.size) {
430             return false
431         }
432 
433         for (i in parameters1.indices) {
434             val parameter1 = parameters1[i]
435             val parameter2 = parameters2[i]
436             val typeString1 = parameter1.type().toString()
437             val typeString2 = parameter2.type().toString()
438             if (typeString1 == typeString2) {
439                 continue
440             }
441             val type1 = parameter1.type().toErasedTypeString(this)
442             val type2 = parameter2.type().toErasedTypeString(other)
443 
444             if (type1 != type2) {
445                 // Workaround for signature-based codebase, where we can't always resolve generic
446                 // parameters: if we see a mismatch here which looks like a failure to erase say T into
447                 // java.lang.Object, don't treat that as a mismatch. (Similar common case: T[] and Object[])
448                 if (typeString1[0].isUpperCase() && typeString1.length == 1 &&
449                     parameter1.codebase is TextCodebase
450                 ) {
451                     continue
452                 }
453                 if (typeString2.length >= 2 && !typeString2[1].isLetterOrDigit() &&
454                     parameter1.codebase is TextCodebase
455                 ) {
456                     continue
457                 }
458                 return false
459             }
460         }
461         return true
462     }
463 
464     /** Returns whether this method has any types in its signature that does not match the given filter */
465     fun hasHiddenType(filterReference: Predicate<Item>): Boolean {
466         for (parameter in parameters()) {
467             val type = parameter.type()
468             if (type.hasTypeArguments()) {
469                 for (argument in type.typeArgumentClasses()) {
470                     if (!filterReference.test(argument)) {
471                         return true
472                     }
473                 }
474             }
475             val clz = type.asClass() ?: continue
476             if (!filterReference.test(clz)) {
477                 return true
478             }
479         }
480 
481         val returnType = returnType()
482         if (returnType != null) {
483             val returnTypeClass = returnType.asClass()
484             if (returnTypeClass != null && !filterReference.test(returnTypeClass)) {
485                 return true
486             }
487             if (returnType.hasTypeArguments()) {
488                 for (argument in returnType.typeArgumentClasses()) {
489                     if (!filterReference.test(argument)) {
490                         return true
491                     }
492                 }
493             }
494         }
495 
496         if (typeParameterList().typeParameterCount() > 0) {
497             for (argument in typeArgumentClasses()) {
498                 if (!filterReference.test(argument)) {
499                     return true
500                 }
501             }
502         }
503 
504         return false
505     }
506 
507     override fun hasShowAnnotationInherited(): Boolean {
508         if (super.hasShowAnnotationInherited()) {
509             return true
510         }
511         return superMethods().any {
512             it.hasShowAnnotationInherited()
513         }
514     }
515 
516     override fun onlyShowForStubPurposesInherited(): Boolean {
517         if (super.onlyShowForStubPurposesInherited()) {
518             return true
519         }
520         return superMethods().any {
521             it.onlyShowForStubPurposesInherited()
522         }
523     }
524 
525     /** Whether this method is a getter/setter for an underlying Kotlin property (val/var) */
526     fun isKotlinProperty(): Boolean = false
527 
528     /** Returns true if this is a synthetic enum method */
529     fun isEnumSyntheticMethod(): Boolean {
530         return containingClass().isEnum() &&
531             (
532                 name() == "values" && parameters().isEmpty() ||
533                     name() == "valueOf" && parameters().size == 1 &&
534                     parameters()[0].type().isString()
535                 )
536     }
537 }
538