/
PsiAccessorConventionUtil.kt
81 lines (70 loc) · 3.01 KB
/
PsiAccessorConventionUtil.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
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)
}
}
val accessorLookalikes = removeNonAccessorsReturning(accessors)
regularFunctions.addAll(accessorLookalikes)
return PsiFunctionsHolder(regularFunctions, accessors)
}
/**
* If a field has no getter, it's not accessible as a property from Kotlin's perspective,
* but it still might have a setter. In this case, this "setter" should be just a regular function
*/
private fun removeNonAccessorsReturning(
fieldAccessors: MutableMap<PsiField, MutableList<PsiMethod>>
): List<PsiMethod> {
val nonAccessors = mutableListOf<PsiMethod>()
fieldAccessors.entries.removeIf { (field, methods) ->
if (methods.size == 1 && methods[0].isSetterFor(field)) {
nonAccessors.add(methods[0])
true
} else {
false
}
}
return nonAccessors
}
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
}