aboutsummaryrefslogtreecommitdiff
path: root/plugins/base/src/main/kotlin/translators/psi
diff options
context:
space:
mode:
authorIgnat Beresnev <ignat.beresnev@jetbrains.com>2022-05-31 15:34:37 +0200
committerGitHub <noreply@github.com>2022-05-31 15:34:37 +0200
commit623cf222ca2ac5e8b9628af5927956ecb6d44d1e (patch)
treea995477a569dd27b99d24bd901bdcc024f4f7dd0 /plugins/base/src/main/kotlin/translators/psi
parent8913c97b25ebf5061ea367129544d7e5186a738d (diff)
downloaddokka-623cf222ca2ac5e8b9628af5927956ecb6d44d1e.tar.gz
dokka-623cf222ca2ac5e8b9628af5927956ecb6d44d1e.tar.bz2
dokka-623cf222ca2ac5e8b9628af5927956ecb6d44d1e.zip
Fix gathering inherited properties (#2481)
* Fix gathering inherited properties in PSI * Refacotr KaJ transformer. Change wrapping TagWrapper for getters and setters. * Add logic to merge inherited properties in kotlin from java sources. * Remove getters and setters from JvmField properties for DObject, DEnum, DInterface in KaJ. * Unify InheritedMember DRI logic. * Fix gathering docs obtained from inheriting java sources in descriptors * Apply requested changes. * Resolve rebase conflicts * Use 221 for qodana analysis * Move accessors generation into DefaultDescriptorToDocumentableTranslator * Fix special "is" case for accessors and refactor logic in general * Remove ambiguous import after rebasing * Remove unused imports and format code * Apply review comment suggestions * Preserve previously lost accessor lookalikes * Extract a variable and correct a typo Co-authored-by: Andrzej Ratajczak <andrzej.ratajczak98@gmail.com>
Diffstat (limited to 'plugins/base/src/main/kotlin/translators/psi')
-rw-r--r--plugins/base/src/main/kotlin/translators/psi/DefaultPsiToDocumentableTranslator.kt406
-rw-r--r--plugins/base/src/main/kotlin/translators/psi/PsiAccessorConventionUtil.kt58
2 files changed, 300 insertions, 164 deletions
diff --git a/plugins/base/src/main/kotlin/translators/psi/DefaultPsiToDocumentableTranslator.kt b/plugins/base/src/main/kotlin/translators/psi/DefaultPsiToDocumentableTranslator.kt
index f9199154..5b30543e 100644
--- a/plugins/base/src/main/kotlin/translators/psi/DefaultPsiToDocumentableTranslator.kt
+++ b/plugins/base/src/main/kotlin/translators/psi/DefaultPsiToDocumentableTranslator.kt
@@ -25,7 +25,6 @@ import org.jetbrains.dokka.links.*
import org.jetbrains.dokka.model.*
import org.jetbrains.dokka.model.AnnotationTarget
import org.jetbrains.dokka.model.Nullable
-import org.jetbrains.dokka.model.TypeConstructor
import org.jetbrains.dokka.model.doc.DocumentationNode
import org.jetbrains.dokka.model.doc.Param
import org.jetbrains.dokka.model.properties.PropertyContainer
@@ -87,14 +86,14 @@ class DefaultPsiToDocumentableTranslator(
)
DModule(
- context.configuration.moduleName,
- psiFiles.parallelMapNotNull { it.safeAs<PsiJavaFile>() }.groupBy { it.packageName }.toList()
+ name = context.configuration.moduleName,
+ packages = psiFiles.parallelMapNotNull { it.safeAs<PsiJavaFile>() }.groupBy { it.packageName }.toList()
.parallelMap { (packageName: String, psiFiles: List<PsiJavaFile>) ->
docParser.parsePackage(packageName, psiFiles)
},
- emptyMap(),
- null,
- setOf(sourceSet)
+ documentation = emptyMap(),
+ expectPresentInSet = null,
+ sourceSets = setOf(sourceSet)
)
}
}
@@ -122,6 +121,9 @@ class DefaultPsiToDocumentableTranslator(
private val PsiMethod.hash: Int
get() = "$returnType $name$parameterList".hashCode()
+ private val PsiField.hash: Int
+ get() = "$type $name".hashCode()
+
private val PsiClassType.shouldBeIgnored: Boolean
get() = isClass("java.lang.Enum") || isClass("java.lang.Object")
@@ -148,19 +150,19 @@ class DefaultPsiToDocumentableTranslator(
val annotations = packageInfo?.packageStatement?.annotationList?.annotations
DPackage(
- dri,
- emptyList(),
- emptyList(),
- psiFiles.parallelMap { psiFile ->
+ dri = dri,
+ functions = emptyList(),
+ properties = emptyList(),
+ classlikes = psiFiles.parallelMap { psiFile ->
coroutineScope {
psiFile.classes.asIterable().parallelMap { parseClasslike(it, dri) }
}
}.flatten(),
- emptyList(),
- documentation,
- null,
- setOf(sourceSetData),
- PropertyContainer.withAll(
+ typealiases = emptyList(),
+ documentation = documentation,
+ expectPresentInSet = null,
+ sourceSets = setOf(sourceSetData),
+ extra = PropertyContainer.withAll(
annotations?.toList().orEmpty().toListOfAnnotations().toSourceSetDependent().toAnnotations()
)
)
@@ -171,10 +173,16 @@ class DefaultPsiToDocumentableTranslator(
val dri = parent.withClass(name.toString())
val superMethodsKeys = hashSetOf<Int>()
val superMethods = mutableListOf<Pair<PsiMethod, DRI>>()
+ val superFieldsKeys = hashSetOf<Int>()
+ val superFields = mutableListOf<Pair<PsiField, DRI>>()
methods.asIterable().parallelForEach { superMethodsKeys.add(it.hash) }
/**
- * Caution! This method mutates superMethodsKeys and superMethods
+ * Caution! This method mutates
+ * - superMethodsKeys
+ * - superMethods
+ * - superFieldsKeys
+ * - superKeys
*/
fun Array<PsiClassType>.getSuperTypesPsiClasses(): List<Pair<PsiClass, JavaClassKindTypes>> {
forEach { type ->
@@ -189,6 +197,13 @@ class DefaultPsiToDocumentableTranslator(
superMethods.add(Pair(method, definedAt))
}
}
+ it.fields.forEach { field ->
+ val hash = field.hash
+ if (!superFieldsKeys.contains(hash)) {
+ superFieldsKeys.add(hash)
+ superFields.add(Pair(field, definedAt))
+ }
+ }
}
}
return filter { !it.shouldBeIgnored }.mapNotNull { supertypePsi ->
@@ -223,24 +238,60 @@ class DefaultPsiToDocumentableTranslator(
}
val ancestry: AncestryNode = traversePsiClassForAncestorsAndInheritedMembers(this)
- val (regularFunctions, accessors) = splitFunctionsAndAccessors()
+
+ val (regularFunctions, accessors) = splitFunctionsAndAccessors(psi.fields, psi.methods)
+ val (regularSuperFunctions, superAccessors) = splitFunctionsAndAccessors(
+ fields = superFields.map { it.first }.toTypedArray(),
+ methods = superMethods.map { it.first }.toTypedArray()
+ )
+
+ val regularSuperFunctionsKeys = regularSuperFunctions.map { it.hash }.toSet()
+ val regularSuperFunctionsWithDRI = superMethods.filter { it.first.hash in regularSuperFunctionsKeys }
+
+ val superAccessorsWithDRI = superAccessors.mapValues { (field, methods) ->
+ val containsJvmField = field.annotations.mapNotNull { it.toAnnotation() }.any { it.isJvmField() }
+ if (containsJvmField) {
+ emptyList()
+ } else {
+ methods.mapNotNull { method -> superMethods.find { it.first.hash == method.hash } }
+ }
+ }
+
val overridden = regularFunctions.flatMap { it.findSuperMethods().toList() }
val documentation = javadocParser.parseDocumentation(this).toSourceSetDependent()
val allFunctions = async {
- regularFunctions.parallelMapNotNull {
+ val parsedRegularFunctions = regularFunctions.parallelMapNotNull {
if (!it.isConstructor) parseFunction(
it,
parentDRI = dri
) else null
- } + superMethods.filter { it.first !in overridden }.parallelMap { parseFunction(it.first, inheritedFrom = it.second) }
+ }
+ val parsedSuperFunctions = regularSuperFunctionsWithDRI
+ .filter { it.first !in overridden }
+ .parallelMap { parseFunction(it.first, inheritedFrom = it.second) }
+
+ parsedRegularFunctions + parsedSuperFunctions
+ }
+ val allFields = async {
+ val parsedFields = fields.toList().parallelMapNotNull {
+ parseField(it, accessors[it].orEmpty())
+ }
+ val parsedSuperFields = superFields.parallelMapNotNull { (field, dri) ->
+ parseFieldWithInheritingAccessors(
+ field,
+ superAccessorsWithDRI[field].orEmpty(),
+ inheritedFrom = dri
+ )
+ }
+ parsedFields + parsedSuperFields
}
val source = PsiDocumentableSource(this).toSourceSetDependent()
val classlikes = async { innerClasses.asIterable().parallelMap { parseClasslike(it, dri) } }
val visibility = getVisibility().toSourceSetDependent()
val ancestors = (listOfNotNull(ancestry.superclass?.let {
- it.typeConstructor.let {
+ it.typeConstructor.let { typeConstructor ->
TypeConstructorWithKind(
- it,
+ typeConstructor,
JavaClassKindTypes.CLASS
)
}
@@ -251,103 +302,103 @@ class DefaultPsiToDocumentableTranslator(
when {
isAnnotationType ->
DAnnotation(
- name.orEmpty(),
- dri,
- documentation,
- null,
- source,
- allFunctions.await(),
- fields.mapNotNull { parseField(it, accessors[it].orEmpty()) },
- classlikes.await(),
- visibility,
- null,
- constructors.map { parseFunction(it, true) },
- mapTypeParameters(dri),
- setOf(sourceSetData),
- false,
- PropertyContainer.withAll(
+ name = name.orEmpty(),
+ dri = dri,
+ documentation = documentation,
+ expectPresentInSet = null,
+ sources = source,
+ functions = allFunctions.await(),
+ properties = allFields.await(),
+ classlikes = classlikes.await(),
+ visibility = visibility,
+ companion = null,
+ constructors = constructors.map { parseFunction(it, true) },
+ generics = mapTypeParameters(dri),
+ sourceSets = setOf(sourceSetData),
+ isExpectActual = false,
+ extra = PropertyContainer.withAll(
implementedInterfacesExtra,
annotations.toList().toListOfAnnotations().toSourceSetDependent()
.toAnnotations()
)
)
isEnum -> DEnum(
- dri,
- name.orEmpty(),
- fields.filterIsInstance<PsiEnumConstant>().map { entry ->
+ dri = dri,
+ name = name.orEmpty(),
+ entries = fields.filterIsInstance<PsiEnumConstant>().map { entry ->
DEnumEntry(
- dri.withClass(entry.name).withEnumEntryExtra(),
- entry.name,
- javadocParser.parseDocumentation(entry).toSourceSetDependent(),
- null,
- emptyList(),
- emptyList(),
- emptyList(),
- setOf(sourceSetData),
- PropertyContainer.withAll(
+ dri = dri.withClass(entry.name).withEnumEntryExtra(),
+ name = entry.name,
+ documentation = javadocParser.parseDocumentation(entry).toSourceSetDependent(),
+ expectPresentInSet = null,
+ functions = emptyList(),
+ properties = emptyList(),
+ classlikes = emptyList(),
+ sourceSets = setOf(sourceSetData),
+ extra = PropertyContainer.withAll(
implementedInterfacesExtra,
annotations.toList().toListOfAnnotations().toSourceSetDependent()
.toAnnotations()
)
)
},
- documentation,
- null,
- source,
- allFunctions.await(),
- fields.filter { it !is PsiEnumConstant }.map { parseField(it, accessors[it].orEmpty()) },
- classlikes.await(),
- visibility,
- null,
- constructors.map { parseFunction(it, true) },
- ancestors,
- setOf(sourceSetData),
- false,
- PropertyContainer.withAll(
+ documentation = documentation,
+ expectPresentInSet = null,
+ sources = source,
+ functions = allFunctions.await(),
+ properties = fields.filter { it !is PsiEnumConstant }.map { parseField(it, accessors[it].orEmpty()) },
+ classlikes = classlikes.await(),
+ visibility = visibility,
+ companion = null,
+ constructors = constructors.map { parseFunction(it, true) },
+ supertypes = ancestors,
+ sourceSets = setOf(sourceSetData),
+ isExpectActual = false,
+ extra = PropertyContainer.withAll(
implementedInterfacesExtra,
annotations.toList().toListOfAnnotations().toSourceSetDependent()
.toAnnotations()
)
)
isInterface -> DInterface(
- dri,
- name.orEmpty(),
- documentation,
- null,
- source,
- allFunctions.await(),
- fields.mapNotNull { parseField(it, accessors[it].orEmpty()) },
- classlikes.await(),
- visibility,
- null,
- mapTypeParameters(dri),
- ancestors,
- setOf(sourceSetData),
- false,
- PropertyContainer.withAll(
+ dri = dri,
+ name = name.orEmpty(),
+ documentation = documentation,
+ expectPresentInSet = null,
+ sources = source,
+ functions = allFunctions.await(),
+ properties = allFields.await(),
+ classlikes = classlikes.await(),
+ visibility = visibility,
+ companion = null,
+ generics = mapTypeParameters(dri),
+ supertypes = ancestors,
+ sourceSets = setOf(sourceSetData),
+ isExpectActual = false,
+ extra = PropertyContainer.withAll(
implementedInterfacesExtra,
annotations.toList().toListOfAnnotations().toSourceSetDependent()
.toAnnotations()
)
)
else -> DClass(
- dri,
- name.orEmpty(),
- constructors.map { parseFunction(it, true) },
- allFunctions.await(),
- fields.mapNotNull { parseField(it, accessors[it].orEmpty()) },
- classlikes.await(),
- source,
- visibility,
- null,
- mapTypeParameters(dri),
- ancestors,
- documentation,
- null,
- modifiers,
- setOf(sourceSetData),
- false,
- PropertyContainer.withAll(
+ dri = dri,
+ name = name.orEmpty(),
+ constructors = constructors.map { parseFunction(it, true) },
+ functions = allFunctions.await(),
+ properties = allFields.await(),
+ classlikes = classlikes.await(),
+ sources = source,
+ visibility = visibility,
+ companion = null,
+ generics = mapTypeParameters(dri),
+ supertypes = ancestors,
+ documentation = documentation,
+ expectPresentInSet = null,
+ modifier = modifiers,
+ sourceSets = setOf(sourceSetData),
+ isExpectActual = false,
+ extra = PropertyContainer.withAll(
implementedInterfacesExtra,
annotations.toList().toListOfAnnotations().toSourceSetDependent()
.toAnnotations(),
@@ -372,40 +423,40 @@ class DefaultPsiToDocumentableTranslator(
} ?: DRI.from(psi)
val docs = javadocParser.parseDocumentation(psi)
return DFunction(
- dri,
- psi.name,
- isConstructor,
- psi.parameterList.parameters.map { psiParameter ->
+ dri = dri,
+ name = psi.name,
+ isConstructor = isConstructor,
+ parameters = psi.parameterList.parameters.map { psiParameter ->
DParameter(
- dri.copy(target = dri.target.nextTarget()),
- psiParameter.name,
- DocumentationNode(
+ dri = dri.copy(target = dri.target.nextTarget()),
+ name = psiParameter.name,
+ documentation = DocumentationNode(
listOfNotNull(docs.firstChildOfTypeOrNull<Param> {
it.name == psiParameter.name
})
).toSourceSetDependent(),
- null,
- getBound(psiParameter.type),
- setOf(sourceSetData),
- PropertyContainer.withAll(
+ expectPresentInSet = null,
+ type = getBound(psiParameter.type),
+ sourceSets = setOf(sourceSetData),
+ extra = PropertyContainer.withAll(
psiParameter.annotations.toList().toListOfAnnotations().toSourceSetDependent()
.toAnnotations()
)
)
},
- docs.toSourceSetDependent(),
- null,
- PsiDocumentableSource(psi).toSourceSetDependent(),
- psi.getVisibility().toSourceSetDependent(),
- psi.returnType?.let { getBound(type = it) } ?: Void,
- psi.mapTypeParameters(dri),
- null,
- psi.getModifier().toSourceSetDependent(),
- setOf(sourceSetData),
- false,
- psi.additionalExtras().let {
+ documentation = docs.toSourceSetDependent(),
+ expectPresentInSet = null,
+ sources = PsiDocumentableSource(psi).toSourceSetDependent(),
+ visibility = psi.getVisibility().toSourceSetDependent(),
+ type = psi.returnType?.let { getBound(type = it) } ?: Void,
+ generics = psi.mapTypeParameters(dri),
+ receiver = null,
+ modifier = psi.getModifier().toSourceSetDependent(),
+ sourceSets = setOf(sourceSetData),
+ isExpectActual = false,
+ extra = psi.additionalExtras().let {
PropertyContainer.withAll(
- InheritedMember(inheritedFrom.toSourceSetDependent()),
+ inheritedFrom?.let { InheritedMember(it.toSourceSetDependent()) },
it.toSourceSetDependent().toAdditionalModifiers(),
(psi.annotations.toList()
.toListOfAnnotations() + it.toListOfAnnotations()).toSourceSetDependent()
@@ -437,6 +488,20 @@ class DefaultPsiToDocumentableTranslator(
Annotations.Annotation(DRI("kotlin.jvm", "JvmStatic"), emptyMap())
}
+ /**
+ * Workaround for getting JvmField Kotlin annotation in PSIs
+ */
+ private fun Collection<PsiAnnotation>.findJvmFieldAnnotation(): Annotations.Annotation? {
+ val anyJvmFieldAnnotation = this.any {
+ it.qualifiedName == "$JVM_FIELD_PACKAGE_NAME.$JVM_FIELD_CLASS_NAMES"
+ }
+ return if (anyJvmFieldAnnotation) {
+ Annotations.Annotation(DRI(JVM_FIELD_PACKAGE_NAME, JVM_FIELD_CLASS_NAMES), emptyMap())
+ } else {
+ null
+ }
+ }
+
private fun <T : AnnotationTarget> PsiTypeParameter.annotations(): PropertyContainer<T> = this.annotations.toList().toListOfAnnotations().annotations()
private fun <T : AnnotationTarget> PsiType.annotations(): PropertyContainer<T> = this.annotations.toList().toListOfAnnotations().annotations()
@@ -521,14 +586,14 @@ class DefaultPsiToDocumentableTranslator(
}
return typeParameters.map { type ->
DTypeParameter(
- dri.copy(target = dri.target.nextTarget()),
- type.name.orEmpty(),
- null,
- javadocParser.parseDocumentation(type).toSourceSetDependent(),
- null,
- mapBounds(type.bounds),
- setOf(sourceSetData),
- PropertyContainer.withAll(
+ dri = dri.copy(target = dri.target.nextTarget()),
+ name = type.name.orEmpty(),
+ presentableName = null,
+ documentation = javadocParser.parseDocumentation(type).toSourceSetDependent(),
+ expectPresentInSet = null,
+ bounds = mapBounds(type.bounds),
+ sourceSets = setOf(sourceSetData),
+ extra = PropertyContainer.withAll(
type.annotations.toList().toListOfAnnotations().toSourceSetDependent()
.toAnnotations()
)
@@ -536,53 +601,66 @@ class DefaultPsiToDocumentableTranslator(
}
}
- private fun PsiMethod.getPropertyNameForFunction() =
- getAnnotation(DescriptorUtils.JVM_NAME.asString())?.findAttributeValue("name")?.text
- ?: when {
- JvmAbi.isGetterName(name) -> propertyNameByGetMethodName(Name.identifier(name))?.asString()
- JvmAbi.isSetterName(name) -> propertyNamesBySetMethodName(Name.identifier(name)).firstOrNull()
- ?.asString()
- else -> null
- }
+ private fun parseFieldWithInheritingAccessors(
+ psi: PsiField,
+ accessors: List<Pair<PsiMethod, DRI>>,
+ inheritedFrom: DRI
+ ): DProperty {
+ val getter = accessors
+ .firstOrNull { (method, _) -> method.isGetterFor(psi) }
+ ?.let { (method, dri) -> parseFunction(method, inheritedFrom = dri) }
+
+ val setter = accessors
+ .firstOrNull { (method, _) -> method.isSetterFor(psi) }
+ ?.let { (method, dri) -> parseFunction(method, inheritedFrom = dri) }
+
+ return parseField(
+ psi = psi,
+ getter = getter,
+ setter = setter,
+ inheritedFrom = inheritedFrom
+ )
+ }
- private fun PsiClass.splitFunctionsAndAccessors(): Pair<MutableList<PsiMethod>, MutableMap<PsiField, MutableList<PsiMethod>>> {
- val fieldNames = fields.associateBy { it.name }
- val accessors = mutableMapOf<PsiField, MutableList<PsiMethod>>()
- val regularMethods = mutableListOf<PsiMethod>()
- methods.forEach { method ->
- val field = method.getPropertyNameForFunction()?.let { name -> fieldNames[name] }
- if (field != null) {
- accessors.getOrPut(field, ::mutableListOf).add(method)
- } else {
- regularMethods.add(method)
- }
- }
- return regularMethods to accessors
+ private fun parseField(psi: PsiField, accessors: List<PsiMethod>, inheritedFrom: DRI? = null): DProperty {
+ val getter = accessors.firstOrNull { it.isGetterFor(psi) }?.let { parseFunction(it) }
+ val setter = accessors.firstOrNull { it.isSetterFor(psi) }?.let { parseFunction(it) }
+ return parseField(
+ psi = psi,
+ getter = getter,
+ setter = setter,
+ inheritedFrom = inheritedFrom
+ )
}
- private fun parseField(psi: PsiField, accessors: List<PsiMethod>): DProperty {
+ private fun parseField(psi: PsiField, getter: DFunction?, setter: DFunction?, inheritedFrom: DRI? = null): DProperty {
val dri = DRI.from(psi)
return DProperty(
- dri,
- psi.name,
- javadocParser.parseDocumentation(psi).toSourceSetDependent(),
- null,
- PsiDocumentableSource(psi).toSourceSetDependent(),
- psi.getVisibility().toSourceSetDependent(),
- getBound(psi.type),
- null,
- accessors.firstOrNull { it.hasParameters() }?.let { parseFunction(it) },
- accessors.firstOrNull { it.returnType == psi.type }?.let { parseFunction(it) },
- psi.getModifier().toSourceSetDependent(),
- setOf(sourceSetData),
- emptyList(),
- false,
- psi.additionalExtras().let {
+ dri = dri,
+ name = psi.name,
+ documentation = javadocParser.parseDocumentation(psi).toSourceSetDependent(),
+ expectPresentInSet = null,
+ sources = PsiDocumentableSource(psi).toSourceSetDependent(),
+ visibility = psi.getVisibility().toSourceSetDependent(),
+ type = getBound(psi.type),
+ receiver = null,
+ setter = setter,
+ getter = getter,
+ modifier = psi.getModifier().toSourceSetDependent(),
+ sourceSets = setOf(sourceSetData),
+ generics = emptyList(),
+ isExpectActual = false,
+ extra = psi.additionalExtras().let {
+ val psiAnnotations = psi.annotations.toList()
+ val parsedAnnotations = psiAnnotations.toListOfAnnotations()
+ val extraModifierAnnotations = it.toListOfAnnotations()
+ val jvmFieldAnnotation = psiAnnotations.findJvmFieldAnnotation()
+ val annotations = parsedAnnotations + extraModifierAnnotations + listOfNotNull(jvmFieldAnnotation)
+
PropertyContainer.withAll(
+ inheritedFrom?.let { inheritedFrom -> InheritedMember(inheritedFrom.toSourceSetDependent()) },
it.toSourceSetDependent().toAdditionalModifiers(),
- (psi.annotations.toList()
- .toListOfAnnotations() + it.toListOfAnnotations()).toSourceSetDependent()
- .toAnnotations()
+ annotations.toSourceSetDependent().toAnnotations()
)
}
)
diff --git a/plugins/base/src/main/kotlin/translators/psi/PsiAccessorConventionUtil.kt b/plugins/base/src/main/kotlin/translators/psi/PsiAccessorConventionUtil.kt
new file mode 100644
index 00000000..c2ab8c03
--- /dev/null
+++ b/plugins/base/src/main/kotlin/translators/psi/PsiAccessorConventionUtil.kt
@@ -0,0 +1,58 @@
+package org.jetbrains.dokka.base.translators.psi
+
+import com.intellij.psi.PsiField
+import com.intellij.psi.PsiMethod
+import org.jetbrains.dokka.base.translators.firstNotNullOfOrNull
+import org.jetbrains.kotlin.load.java.JvmAbi
+import org.jetbrains.kotlin.load.java.propertyNameByGetMethodName
+import org.jetbrains.kotlin.load.java.propertyNamesBySetMethodName
+import org.jetbrains.kotlin.name.Name
+import org.jetbrains.kotlin.resolve.DescriptorUtils
+
+
+internal data class PsiFunctionsHolder(
+ val regularFunctions: List<PsiMethod>,
+ val accessors: Map<PsiField, List<PsiMethod>>
+)
+
+internal fun splitFunctionsAndAccessors(fields: Array<PsiField>, methods: Array<PsiMethod>): PsiFunctionsHolder {
+ val fieldsByName = fields.associateBy { it.name }
+ val regularFunctions = mutableListOf<PsiMethod>()
+ val accessors = mutableMapOf<PsiField, MutableList<PsiMethod>>()
+ methods.forEach { method ->
+ val possiblePropertyNamesForFunction = method.getPossiblePropertyNamesForFunction()
+ val field = possiblePropertyNamesForFunction.firstNotNullOfOrNull { fieldsByName[it] }
+ if (field != null && method.isAccessorFor(field)) {
+ accessors.getOrPut(field, ::mutableListOf).add(method)
+ } else {
+ regularFunctions.add(method)
+ }
+ }
+ return PsiFunctionsHolder(regularFunctions, accessors)
+}
+
+internal fun PsiMethod.getPossiblePropertyNamesForFunction(): List<String> {
+ val jvmName = getAnnotation(DescriptorUtils.JVM_NAME.asString())?.findAttributeValue("name")?.text
+ return jvmName?.let { listOf(jvmName) }
+ ?: when {
+ JvmAbi.isGetterName(name) -> listOfNotNull(
+ propertyNameByGetMethodName(Name.identifier(name))?.asString()
+ )
+ JvmAbi.isSetterName(name) -> {
+ propertyNamesBySetMethodName(Name.identifier(name)).map { it.asString() }
+ }
+ else -> listOf()
+ }
+}
+
+internal fun PsiMethod.isAccessorFor(field: PsiField): Boolean {
+ return this.isGetterFor(field) || this.isSetterFor(field)
+}
+
+internal fun PsiMethod.isGetterFor(field: PsiField): Boolean {
+ return this.returnType == field.type && !this.hasParameters()
+}
+
+internal fun PsiMethod.isSetterFor(field: PsiField): Boolean {
+ return parameterList.getParameter(0)?.type == field.type
+}