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.compatibility
20 import com.android.tools.metalava.model.AnnotationRetention
21 import com.android.tools.metalava.model.ClassItem
22 import com.android.tools.metalava.model.CompilationUnit
23 import com.android.tools.metalava.model.ConstructorItem
24 import com.android.tools.metalava.model.FieldItem
25 import com.android.tools.metalava.model.MethodItem
26 import com.android.tools.metalava.model.PackageItem
27 import com.android.tools.metalava.model.PropertyItem
28 import com.android.tools.metalava.model.TypeItem
29 import com.android.tools.metalava.model.TypeParameterList
30 import com.android.tools.metalava.model.VisibilityLevel
31 import com.intellij.lang.jvm.types.JvmReferenceType
32 import com.intellij.psi.PsiClass
33 import com.intellij.psi.PsiClassType
34 import com.intellij.psi.PsiCompiledFile
35 import com.intellij.psi.PsiModifier
36 import com.intellij.psi.PsiModifierListOwner
37 import com.intellij.psi.PsiType
38 import com.intellij.psi.PsiTypeParameter
39 import com.intellij.psi.SyntheticElement
40 import com.intellij.psi.impl.source.PsiClassReferenceType
41 import com.intellij.psi.util.PsiUtil
42 import org.jetbrains.kotlin.psi.KtProperty
43 import org.jetbrains.uast.UClass
44 import org.jetbrains.uast.UFile
45 import org.jetbrains.uast.UMethod
46 import org.jetbrains.uast.getParentOfType
47 import org.jetbrains.uast.kotlin.KotlinUClass
48
49 open class PsiClassItem(
50 override val codebase: PsiBasedCodebase,
51 val psiClass: PsiClass,
52 private val name: String,
53 private val fullName: String,
54 private val qualifiedName: String,
55 private val hasImplicitDefaultConstructor: Boolean,
56 val classType: ClassType,
57 modifiers: PsiModifierItem,
58 documentation: String
59 ) :
60 PsiItem(
61 codebase = codebase,
62 modifiers = modifiers,
63 documentation = documentation,
64 element = psiClass
65 ), ClassItem {
66
67 lateinit var containingPackage: PsiPackageItem
68
69 override fun containingPackage(): PackageItem = containingClass?.containingPackage() ?: containingPackage
70 override fun simpleName(): String = name
71 override fun fullName(): String = fullName
72 override fun qualifiedName(): String = qualifiedName
73 override fun isDefined(): Boolean = codebase.unsupported()
74 override fun isInterface(): Boolean = classType == ClassType.INTERFACE
75 override fun isAnnotationType(): Boolean = classType == ClassType.ANNOTATION_TYPE
76 override fun isEnum(): Boolean = classType == ClassType.ENUM
77 override fun hasImplicitDefaultConstructor(): Boolean = hasImplicitDefaultConstructor
78
79 private var superClass: ClassItem? = null
80 private var superClassType: TypeItem? = null
81 override fun superClass(): ClassItem? = superClass
82 override fun superClassType(): TypeItem? = superClassType
83
84 override fun setSuperClass(superClass: ClassItem?, superClassType: TypeItem?) {
85 this.superClass = superClass
86 this.superClassType = superClassType
87 }
88
89 override var stubConstructor: ConstructorItem? = null
90 override var artifact: String? = null
91
92 private var containingClass: PsiClassItem? = null
93 override fun containingClass(): PsiClassItem? = containingClass
94
95 override var hasPrivateConstructor: Boolean = false
96
97 override fun interfaceTypes(): List<TypeItem> = interfaceTypes
98
99 override fun setInterfaceTypes(interfaceTypes: List<TypeItem>) {
100 @Suppress("UNCHECKED_CAST")
101 setInterfaces(interfaceTypes as List<PsiTypeItem>)
102 }
103
104 private fun setInterfaces(interfaceTypes: List<PsiTypeItem>) {
105 this.interfaceTypes = interfaceTypes
106 }
107
108 private var allInterfaces: List<ClassItem>? = null
109
110 override fun allInterfaces(): Sequence<ClassItem> {
111 if (allInterfaces == null) {
112 val classes = mutableSetOf<PsiClass>()
113 var curr: PsiClass? = psiClass
114 while (curr != null) {
115 if (curr.isInterface && !classes.contains(curr)) {
116 classes.add(curr)
117 }
118 addInterfaces(classes, curr.interfaces)
119 curr = curr.superClass
120 }
121 val result = mutableListOf<ClassItem>()
122 for (cls in classes) {
123 val item = codebase.findOrCreateClass(cls)
124 result.add(item)
125 }
126
127 allInterfaces = result
128 }
129
130 return allInterfaces!!.asSequence()
131 }
132
133 private fun addInterfaces(result: MutableSet<PsiClass>, interfaces: Array<out PsiClass>) {
134 for (itf in interfaces) {
135 if (itf.isInterface && !result.contains(itf)) {
136 result.add(itf)
137 addInterfaces(result, itf.interfaces)
138 val superClass = itf.superClass
139 if (superClass != null) {
140 addInterfaces(result, arrayOf(superClass))
141 }
142 }
143 }
144 }
145
146 private lateinit var innerClasses: List<PsiClassItem>
147 private lateinit var interfaceTypes: List<TypeItem>
148 private lateinit var constructors: List<PsiConstructorItem>
149 private lateinit var methods: List<PsiMethodItem>
150 private lateinit var properties: List<PsiPropertyItem>
151 private lateinit var fields: List<FieldItem>
152
153 /**
154 * If this item was created by filtering down a different codebase, this temporarily
155 * points to the original item during construction. This is used to let us initialize
156 * for example throws lists later, when all classes in the codebase have been
157 * initialized.
158 */
159 internal var source: PsiClassItem? = null
160
161 override fun innerClasses(): List<PsiClassItem> = innerClasses
162 override fun constructors(): List<PsiConstructorItem> = constructors
163 override fun methods(): List<PsiMethodItem> = methods
164 override fun properties(): List<PropertyItem> = properties
165 override fun fields(): List<FieldItem> = fields
166
167 override fun toType(): TypeItem {
168 return PsiTypeItem.create(codebase, codebase.getClassType(psiClass))
169 }
170
171 override fun hasTypeVariables(): Boolean = psiClass.hasTypeParameters()
172
173 override fun typeParameterList(): TypeParameterList {
174 if (psiClass.hasTypeParameters()) {
175 return PsiTypeParameterList(
176 codebase, psiClass.typeParameterList
177 ?: return TypeParameterList.NONE
178 )
179 } else {
180 return TypeParameterList.NONE
181 }
182 }
183
184 override fun typeArgumentClasses(): List<ClassItem> {
185 return PsiTypeItem.typeParameterClasses(
186 codebase,
187 psiClass.typeParameterList
188 )
189 }
190
191 override val isTypeParameter: Boolean
192 get() = psiClass is PsiTypeParameter
193
194 override fun getCompilationUnit(): CompilationUnit? {
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>(UFile::class.java)
207 } else {
208 null
209 }
210 return PsiCompilationUnit(codebase, uFile, containingFile)
211 }
212
213 override fun finishInitialization() {
214 super.finishInitialization()
215
216 for (method in methods) {
217 method.finishInitialization()
218 }
219 for (method in constructors) {
220 method.finishInitialization()
221 }
222 for (field in fields) {
223 // There may be non-Psi fields here later (thanks to addField) but not during construction
224 (field as PsiFieldItem).finishInitialization()
225 }
226 for (inner in innerClasses) {
227 inner.finishInitialization()
228 }
229
230 // Delay initializing super classes and implemented interfaces for all inner classes: they may refer
231 // to *other* inner classes in this class, which would lead to an attempt to construct
232 // recursively. Instead, we wait until all the inner classes have been constructed, and at
233 // the very end, initialize super classes and interfaces recursively.
234 if (psiClass.containingClass == null) {
235 initializeSuperClasses()
236 }
237 }
238
239 private fun initializeSuperClasses() {
240 val extendsListTypes = psiClass.extendsListTypes
241 if (extendsListTypes.isNotEmpty()) {
242 val type = PsiTypeItem.create(codebase, extendsListTypes[0])
243 this.superClassType = type
244 this.superClass = type.asClass()
245 } else {
246 val superType = psiClass.superClassType
247 if (superType is PsiType) {
248 this.superClassType = PsiTypeItem.create(codebase, superType)
249 this.superClass = this.superClassType?.asClass()
250 }
251 }
252
253 // Add interfaces. If this class is an interface, it can implement both
254 // classes from the extends clause and from the implements clause.
255 val interfaces = psiClass.implementsListTypes
256 setInterfaces(if (interfaces.isEmpty() && extendsListTypes.size <= 1) {
257 emptyList()
258 } else {
259 val result = ArrayList<PsiTypeItem>(interfaces.size + extendsListTypes.size - 1)
260 val create: (PsiClassType) -> PsiTypeItem = {
261 val type = PsiTypeItem.create(codebase, it)
262 type.asClass() // ensure that we initialize classes eagerly too such that they're registered etc
263 type
264 }
265 (1 until extendsListTypes.size).mapTo(result) { create(extendsListTypes[it]) }
266 interfaces.mapTo(result) { create(it) }
267 result
268 })
269
270 for (inner in innerClasses) {
271 inner.initializeSuperClasses()
272 }
273 }
274
275 protected fun initialize(
276 innerClasses: List<PsiClassItem>,
277 interfaceTypes: List<TypeItem>,
278 constructors: List<PsiConstructorItem>,
279 methods: List<PsiMethodItem>,
280 fields: List<FieldItem>
281 ) {
282 this.innerClasses = innerClasses
283 this.interfaceTypes = interfaceTypes
284 this.constructors = constructors
285 this.methods = methods
286 this.fields = fields
287 }
288
289 override fun mapTypeVariables(target: ClassItem): Map<String, String> {
290 val targetPsi = target.psi() as PsiClass
291 val maps = mapTypeVariablesToSuperclass(
292 psiClass, targetPsi, considerSuperClasses = true,
293 considerInterfaces = targetPsi.isInterface
294 ) ?: return emptyMap()
295
296 if (maps.isEmpty()) {
297 return emptyMap()
298 }
299
300 if (maps.size == 1) {
301 return maps[0]
302 }
303
304 val first = maps[0]
305 val flattened = mutableMapOf<String, String>()
306 for (key in first.keys) {
307 var variable: String? = key
308 for (map in maps) {
309 val value = map[variable]
310 variable = value
311 if (value == null) {
312 break
313 } else {
314 flattened[key] = value
315 }
316 }
317 }
318 return flattened
319 }
320
321 override fun equals(other: Any?): Boolean {
322 if (this === other) {
323 return true
324 }
325 return other is ClassItem && qualifiedName == other.qualifiedName()
326 }
327
328 /**
329 * Creates a constructor in this class
330 */
331 override fun createDefaultConstructor(): ConstructorItem {
332 return PsiConstructorItem.createDefaultConstructor(codebase, this, psiClass)
333 }
334
335 override fun createMethod(template: MethodItem): MethodItem {
336 val method = template as PsiMethodItem
337
338 val replacementMap = mapTypeVariables(template.containingClass())
339 val newMethod: PsiMethodItem
340 if (replacementMap.isEmpty()) {
341 newMethod = PsiMethodItem.create(codebase, this, method)
342 } else {
343 val stub = method.toStub(replacementMap)
344 val psiMethod = codebase.createPsiMethod(stub, psiClass)
345 newMethod = PsiMethodItem.create(codebase, this, psiMethod)
346 newMethod.inheritedMethod = method.inheritedMethod
347 newMethod.documentation = method.documentation
348 }
349
350 if (template.throwsTypes().isEmpty()) {
351 newMethod.setThrowsTypes(emptyList())
352 } else {
353 val throwsTypes = mutableListOf<ClassItem>()
354 for (type in template.throwsTypes()) {
355 if (type.codebase === codebase) {
356 throwsTypes.add(type)
357 } else {
358 throwsTypes.add(codebase.findOrCreateClass(((type as PsiClassItem).psiClass)))
359 }
360 }
361 newMethod.setThrowsTypes(throwsTypes)
362 }
363
364 return newMethod
365 }
366
367 override fun addMethod(method: MethodItem) {
368 (methods as MutableList<PsiMethodItem>).add(method as PsiMethodItem)
369 }
370
371 private var retention: AnnotationRetention? = null
372
373 override fun getRetention(): AnnotationRetention {
374 retention?.let { return it }
375
376 if (!isAnnotationType()) {
377 error("getRetention() should only be called on annotation classes")
378 }
379
380 retention = ClassItem.findRetention(this)
381 return retention!!
382 }
383
384 override fun hashCode(): Int = qualifiedName.hashCode()
385
386 override fun toString(): String = "class ${qualifiedName()}"
387
388 companion object {
389 private fun hasExplicitRetention(modifiers: PsiModifierItem, psiClass: PsiClass, isKotlin: Boolean): Boolean {
390 if (modifiers.findAnnotation("java.lang.annotation.Retention") != null) {
391 return true
392 }
393 if (modifiers.findAnnotation("kotlin.annotation.Retention") != null) {
394 return true
395 }
396 if (isKotlin && psiClass is UClass) {
397 // In Kotlin some annotations show up on the Java facade only; for example,
398 // a @DslMarker annotation will imply a runtime annotation which is present
399 // in the java facade, not in the source list of annotations
400 val modifierList = psiClass.modifierList
401 if (modifierList != null && modifierList.annotations.any {
402 val qualifiedName = it.qualifiedName
403 qualifiedName == "kotlin.annotation.Retention" ||
404 qualifiedName == "java.lang.annotation.Retention"
405 }) {
406 return true
407 }
408 }
409 return false
410 }
411
412 fun create(codebase: PsiBasedCodebase, psiClass: PsiClass): PsiClassItem {
413 if (psiClass is PsiTypeParameter) {
414 return PsiTypeParameterItem.create(codebase, psiClass)
415 }
416 val simpleName = psiClass.name!!
417 val fullName = computeFullClassName(psiClass)
418 val qualifiedName = psiClass.qualifiedName ?: simpleName
419 val hasImplicitDefaultConstructor = hasImplicitDefaultConstructor(psiClass)
420 val classType = ClassType.getClassType(psiClass)
421
422 val commentText = PsiItem.javadoc(psiClass)
423 val modifiers = modifiers(codebase, psiClass, commentText)
424 val item = PsiClassItem(
425 codebase = codebase,
426 psiClass = psiClass,
427 name = simpleName,
428 fullName = fullName,
429 qualifiedName = qualifiedName,
430 classType = classType,
431 hasImplicitDefaultConstructor = hasImplicitDefaultConstructor,
432 documentation = commentText,
433 modifiers = modifiers
434 )
435 codebase.registerClass(item)
436 item.modifiers.setOwner(item)
437
438 // Construct the children
439 val psiMethods = psiClass.methods
440 val methods: MutableList<PsiMethodItem> = ArrayList(psiMethods.size)
441 val isKotlin = isKotlin(psiClass)
442
443 if (classType == ClassType.ENUM) {
444 // In compatibility mode we want explicit valueOf and values methods.
445 // UAST recently started including these in the AST (as synthetic elements),
446 // so we no longer need to create those here, but we still need to create
447 // the synthetic constructor
448 if (compatibility.defaultEnumMethods) {
449 // Also add a private constructor; used when emitting the private API
450 val psiMethod = codebase.createConstructor("private ${psiClass.name}", psiClass)
451 methods.add(PsiConstructorItem.create(codebase, item, psiMethod))
452 }
453 } else if (classType == ClassType.ANNOTATION_TYPE && compatibility.explicitlyListClassRetention &&
454 !hasExplicitRetention(modifiers, psiClass, isKotlin)
455 ) {
456 // By policy, include explicit retention policy annotation if missing
457 modifiers.addAnnotation(
458 codebase.createAnnotation(
459 "@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS)",
460 item, false
461 )
462 )
463 }
464
465 val constructors: MutableList<PsiConstructorItem> = ArrayList(5)
466 for (psiMethod in psiMethods) {
467 if (psiMethod.isConstructor) {
468 val constructor = PsiConstructorItem.create(codebase, item, psiMethod)
469 constructors.add(constructor)
470 } else if (classType == ClassType.ENUM &&
471 !compatibility.defaultEnumMethods &&
472 psiMethod is SyntheticElement
473 ) {
474 // skip
475 } else {
476 val method = PsiMethodItem.create(codebase, item, psiMethod)
477 methods.add(method)
478 }
479 }
480
481 if (hasImplicitDefaultConstructor) {
482 assert(constructors.isEmpty())
483 constructors.add(PsiConstructorItem.createDefaultConstructor(codebase, item, psiClass))
484 }
485
486 val fields: MutableList<FieldItem> = mutableListOf()
487 val psiFields = psiClass.fields
488 if (psiFields.isNotEmpty()) {
489 psiFields.asSequence()
490 .mapTo(fields) {
491 PsiFieldItem.create(codebase, item, it)
492 }
493 }
494
495 if (classType == ClassType.INTERFACE) {
496 // All members are implicitly public, fields are implicitly static, non-static methods are abstract
497 // (except in Java 1.9, where they can be private
498 for (method in methods) {
499 if (!method.isPrivate) {
500 method.mutableModifiers().setVisibilityLevel(VisibilityLevel.PUBLIC)
501 }
502 }
503 for (method in fields) {
504 val m = method.mutableModifiers()
505 m.setVisibilityLevel(VisibilityLevel.PUBLIC)
506 m.setStatic(true)
507 }
508 }
509
510 item.constructors = constructors
511 item.methods = methods
512 item.fields = fields
513
514 item.properties = emptyList()
515 if (isKotlin) {
516 // Try to initialize the Kotlin properties
517 val properties = mutableListOf<PsiPropertyItem>()
518 for (method in psiMethods) {
519 if (method is UMethod) {
520 if (method.modifierList.hasModifierProperty(PsiModifier.STATIC)) {
521 // Skip extension properties
522 continue
523 }
524 val sourcePsi = method.sourcePsi
525 if (sourcePsi is KtProperty) {
526 if (method.name.startsWith("set")) {
527 continue
528 }
529 val name = sourcePsi.name ?: continue
530 val psiType = method.returnType ?: continue
531 properties.add(PsiPropertyItem.create(codebase, item, name, psiType, method))
532 }
533 }
534 }
535 item.properties = properties
536 }
537
538 val psiInnerClasses = psiClass.innerClasses
539 item.innerClasses = if (psiInnerClasses.isEmpty()) {
540 emptyList()
541 } else {
542 val result = psiInnerClasses.asSequence()
543 .map {
544 val inner = codebase.findOrCreateClass(it)
545 inner.containingClass = item
546 inner
547 }
548 .toMutableList()
549 result
550 }
551
552 return item
553 }
554
555 /**
556 * Computes the "full" class name; this is not the qualified class name (e.g. with package)
557 * but for an inner class it includes all the outer classes
558 */
559 fun computeFullClassName(cls: PsiClass): String {
560 if (cls.containingClass == null) {
561 val name = cls.name
562 return name!!
563 } else {
564 val list = mutableListOf<String>()
565 var curr: PsiClass? = cls
566 while (curr != null) {
567 val name = curr.name
568 curr = if (name != null) {
569 list.add(name)
570 curr.containingClass
571 } else {
572 break
573 }
574 }
575 return list.asReversed().asSequence().joinToString(separator = ".") { it }
576 }
577 }
578
579 private fun hasImplicitDefaultConstructor(psiClass: PsiClass): Boolean {
580 if (psiClass.name?.startsWith("-") == true) {
581 // Deliberately hidden; see examples like
582 // @file:JvmName("-ViewModelExtensions") // Hide from Java sources in the IDE.
583 return false
584 }
585 if (psiClass is KotlinUClass && psiClass.sourcePsi == null) {
586 // Top level kt classes (FooKt for Foo.kt) do not have implicit default constructor
587 return false
588 }
589
590 val constructors = psiClass.constructors
591 if (constructors.isEmpty() && !psiClass.isInterface && !psiClass.isAnnotationType && !psiClass.isEnum) {
592 if (PsiUtil.hasDefaultConstructor(psiClass)) {
593 return true
594 }
595
596 // The above method isn't always right; for example, for the ContactsContract.Presence class
597 // in the framework, which looks like this:
598 // @Deprecated
599 // public static final class Presence extends StatusUpdates {
600 // }
601 // javac makes a default constructor:
602 // public final class android.provider.ContactsContract$Presence extends android.provider.ContactsContract$StatusUpdates {
603 // public android.provider.ContactsContract$Presence();
604 // }
605 // but the above method returns false. So add some of our own heuristics:
606 if (psiClass.hasModifierProperty(PsiModifier.FINAL) && !psiClass.hasModifierProperty(
607 PsiModifier.ABSTRACT
608 ) &&
609 psiClass.hasModifierProperty(PsiModifier.PUBLIC)
610 ) {
611 return true
612 }
613 }
614
615 return false
616 }
617
618 fun mapTypeVariablesToSuperclass(
619 psiClass: PsiClass,
620 targetClass: PsiClass,
621 considerSuperClasses: Boolean = true,
622 considerInterfaces: Boolean = psiClass.isInterface
623 ): MutableList<Map<String, String>>? {
624 // TODO: Prune search if type doesn't have type arguments!
625 if (considerSuperClasses) {
626 val list = mapTypeVariablesToSuperclass(
627 psiClass.superClassType, targetClass,
628 considerSuperClasses, considerInterfaces
629 )
630 if (list != null) {
631 return list
632 }
633 }
634
635 if (considerInterfaces) {
636 for (interfaceType in psiClass.interfaceTypes) {
637 val list = mapTypeVariablesToSuperclass(
638 interfaceType, targetClass,
639 considerSuperClasses, considerInterfaces
640 )
641 if (list != null) {
642 return list
643 }
644 }
645 }
646
647 return null
648 }
649
650 private fun mapTypeVariablesToSuperclass(
651 type: JvmReferenceType?,
652 targetClass: PsiClass,
653 considerSuperClasses: Boolean = true,
654 considerInterfaces: Boolean = true
655 ): MutableList<Map<String, String>>? {
656 // TODO: Prune search if type doesn't have type arguments!
657 val superType = type as? PsiClassReferenceType
658 val superClass = superType?.resolve()
659 if (superClass != null) {
660 if (superClass == targetClass) {
661 val map = mapTypeVariablesToSuperclass(superType)
662 return if (map != null) {
663 mutableListOf(map)
664 } else {
665 null
666 }
667 } else {
668 val list = mapTypeVariablesToSuperclass(
669 superClass, targetClass, considerSuperClasses,
670 considerInterfaces
671 )
672 if (list != null) {
673 val map = mapTypeVariablesToSuperclass(superType)
674 if (map != null) {
675 list.add(map)
676 }
677 return list
678 }
679 }
680 }
681
682 return null
683 }
684
685 private fun mapTypeVariablesToSuperclass(superType: PsiClassReferenceType?): Map<String, String>? {
686 superType ?: return null
687
688 val map = mutableMapOf<String, String>()
689 val superClass = superType.resolve()
690 if (superClass != null && superType.hasParameters()) {
691 val superTypeParameters = superClass.typeParameters
692 superType.parameters.forEachIndexed { index, parameter ->
693 if (parameter is PsiClassReferenceType) {
694 val parameterClass = parameter.resolve()
695 if (parameterClass != null) {
696 val parameterName = parameterClass.qualifiedName ?: parameterClass.name ?: parameter.name
697 if (index < superTypeParameters.size) {
698 val superTypeParameter = superTypeParameters[index]
699 val superTypeName = superTypeParameter.qualifiedName ?: superTypeParameter.name
700 if (superTypeName != null) {
701 map[superTypeName] = parameterName
702 }
703 }
704 }
705 }
706 }
707 }
708
709 return map
710 }
711 }
712 }
713
PsiModifierListOwnernull714 fun PsiModifierListOwner.isPrivate(): Boolean = modifierList?.hasExplicitModifier(PsiModifier.PRIVATE) == true
715 fun PsiModifierListOwner.isPackagePrivate(): Boolean {
716 val modifiers = modifierList ?: return false
717 return !(modifiers.hasModifierProperty(PsiModifier.PUBLIC) ||
718 modifiers.hasModifierProperty(PsiModifier.PROTECTED))
719 }