Analysis API: Return KtCallCandidateInfo instead of KtCallInfo in

KtCallResolver.collectAllCandidates().
This commit is contained in:
Mark Punzalan
2022-02-07 23:18:25 +00:00
committed by Ilya Kirillov
parent b8cdfc5aad
commit 3f3873dc50
73 changed files with 1017 additions and 947 deletions
@@ -7,7 +7,7 @@ package org.jetbrains.kotlin.analysis.api.impl.base.test.components
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
import org.jetbrains.kotlin.analysis.api.calls.KtCallInfo
import org.jetbrains.kotlin.analysis.api.calls.KtCallCandidateInfo
import org.jetbrains.kotlin.analysis.api.impl.barebone.test.expressionMarkerProvider
import org.jetbrains.kotlin.analysis.api.impl.base.test.test.framework.AbstractHLApiSingleModuleTest
import org.jetbrains.kotlin.psi.*
@@ -26,14 +26,17 @@ abstract class AbstractResolveCandidatesTest : AbstractHLApiSingleModuleTest() {
if (candidates.isEmpty()) {
"NO_CANDIDATES"
} else {
candidates.joinToString("\n\n") { stringRepresentation(it) }
val sortedCandidates = candidates.sortedWith { candidate1, candidate2 ->
compareCalls(candidate1.candidate, candidate2.candidate)
}
sortedCandidates.joinToString("\n\n") { stringRepresentation(it) }
}
}
}
testServices.assertions.assertEqualsToTestDataFileSibling(actual)
}
private fun KtAnalysisSession.collectCallCandidates(element: PsiElement): List<KtCallInfo> = when (element) {
private fun KtAnalysisSession.collectCallCandidates(element: PsiElement): List<KtCallCandidateInfo> = when (element) {
is KtValueArgument -> this@collectCallCandidates.collectCallCandidates(element.getArgumentExpression()!!)
is KtDeclarationModifierList -> {
val annotationEntry = element.annotationEntries.singleOrNull()
@@ -8,7 +8,6 @@ package org.jetbrains.kotlin.analysis.api.impl.base.test.components
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
import org.jetbrains.kotlin.analysis.api.calls.KtCall
import org.jetbrains.kotlin.analysis.api.calls.KtCallInfo
import org.jetbrains.kotlin.analysis.api.calls.KtCallableMemberCall
import org.jetbrains.kotlin.analysis.api.diagnostics.KtDiagnostic
import org.jetbrains.kotlin.analysis.api.impl.base.KtMapBackedSubstitutor
@@ -20,93 +19,90 @@ import kotlin.reflect.KProperty1
import kotlin.reflect.KVisibility
import kotlin.reflect.full.memberProperties
internal fun KtAnalysisSession.stringRepresentation(call: KtCallInfo): String {
fun Any.stringValue(): String {
fun KtType.render() = asStringForDebugging().replace('/', '.')
fun String.indented() = replace("\n", "\n ")
return when (this) {
is KtFunctionLikeSymbol -> buildString {
append(
when (this@stringValue) {
is KtFunctionSymbol -> callableIdIfNonLocal ?: name
is KtSamConstructorSymbol -> callableIdIfNonLocal ?: name
is KtConstructorSymbol -> "<constructor>"
is KtPropertyGetterSymbol -> callableIdIfNonLocal ?: "<getter>"
is KtPropertySetterSymbol -> callableIdIfNonLocal ?: "<setter>"
else -> error("unexpected symbol kind in KtCall: ${this@stringValue::class.java}")
}
)
append("(")
(this@stringValue as? KtFunctionSymbol)?.receiverType?.let { receiver ->
append("<extension receiver>: ${receiver.render()}")
if (valueParameters.isNotEmpty()) append(", ")
internal fun KtAnalysisSession.stringRepresentation(any: Any): String = with(any) {
fun KtType.render() = asStringForDebugging().replace('/', '.')
fun String.indented() = replace("\n", "\n ")
return when (this) {
is KtFunctionLikeSymbol -> buildString {
append(
when (this@with) {
is KtFunctionSymbol -> callableIdIfNonLocal ?: name
is KtSamConstructorSymbol -> callableIdIfNonLocal ?: name
is KtConstructorSymbol -> "<constructor>"
is KtPropertyGetterSymbol -> callableIdIfNonLocal ?: "<getter>"
is KtPropertySetterSymbol -> callableIdIfNonLocal ?: "<setter>"
else -> error("unexpected symbol kind in KtCall: ${this@with::class.java}")
}
)
append("(")
(this@with as? KtFunctionSymbol)?.receiverType?.let { receiver ->
append("<extension receiver>: ${receiver.render()}")
if (valueParameters.isNotEmpty()) append(", ")
}
@Suppress("DEPRECATION")
(this@stringValue as? KtCallableSymbol)?.getDispatchReceiverType()?.let { dispatchReceiverType ->
append("<dispatch receiver>: ${dispatchReceiverType.render()}")
if (valueParameters.isNotEmpty()) append(", ")
}
valueParameters.joinTo(this) { it.stringValue() }
append(")")
append(": ${returnType.render()}")
@Suppress("DEPRECATION")
(this@with as? KtCallableSymbol)?.getDispatchReceiverType()?.let { dispatchReceiverType ->
append("<dispatch receiver>: ${dispatchReceiverType.render()}")
if (valueParameters.isNotEmpty()) append(", ")
}
is KtValueParameterSymbol -> "${if (isVararg) "vararg " else ""}$name: ${returnType.render()}"
is KtTypeParameterSymbol -> this.nameOrAnonymous.asString()
is KtVariableSymbol -> "${if (isVal) "val" else "var"} $name: ${returnType.render()}"
is KtSymbol -> DebugSymbolRenderer.render(this)
is Boolean -> toString()
is Map<*, *> -> if (isEmpty()) "{}" else entries.joinToString(
separator = ",\n ",
prefix = "{\n ",
postfix = "\n}"
) { (k, v) -> "${k?.stringValue()?.indented()} -> (${v?.stringValue()?.indented()})" }
is Collection<*> -> if (isEmpty()) "[]" else joinToString(
separator = ",\n ",
prefix = "[\n ",
postfix = "\n]"
) {
it?.stringValue()?.indented() ?: "null"
}
is PsiElement -> this.text
is KtSubstitutor.Empty -> "<empty substitutor>"
is KtMapBackedSubstitutor -> {
val mappingText = getAsMap().entries
.joinToString(prefix = "{", postfix = "}") { (k, v) -> k.stringValue() + " = " + v.asStringForDebugging() }
"<map substitutor: $mappingText>"
}
is KtSubstitutor -> "<complex substitutor>"
is KtDiagnostic -> "$severity<$factoryName: $defaultMessage>"
is KtType -> render()
is Enum<*> -> name
is Name -> asString()
else -> buildString {
val clazz = this@stringValue::class
val className = clazz.simpleName!!
append(className)
appendLine(":")
clazz.memberProperties
.filter { it.name != "token" && it.visibility == KVisibility.PUBLIC }
.joinTo(this, separator = "\n ", prefix = " ") { property ->
val name = property.name
valueParameters.joinTo(this) { stringRepresentation(it) }
append(")")
append(": ${returnType.render()}")
}
is KtValueParameterSymbol -> "${if (isVararg) "vararg " else ""}$name: ${returnType.render()}"
is KtTypeParameterSymbol -> this.nameOrAnonymous.asString()
is KtVariableSymbol -> "${if (isVal) "val" else "var"} $name: ${returnType.render()}"
is KtSymbol -> DebugSymbolRenderer.render(this)
is Boolean -> toString()
is Map<*, *> -> if (isEmpty()) "{}" else entries.joinToString(
separator = ",\n ",
prefix = "{\n ",
postfix = "\n}"
) { (k, v) -> "${k?.let { stringRepresentation(it).indented() }} -> (${v?.let { stringRepresentation(it).indented() }})" }
is Collection<*> -> if (isEmpty()) "[]" else joinToString(
separator = ",\n ",
prefix = "[\n ",
postfix = "\n]"
) {
it?.let { stringRepresentation(it).indented() } ?: "null"
}
is PsiElement -> this.text
is KtSubstitutor.Empty -> "<empty substitutor>"
is KtMapBackedSubstitutor -> {
val mappingText = getAsMap().entries
.joinToString(prefix = "{", postfix = "}") { (k, v) -> stringRepresentation(k) + " = " + v.asStringForDebugging() }
"<map substitutor: $mappingText>"
}
is KtSubstitutor -> "<complex substitutor>"
is KtDiagnostic -> "$severity<$factoryName: $defaultMessage>"
is KtType -> render()
is Enum<*> -> name
is Name -> asString()
else -> buildString {
val clazz = this@with::class
val className = clazz.simpleName!!
append(className)
appendLine(":")
clazz.memberProperties
.filter { it.name != "token" && it.visibility == KVisibility.PUBLIC }
.joinTo(this, separator = "\n ", prefix = " ") { property ->
val name = property.name
@Suppress("UNCHECKED_CAST")
val value = (property as KProperty1<Any, *>).get(this@stringValue)?.let {
if (className == "KtErrorCallInfo" && name == "candidateCalls") {
// The order of calls in KtErrorCallInfo.candidateCalls is non-deterministic. Sort by symbol string value.
(it as Collection<KtCall>).sortedWith { call1, call2 ->
if (call1 is KtCallableMemberCall<*, *> && call2 is KtCallableMemberCall<*, *>) {
call1.partiallyAppliedSymbol.stringValue().compareTo(call2.partiallyAppliedSymbol.stringValue())
} else 0
}
} else it
}
val valueAsString = value?.stringValue()?.indented()
"$name = $valueAsString"
@Suppress("UNCHECKED_CAST")
val value = (property as KProperty1<Any, *>).get(this@with)?.let {
if (className == "KtErrorCallInfo" && name == "candidateCalls") {
(it as Collection<KtCall>).sortedWith { call1, call2 -> compareCalls(call1, call2) }
} else it
}
}
val valueAsString = value?.let { stringRepresentation(it).indented() }
"$name = $valueAsString"
}
}
}
}
return call.stringValue()
internal fun KtAnalysisSession.compareCalls(call1: KtCall, call2: KtCall): Int {
// The order of candidate calls is non-deterministic. Sort by symbol string value.
if (call1 !is KtCallableMemberCall<*, *> || call2 !is KtCallableMemberCall<*, *>) return 0
return stringRepresentation(call1.partiallyAppliedSymbol).compareTo(stringRepresentation(call2.partiallyAppliedSymbol))
}