• 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. Returns the containing class 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())
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             visitor.visitType(type, this)
218         }
219 
220         for (parameter in parameters()) {
221             parameter.acceptTypes(visitor)
222         }
223 
224         for (exception in throwsTypes()) {
225             exception.acceptTypes(visitor)
226         }
227 
228         if (!isConstructor()) {
229             val type = returnType()
230             visitor.visitType(type, this)
231         }
232     }
233 
234     companion object {
235         private fun compareMethods(o1: MethodItem, o2: MethodItem): Int {
236             val name1 = o1.name()
237             val name2 = o2.name()
238             if (name1 == name2) {
239                 val rankDelta = o1.sortingRank - o2.sortingRank
240                 if (rankDelta != 0) {
241                     return rankDelta
242                 }
243 
244                 // Compare by the rest of the signature to ensure stable output (we don't need to sort
245                 // by return value or modifiers or modifiers or throws-lists since methods can't be overloaded
246                 // by just those attributes
247                 val p1 = o1.parameters()
248                 val p2 = o2.parameters()
249                 val p1n = p1.size
250                 val p2n = p2.size
251                 for (i in 0 until minOf(p1n, p2n)) {
252                     val compareTypes =
253                         p1[i].type().toTypeString()
254                             .compareTo(p2[i].type().toTypeString(), ignoreCase = true)
255                     if (compareTypes != 0) {
256                         return compareTypes
257                     }
258                     // (Don't compare names; they're not part of the signatures)
259                 }
260                 return p1n.compareTo(p2n)
261             }
262 
263             return name1.compareTo(name2)
264         }
265 
266         val comparator: Comparator<MethodItem> = Comparator { o1, o2 -> compareMethods(o1, o2) }
267         val sourceOrderComparator: Comparator<MethodItem> = Comparator { o1, o2 ->
268             val delta = o1.sortingRank - o2.sortingRank
269             if (delta == 0) {
270                 // Within a source file all the items will have unique sorting ranks, but since
271                 // we copy methods in from hidden super classes it's possible for ranks to clash,
272                 // and in that case we'll revert to a signature based comparison
273                 comparator.compare(o1, o2)
274             } else {
275                 delta
276             }
277         }
278 
279         fun sameSignature(method: MethodItem, superMethod: MethodItem, compareRawTypes: Boolean = false): Boolean {
280             // If the return types differ, override it (e.g. parent implements clone(),
281             // subclass overrides with more specific return type)
282             if (method.returnType() != superMethod.returnType()) {
283                 return false
284             }
285 
286             if (method.deprecated != superMethod.deprecated && !method.deprecated) {
287                 return false
288             }
289 
290             // Compare modifier lists; note that here we need to
291             // skip modifiers that don't apply in compat mode if set
292             if (!method.modifiers.equivalentTo(superMethod.modifiers)) {
293                 return false
294             }
295 
296             val parameterList1 = method.parameters()
297             val parameterList2 = superMethod.parameters()
298 
299             if (parameterList1.size != parameterList2.size) {
300                 return false
301             }
302 
303             assert(parameterList1.size == parameterList2.size)
304             for (i in parameterList1.indices) {
305                 val p1 = parameterList1[i]
306                 val p2 = parameterList2[i]
307                 val pt1 = p1.type()
308                 val pt2 = p2.type()
309 
310                 if (compareRawTypes) {
311                     if (pt1.toErasedTypeString() != pt2.toErasedTypeString()) {
312                         return false
313                     }
314                 } else {
315                     if (pt1 != pt2) {
316                         return false
317                     }
318                 }
319 
320                 // TODO: Compare annotations to see for example whether
321                 // you've refined the nullness policy; if so, that should be included
322             }
323 
324             // Also compare throws lists
325             val throwsList12 = method.throwsTypes()
326             val throwsList2 = superMethod.throwsTypes()
327 
328             if (throwsList12.size != throwsList2.size) {
329                 return false
330             }
331 
332             assert(throwsList12.size == throwsList2.size)
333             for (i in throwsList12.indices) {
334                 val p1 = throwsList12[i]
335                 val p2 = throwsList2[i]
336                 val pt1 = p1.qualifiedName()
337                 val pt2 = p2.qualifiedName()
338                 if (pt1 != pt2) { // assumes throws lists are sorted!
339                     return false
340                 }
341             }
342 
343             return true
344         }
345     }
346 
347     fun formatParameters(): String? {
348         // TODO: Generalize, allow callers to control whether to include annotations, whether to erase types,
349         // whether to include names, etc
350         if (parameters().isEmpty()) {
351             return ""
352         }
353         val sb = StringBuilder()
354         for (parameter in parameters()) {
355             if (sb.isNotEmpty()) {
356                 sb.append(", ")
357             }
358             sb.append(parameter.type().toTypeString())
359         }
360 
361         return sb.toString()
362     }
363 
364     override fun requiresNullnessInfo(): Boolean {
365         return when {
366             modifiers.hasJvmSyntheticAnnotation() -> false
367             isConstructor() -> false
368             (!returnType().primitive) -> true
369             parameters().any { !it.type().primitive } -> true
370             else -> false
371         }
372     }
373 
374     override fun hasNullnessInfo(): Boolean {
375         if (!requiresNullnessInfo()) {
376             return true
377         }
378 
379         if (!isConstructor() && !returnType().primitive) {
380             if (!modifiers.hasNullnessInfo()) {
381                 return false
382             }
383         }
384 
385         @Suppress("LoopToCallChain") // The quickfix is wrong! (covered by AnnotationStatisticsTest)
386         for (parameter in parameters()) {
387             if (!parameter.hasNullnessInfo()) {
388                 return false
389             }
390         }
391 
392         return true
393     }
394 
395     fun isImplicitConstructor(): Boolean {
396         return isConstructor() && modifiers.isPublic() && parameters().isEmpty()
397     }
398 
399     /** Finds uncaught exceptions actually thrown inside this method (as opposed to ones
400      * declared in the signature) */
401     fun findThrownExceptions(): Set<ClassItem> = codebase.unsupported()
402 
403     /** If annotation method, returns the default value as a source expression */
404     fun defaultValue(): String = ""
405 
406     fun hasDefaultValue(): Boolean {
407         return defaultValue() != ""
408     }
409 
410     /**
411      * Returns true if this method is a signature match for the given method (e.g. can
412      * be overriding). This checks that the name and parameter lists match, but ignores
413      * differences in parameter names, return value types and throws list types.
414      */
415     fun matches(other: MethodItem): Boolean {
416         if (this === other) return true
417 
418         if (name() != other.name()) {
419             return false
420         }
421 
422         val parameters1 = parameters()
423         val parameters2 = other.parameters()
424 
425         if (parameters1.size != parameters2.size) {
426             return false
427         }
428 
429         for (i in parameters1.indices) {
430             val parameter1 = parameters1[i]
431             val parameter2 = parameters2[i]
432             val typeString1 = parameter1.type().toString()
433             val typeString2 = parameter2.type().toString()
434             if (typeString1 == typeString2) {
435                 continue
436             }
437             val type1 = parameter1.type().toErasedTypeString(this)
438             val type2 = parameter2.type().toErasedTypeString(other)
439 
440             if (type1 != type2) {
441                 // Workaround for signature-based codebase, where we can't always resolve generic
442                 // parameters: if we see a mismatch here which looks like a failure to erase say T into
443                 // java.lang.Object, don't treat that as a mismatch. (Similar common case: T[] and Object[])
444                 if (typeString1[0].isUpperCase() && typeString1.length == 1 &&
445                     parameter1.codebase is TextCodebase
446                 ) {
447                     continue
448                 }
449                 if (typeString2.length >= 2 && !typeString2[1].isLetterOrDigit() &&
450                     parameter1.codebase is TextCodebase
451                 ) {
452                     continue
453                 }
454                 return false
455             }
456         }
457         return true
458     }
459 
460     /** Returns whether this method has any types in its signature that does not match the given filter */
461     fun hasHiddenType(filterReference: Predicate<Item>): Boolean {
462         for (parameter in parameters()) {
463             val type = parameter.type()
464             if (type.hasTypeArguments()) {
465                 for (argument in type.typeArgumentClasses()) {
466                     if (!filterReference.test(argument)) {
467                         return true
468                     }
469                 }
470             }
471             val clz = type.asClass() ?: continue
472             if (!filterReference.test(clz)) {
473                 return true
474             }
475         }
476 
477         val returnType = returnType()
478         val returnTypeClass = returnType.asClass()
479         if (returnTypeClass != null && !filterReference.test(returnTypeClass)) {
480             return true
481         }
482         if (returnType.hasTypeArguments()) {
483             for (argument in returnType.typeArgumentClasses()) {
484                 if (!filterReference.test(argument)) {
485                     return true
486                 }
487             }
488         }
489 
490         if (typeParameterList().typeParameterCount() > 0) {
491             for (argument in typeArgumentClasses()) {
492                 if (!filterReference.test(argument)) {
493                     return true
494                 }
495             }
496         }
497 
498         return false
499     }
500 
501     override fun hasShowAnnotationInherited(): Boolean {
502         if (super.hasShowAnnotationInherited()) {
503             return true
504         }
505         return superMethods().any {
506             it.hasShowAnnotationInherited()
507         }
508     }
509 
510     override fun onlyShowForStubPurposesInherited(): Boolean {
511         if (super.onlyShowForStubPurposesInherited()) {
512             return true
513         }
514         return superMethods().any {
515             it.onlyShowForStubPurposesInherited()
516         }
517     }
518 
519     /** Whether this method is a getter/setter for an underlying Kotlin property (val/var) */
520     fun isKotlinProperty(): Boolean = false
521 
522     /** Returns true if this is a synthetic enum method */
523     fun isEnumSyntheticMethod(): Boolean {
524         return containingClass().isEnum() &&
525             (
526                 name() == "values" && parameters().isEmpty() ||
527                     name() == "valueOf" && parameters().size == 1 &&
528                     parameters()[0].type().isString()
529                 )
530     }
531 }
532