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.AnnotationItem
20 import com.android.tools.metalava.model.AnnotationRetention
21 import com.android.tools.metalava.model.ClassItem
22 import com.android.tools.metalava.model.ClassKind
23 import com.android.tools.metalava.model.ClassTypeItem
24 import com.android.tools.metalava.model.ConstructorItem
25 import com.android.tools.metalava.model.DefaultModifierList
26 import com.android.tools.metalava.model.FieldItem
27 import com.android.tools.metalava.model.MethodItem
28 import com.android.tools.metalava.model.PackageItem
29 import com.android.tools.metalava.model.PropertyItem
30 import com.android.tools.metalava.model.SourceFile
31 import com.android.tools.metalava.model.TypeParameterList
32 import com.android.tools.metalava.model.VisibilityLevel
33 import com.android.tools.metalava.model.hasAnnotation
34 import com.android.tools.metalava.model.isRetention
35 import com.android.tools.metalava.model.updateCopiedMethodState
36 import com.intellij.psi.PsiClass
37 import com.intellij.psi.PsiClassType
38 import com.intellij.psi.PsiCompiledFile
39 import com.intellij.psi.PsiMethod
40 import com.intellij.psi.PsiModifier
41 import com.intellij.psi.PsiType
42 import com.intellij.psi.PsiTypeParameter
43 import com.intellij.psi.util.PsiUtil
44 import org.jetbrains.kotlin.psi.KtParameter
45 import org.jetbrains.kotlin.psi.KtProperty
46 import org.jetbrains.kotlin.psi.KtPropertyAccessor
47 import org.jetbrains.kotlin.psi.KtTypeReference
48 import org.jetbrains.kotlin.psi.psiUtil.isPropertyParameter
49 import org.jetbrains.uast.UClass
50 import org.jetbrains.uast.UFile
51 import org.jetbrains.uast.getParentOfType
52 
53 open class PsiClassItem
54 internal constructor(
55     codebase: PsiBasedCodebase,
56     val psiClass: PsiClass,
57     private val name: String,
58     private val fullName: String,
59     private val qualifiedName: String,
60     private val hasImplicitDefaultConstructor: Boolean,
61     override val classKind: ClassKind,
62     override val typeParameterList: TypeParameterList,
63     private val superClassType: ClassTypeItem?,
64     private var interfaceTypes: List<ClassTypeItem>,
65     modifiers: DefaultModifierList,
66     documentation: String,
67     /** True if this class is from the class path (dependencies). Exposed in [isFromClassPath]. */
68     private val fromClassPath: Boolean
69 ) :
70     PsiItem(
71         codebase = codebase,
72         modifiers = modifiers,
73         documentation = documentation,
74         element = psiClass
75     ),
76     ClassItem {
77 
78     init {
79         emit = !modifiers.isExpect()
80     }
81 
82     lateinit var containingPackage: PsiPackageItem
83 
84     override fun containingPackage(): PackageItem =
85         containingClass?.containingPackage() ?: containingPackage
86 
87     override fun simpleName(): String = name
88 
89     override fun fullName(): String = fullName
90 
91     override fun qualifiedName(): String = qualifiedName
92 
93     override fun psi() = psiClass
94 
95     override fun isFromClassPath(): Boolean = fromClassPath
96 
97     override fun hasImplicitDefaultConstructor(): Boolean = hasImplicitDefaultConstructor
98 
99     override fun superClass(): ClassItem? = superClassType?.asClass()
100 
101     override fun superClassType(): ClassTypeItem? = superClassType
102 
103     override var stubConstructor: ConstructorItem? = null
104     override var artifact: String? = null
105 
106     private var containingClass: PsiClassItem? = null
107 
108     override fun containingClass(): PsiClassItem? = containingClass
109 
110     override var hasPrivateConstructor: Boolean = false
111 
112     override fun interfaceTypes(): List<ClassTypeItem> = interfaceTypes
113 
114     override fun setInterfaceTypes(interfaceTypes: List<ClassTypeItem>) {
115         this.interfaceTypes = interfaceTypes
116     }
117 
118     private var allInterfaces: List<ClassItem>? = null
119 
120     override fun allInterfaces(): Sequence<ClassItem> {
121         if (allInterfaces == null) {
122             val classes = mutableSetOf<PsiClass>()
123             var curr: PsiClass? = psiClass
124             while (curr != null) {
125                 if (curr.isInterface && !classes.contains(curr)) {
126                     classes.add(curr)
127                 }
128                 addInterfaces(classes, curr.interfaces)
129                 curr = curr.superClass
130             }
131             val result = mutableListOf<ClassItem>()
132             for (cls in classes) {
133                 val item = codebase.findOrCreateClass(cls)
134                 result.add(item)
135             }
136 
137             allInterfaces = result
138         }
139 
140         return allInterfaces!!.asSequence()
141     }
142 
143     private fun addInterfaces(result: MutableSet<PsiClass>, interfaces: Array<out PsiClass>) {
144         for (itf in interfaces) {
145             if (itf.isInterface && !result.contains(itf)) {
146                 result.add(itf)
147                 addInterfaces(result, itf.interfaces)
148                 val superClass = itf.superClass
149                 if (superClass != null) {
150                     addInterfaces(result, arrayOf(superClass))
151                 }
152             }
153         }
154     }
155 
156     private lateinit var innerClasses: List<PsiClassItem>
157     private lateinit var constructors: List<PsiConstructorItem>
158     private lateinit var methods: MutableList<PsiMethodItem>
159     private lateinit var properties: List<PsiPropertyItem>
160     private lateinit var fields: List<FieldItem>
161 
162     /**
163      * If this item was created by filtering down a different codebase, this temporarily points to
164      * the original item during construction. This is used to let us initialize for example throws
165      * lists later, when all classes in the codebase have been initialized.
166      */
167     internal var source: PsiClassItem? = null
168 
169     override fun innerClasses(): List<PsiClassItem> = innerClasses
170 
171     override fun constructors(): List<ConstructorItem> = constructors
172 
173     override fun methods(): List<PsiMethodItem> = methods
174 
175     override fun properties(): List<PropertyItem> = properties
176 
177     override fun fields(): List<FieldItem> = fields
178 
179     final override var primaryConstructor: PsiConstructorItem? = null
180         private set
181 
182     /** Must only be used by [type] to cache its result. */
183     private lateinit var classTypeItem: PsiClassTypeItem
184 
185     override fun type(): ClassTypeItem {
186         if (!::classTypeItem.isInitialized) {
187             classTypeItem = codebase.globalTypeItemFactory.getClassTypeForClass(this)
188         }
189         return classTypeItem
190     }
191 
192     override fun hasTypeVariables(): Boolean = psiClass.hasTypeParameters()
193 
194     override fun getSourceFile(): SourceFile? {
195         if (isInnerClass()) {
196             return null
197         }
198 
199         val containingFile = psiClass.containingFile ?: return null
200         if (containingFile is PsiCompiledFile) {
201             return null
202         }
203 
204         val uFile =
205             if (psiClass is UClass) {
206                 psiClass.getParentOfType(UFile::class.java)
207             } else {
208                 null
209             }
210 
211         return PsiSourceFile(codebase, containingFile, uFile)
212     }
213 
214     override fun equals(other: Any?): Boolean {
215         if (this === other) {
216             return true
217         }
218         return other is ClassItem && qualifiedName == other.qualifiedName()
219     }
220 
221     /** Creates a constructor in this class */
222     override fun createDefaultConstructor(): ConstructorItem {
223         return PsiConstructorItem.createDefaultConstructor(codebase, this, psiClass)
224     }
225 
226     override fun inheritMethodFromNonApiAncestor(template: MethodItem): MethodItem {
227         val method = template as PsiMethodItem
228         require(method.codebase == codebase) {
229             "Unexpected attempt to copy $method from one codebase (${method.codebase.location}) to another (${codebase.location})"
230         }
231         val newMethod = PsiMethodItem.create(this, method)
232 
233         // Remember which class this method was copied from.
234         newMethod.inheritedFrom = template.containingClass()
235 
236         newMethod.updateCopiedMethodState()
237 
238         return newMethod
239     }
240 
241     override fun addMethod(method: MethodItem) {
242         methods.add(method as PsiMethodItem)
243     }
244 
245     private var retention: AnnotationRetention? = null
246 
247     override fun getRetention(): AnnotationRetention {
248         retention?.let {
249             return it
250         }
251 
252         if (!isAnnotationType()) {
253             error("getRetention() should only be called on annotation classes")
254         }
255 
256         retention = ClassItem.findRetention(this)
257         return retention!!
258     }
259 
260     override fun hashCode(): Int = qualifiedName.hashCode()
261 
262     companion object {
263         private fun hasExplicitRetention(
264             modifiers: DefaultModifierList,
265             psiClass: PsiClass,
266             isKotlin: Boolean
267         ): Boolean {
268             if (modifiers.hasAnnotation(AnnotationItem::isRetention)) {
269                 return true
270             }
271             if (isKotlin && psiClass is UClass) {
272                 // In Kotlin some annotations show up on the Java facade only; for example,
273                 // a @DslMarker annotation will imply a runtime annotation which is present
274                 // in the java facade, not in the source list of annotations
275                 val modifierList = psiClass.modifierList
276                 if (
277                     modifierList != null &&
278                         modifierList.annotations.any { isRetention(it.qualifiedName) }
279                 ) {
280                     return true
281                 }
282             }
283             return false
284         }
285 
286         internal fun create(
287             codebase: PsiBasedCodebase,
288             psiClass: PsiClass,
289             containingClassItem: PsiClassItem?,
290             enclosingClassTypeItemFactory: PsiTypeItemFactory,
291             fromClassPath: Boolean,
292         ): PsiClassItem {
293             if (psiClass is PsiTypeParameter) {
294                 error(
295                     "Must not be called with PsiTypeParameter; use PsiTypeParameterItem.create(...) instead"
296                 )
297             }
298             val simpleName = psiClass.name!!
299             val fullName = computeFullClassName(psiClass)
300             val qualifiedName = psiClass.qualifiedName ?: simpleName
301             val hasImplicitDefaultConstructor = hasImplicitDefaultConstructor(psiClass)
302             val classKind = getClassKind(psiClass)
303 
304             val commentText = javadoc(psiClass, codebase.allowReadingComments)
305             val modifiers = PsiModifierItem.create(codebase, psiClass, commentText)
306 
307             // Create the TypeParameterList for this before wrapping any of the other types used by
308             // it as they may reference a type parameter in the list.
309             val (typeParameterList, classTypeItemFactory) =
310                 PsiTypeParameterList.create(
311                     codebase,
312                     enclosingClassTypeItemFactory,
313                     "class $qualifiedName",
314                     psiClass
315                 )
316 
317             val (superClassType, interfaceTypes) =
318                 computeSuperTypes(psiClass, classKind, classTypeItemFactory)
319 
320             val item =
321                 PsiClassItem(
322                     codebase = codebase,
323                     psiClass = psiClass,
324                     name = simpleName,
325                     fullName = fullName,
326                     qualifiedName = qualifiedName,
327                     classKind = classKind,
328                     typeParameterList = typeParameterList,
329                     superClassType = superClassType,
330                     interfaceTypes = interfaceTypes,
331                     hasImplicitDefaultConstructor = hasImplicitDefaultConstructor,
332                     documentation = commentText,
333                     modifiers = modifiers,
334                     fromClassPath = fromClassPath,
335                 )
336             item.containingClass = containingClassItem
337 
338             // Register this class now.
339             codebase.registerClass(item)
340 
341             // Construct the children
342             val psiMethods = psiClass.methods
343             val methods: MutableList<PsiMethodItem> = ArrayList(psiMethods.size)
344             val isKotlin = psiClass.isKotlin()
345 
346             if (
347                 classKind == ClassKind.ANNOTATION_TYPE &&
348                     !hasExplicitRetention(modifiers, psiClass, isKotlin)
349             ) {
350                 // By policy, include explicit retention policy annotation if missing
351                 val defaultRetentionPolicy = AnnotationRetention.getDefault(isKotlin)
352                 modifiers.addAnnotation(
353                     codebase.createAnnotation(
354                         buildString {
355                             append('@')
356                             append(java.lang.annotation.Retention::class.qualifiedName)
357                             append('(')
358                             append(java.lang.annotation.RetentionPolicy::class.qualifiedName)
359                             append('.')
360                             append(defaultRetentionPolicy.name)
361                             append(')')
362                         },
363                         item,
364                     )
365                 )
366             }
367 
368             // create methods
369             val constructors: MutableList<PsiConstructorItem> = ArrayList(5)
370             var hasConstructorWithOnlyOptionalArgs = false
371             var noArgConstructor: PsiConstructorItem? = null
372             for (psiMethod in psiMethods) {
373                 if (psiMethod.isConstructor) {
374                     val constructor =
375                         PsiConstructorItem.create(
376                             codebase,
377                             item,
378                             psiMethod,
379                             classTypeItemFactory,
380                         )
381                     // After KT-13495, "all constructors of `sealed` classes now have `protected`
382                     // visibility by default," and (S|U)LC follows that (hence the same in UAST).
383                     // However, that change was made to allow more flexible class hierarchy and
384                     // nesting. If they're compiled to JVM bytecode, sealed class's ctor is still
385                     // technically `private` to block instantiation from outside class hierarchy.
386                     // Another synthetic constructor, along with an internal ctor marker, is added
387                     // for subclasses of a sealed class. Therefore, from Metalava's perspective,
388                     // it is not necessary to track such semantically protected ctor. Here we force
389                     // set the visibility to `private` back to ignore it during signature writing.
390                     if (item.modifiers.isSealed()) {
391                         constructor.modifiers.setVisibilityLevel(VisibilityLevel.PRIVATE)
392                     }
393                     if (constructor.areAllParametersOptional()) {
394                         if (constructor.parameters().isNotEmpty()) {
395                             constructors.add(constructor)
396                             // uast reported a constructor having only optional arguments, so if we
397                             // later find an explicit no-arg constructor, we can skip it because
398                             // its existence is implied
399                             hasConstructorWithOnlyOptionalArgs = true
400                         } else {
401                             noArgConstructor = constructor
402                         }
403                     } else {
404                         constructors.add(constructor)
405                     }
406                 } else if (classKind == ClassKind.ENUM && psiMethod.isSyntheticEnumMethod()) {
407                     // skip
408                 } else {
409                     val method =
410                         PsiMethodItem.create(codebase, item, psiMethod, classTypeItemFactory)
411                     methods.add(method)
412                 }
413             }
414 
415             // Add the no-arg constructor back in if no constructors have only optional arguments
416             // or if an all-optional constructor created it as part of @JvmOverloads
417             if (
418                 noArgConstructor != null &&
419                     (!hasConstructorWithOnlyOptionalArgs ||
420                         noArgConstructor.modifiers.isAnnotatedWith("kotlin.jvm.JvmOverloads"))
421             ) {
422                 constructors.add(noArgConstructor)
423             }
424 
425             // Note that this is dependent on the constructor filtering above. UAST sometimes
426             // reports duplicate primary constructors, e.g.: the implicit no-arg constructor
427             constructors.singleOrNull { it.isPrimary }?.let { item.primaryConstructor = it }
428 
429             if (hasImplicitDefaultConstructor) {
430                 assert(constructors.isEmpty())
431                 constructors.add(
432                     PsiConstructorItem.createDefaultConstructor(codebase, item, psiClass)
433                 )
434             }
435 
436             val fields: MutableList<PsiFieldItem> = mutableListOf()
437             val psiFields = psiClass.fields
438             if (psiFields.isNotEmpty()) {
439                 psiFields.asSequence().mapTo(fields) {
440                     PsiFieldItem.create(codebase, item, it, classTypeItemFactory)
441                 }
442             }
443 
444             if (classKind == ClassKind.INTERFACE) {
445                 // All members are implicitly public, fields are implicitly static, non-static
446                 // methods are abstract
447                 // (except in Java 1.9, where they can be private
448                 for (method in methods) {
449                     if (!method.isPrivate) {
450                         method.mutableModifiers().setVisibilityLevel(VisibilityLevel.PUBLIC)
451                     }
452                 }
453                 for (method in fields) {
454                     val m = method.mutableModifiers()
455                     m.setVisibilityLevel(VisibilityLevel.PUBLIC)
456                     m.setStatic(true)
457                 }
458             }
459 
460             item.constructors = constructors
461             item.methods = methods
462             item.fields = fields
463 
464             item.properties = emptyList()
465 
466             if (isKotlin && methods.isNotEmpty()) {
467                 val getters = mutableMapOf<String, PsiMethodItem>()
468                 val setters = mutableMapOf<String, PsiMethodItem>()
469                 val backingFields = fields.associateBy { it.name() }
470                 val constructorParameters =
471                     item.primaryConstructor
472                         ?.parameters()
473                         ?.filter { (it.sourcePsi as? KtParameter)?.isPropertyParameter() ?: false }
474                         ?.associateBy { it.name() }
475                         .orEmpty()
476 
477                 for (method in methods) {
478                     if (method.isKotlinProperty()) {
479                         val name =
480                             when (val sourcePsi = method.sourcePsi) {
481                                 is KtProperty -> sourcePsi.name
482                                 is KtPropertyAccessor -> sourcePsi.property.name
483                                 is KtParameter -> sourcePsi.name
484                                 else -> null
485                             }
486                                 ?: continue
487 
488                         if (method.parameters().isEmpty()) {
489                             if (!method.name().startsWith("component")) {
490                                 getters[name] = method
491                             }
492                         } else {
493                             setters[name] = method
494                         }
495                     }
496                 }
497 
498                 val properties = mutableListOf<PsiPropertyItem>()
499                 for ((name, getter) in getters) {
500                     val type = getter.returnType() as? PsiTypeItem ?: continue
501                     properties +=
502                         PsiPropertyItem.create(
503                             codebase = codebase,
504                             containingClass = item,
505                             name = name,
506                             type = type,
507                             getter = getter,
508                             setter = setters[name],
509                             constructorParameter = constructorParameters[name],
510                             backingField = backingFields[name]
511                         )
512                 }
513                 item.properties = properties
514             }
515 
516             val psiInnerClasses = psiClass.innerClasses
517             item.innerClasses =
518                 if (psiInnerClasses.isEmpty()) {
519                     emptyList()
520                 } else {
521                     val result =
522                         psiInnerClasses
523                             .asSequence()
524                             .map {
525                                 codebase.createClass(
526                                     psiClass = it,
527                                     containingClassItem = item,
528                                     enclosingClassTypeItemFactory = classTypeItemFactory
529                                 )
530                             }
531                             .toMutableList()
532                     result
533                 }
534 
535             return item
536         }
537 
538         /**
539          * Compute the super types for the class.
540          *
541          * Returns a pair of the optional super class type and the possibly empty list of interface
542          * types.
543          */
544         private fun computeSuperTypes(
545             psiClass: PsiClass,
546             classKind: ClassKind,
547             classTypeItemFactory: PsiTypeItemFactory
548         ): Pair<ClassTypeItem?, List<ClassTypeItem>> {
549 
550             // A map from the qualified type name to the corresponding [KtTypeReference]. This is
551             // empty for non-Kotlin code, otherwise it maps from the qualified type name of a
552             // super type to the associated [KtTypeReference]. The qualified name is used to map
553             // between them because Kotlin does not differentiate between `implements` and `extends`
554             // lists and just has one super type list. The qualified name is safe because a class
555             // cannot implement/extend the same generic type multiple times with different type
556             // arguments so the qualified name should be unique among the super type list.
557             // The [KtTypeReference] is needed to access the type nullability of the generic type
558             // arguments.
559             val qualifiedNameToKt =
560                 if (psiClass is UClass) {
561                     psiClass.uastSuperTypes.associateBy({ it.getQualifiedName() }) {
562                         it.sourcePsi as KtTypeReference
563                     }
564                 } else emptyMap()
565 
566             // Get the [KtTypeReference], if any, associated with ths [PsiType] which must be a
567             // [PsiClassType] as that is the only type allowed in an extends/implements list.
568             fun PsiType.ktTypeReference(): KtTypeReference? {
569                 val qualifiedName = (this as PsiClassType).computeQualifiedName()
570                 return qualifiedNameToKt[qualifiedName]
571             }
572 
573             // Construct the super class type if needed and available.
574             val superClassType =
575                 if (classKind != ClassKind.INTERFACE) {
576                     val superClassPsiType = psiClass.superClassType as? PsiType
577                     superClassPsiType?.let { superClassType ->
578                         val ktTypeRef = superClassType.ktTypeReference()
579                         classTypeItemFactory.getSuperClassType(
580                             PsiTypeInfo(superClassType, ktTypeRef)
581                         )
582                     }
583                 } else null
584 
585             // Get the interfaces from the appropriate list.
586             val interfaces =
587                 if (classKind == ClassKind.INTERFACE || classKind == ClassKind.ANNOTATION_TYPE) {
588                     // An interface uses "extends <interfaces>", either explicitly for normal
589                     // interfaces or implicitly for annotations.
590                     psiClass.extendsListTypes
591                 } else {
592                     // A class uses "extends <interfaces>".
593                     psiClass.implementsListTypes
594                 }
595 
596             // Map them to PsiTypeItems.
597             val interfaceTypes =
598                 interfaces.map { interfaceType ->
599                     val ktTypeRef = interfaceType.ktTypeReference()
600                     classTypeItemFactory.getInterfaceType(PsiTypeInfo(interfaceType, ktTypeRef))
601                 }
602             return Pair(superClassType, interfaceTypes)
603         }
604 
605         private fun getClassKind(psiClass: PsiClass): ClassKind {
606             return when {
607                 psiClass.isAnnotationType -> ClassKind.ANNOTATION_TYPE
608                 psiClass.isInterface -> ClassKind.INTERFACE
609                 psiClass.isEnum -> ClassKind.ENUM
610                 psiClass is PsiTypeParameter ->
611                     error("Must not call this with a PsiTypeParameter - $psiClass")
612                 else -> ClassKind.CLASS
613             }
614         }
615 
616         /**
617          * Computes the "full" class name; this is not the qualified class name (e.g. with package)
618          * but for an inner class it includes all the outer classes
619          */
620         fun computeFullClassName(cls: PsiClass): String {
621             if (cls.containingClass == null) {
622                 val name = cls.name
623                 return name!!
624             } else {
625                 val list = mutableListOf<String>()
626                 var curr: PsiClass? = cls
627                 while (curr != null) {
628                     val name = curr.name
629                     curr =
630                         if (name != null) {
631                             list.add(name)
632                             curr.containingClass
633                         } else {
634                             break
635                         }
636                 }
637                 return list.asReversed().joinToString(separator = ".") { it }
638             }
639         }
640 
641         private fun hasImplicitDefaultConstructor(psiClass: PsiClass): Boolean {
642             if (psiClass.name?.startsWith("-") == true) {
643                 // Deliberately hidden; see examples like
644                 //     @file:JvmName("-ViewModelExtensions") // Hide from Java sources in the IDE.
645                 return false
646             }
647             if (psiClass is UClass && psiClass.sourcePsi == null) {
648                 // Top level kt classes (FooKt for Foo.kt) do not have implicit default constructor
649                 return false
650             }
651 
652             val constructors = psiClass.constructors
653             if (
654                 constructors.isEmpty() &&
655                     !psiClass.isInterface &&
656                     !psiClass.isAnnotationType &&
657                     !psiClass.isEnum
658             ) {
659                 if (PsiUtil.hasDefaultConstructor(psiClass)) {
660                     return true
661                 }
662 
663                 // The above method isn't always right; for example, for the
664                 // ContactsContract.Presence class
665                 // in the framework, which looks like this:
666                 //    @Deprecated
667                 //    public static final class Presence extends StatusUpdates {
668                 //    }
669                 // javac makes a default constructor:
670                 //    public final class android.provider.ContactsContract$Presence extends
671                 // android.provider.ContactsContract$StatusUpdates {
672                 //        public android.provider.ContactsContract$Presence();
673                 //    }
674                 // but the above method returns false. So add some of our own heuristics:
675                 if (
676                     psiClass.hasModifierProperty(PsiModifier.FINAL) &&
677                         !psiClass.hasModifierProperty(PsiModifier.ABSTRACT) &&
678                         psiClass.hasModifierProperty(PsiModifier.PUBLIC)
679                 ) {
680                     return true
681                 }
682             }
683 
684             return false
685         }
686     }
687 }
688 
689 /**
690  * Check whether the method is a synthetic enum method.
691  *
692  * i.e. `getEntries()` from Kotlin and `values()` and `valueOf(String)` from both Java and Kotlin.
693  */
PsiMethodnull694 private fun PsiMethod.isSyntheticEnumMethod(): Boolean {
695     if (containingClass?.isEnum != true) return false
696     val parameterCount = parameterList.parametersCount
697     return (parameterCount == 0 && (name == "values" || name == "getEntries")) ||
698         (parameterCount == 1 &&
699             name == "valueOf" &&
700             (parameterList.parameters[0].type as? PsiClassType)?.computeQualifiedName() ==
701                 "java.lang.String")
702 }
703