FIR IDE: rework KtCall to work with error cals

This commit is contained in:
Ilya Kirillov
2020-12-07 17:43:29 +01:00
parent e6327ef490
commit f30c6bf86a
27 changed files with 256 additions and 124 deletions
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.idea.fir.highlighter.visitors
import com.intellij.lang.annotation.AnnotationHolder
import com.intellij.openapi.editor.colors.TextAttributesKey
import org.jetbrains.kotlin.idea.frontend.api.*
import org.jetbrains.kotlin.idea.frontend.api.calls.*
import org.jetbrains.kotlin.idea.frontend.api.symbols.*
import org.jetbrains.kotlin.idea.frontend.api.symbols.markers.KtSymbolKind
import org.jetbrains.kotlin.lexer.KtTokens
@@ -20,25 +21,20 @@ internal class FunctionCallHighlightingVisitor(
holder: AnnotationHolder
) : FirAfterResolveHighlightingVisitor(analysisSession, holder) {
override fun visitBinaryExpression(expression: KtBinaryExpression) = with(analysisSession) {
(expression.operationReference as? KtReferenceExpression)
?.takeIf {
// do not highlight assignment statement
(it as? KtOperationReferenceExpression)?.operationSignTokenType != KtTokens.EQ
}?.let { callee ->
expression.resolveCall()
?.takeIf { callInfo ->
// ignore arithmetic-like operator calls
(callInfo.targetFunction as? KtFunctionSymbol)?.isOperator != true
}
?.let { callInfo ->
getTextAttributesForCal(callInfo)?.let { attributes ->
highlightName(callee, attributes)
}
}
}
val operationReference = expression.operationReference as? KtReferenceExpression ?: return
if (operationReference.isAssignment()) return
val call = expression.resolveCall() ?: return
if (call.isErrorCall) return
if (call.isSuccessCallOf<KtFunctionSymbol> { it.isOperator }) return
getTextAttributesForCal(call)?.let { attributes ->
highlightName(operationReference, attributes)
}
super.visitBinaryExpression(expression)
}
private fun KtReferenceExpression.isAssignment() =
(this as? KtOperationReferenceExpression)?.operationSignTokenType == KtTokens.EQ
override fun visitCallExpression(expression: KtCallExpression) = with(analysisSession) {
expression.calleeExpression
?.takeUnless { it is KtLambdaExpression }
@@ -53,9 +49,9 @@ internal class FunctionCallHighlightingVisitor(
super.visitCallExpression(expression)
}
private fun getTextAttributesForCal(callInfo: CallInfo): TextAttributesKey? = when {
callInfo.isSuspendCall -> Colors.SUSPEND_FUNCTION_CALL
callInfo is FunctionCallInfo -> when (val function = callInfo.targetFunction) {
private fun getTextAttributesForCal(call: KtCall): TextAttributesKey? = when {
call.isSuccessCallOf<KtFunctionSymbol> { it.isSuspend } -> Colors.SUSPEND_FUNCTION_CALL
call is KtFunctionCall -> when (val function = call.targetFunction) {
is KtConstructorSymbol -> Colors.CONSTRUCTOR_CALL
is KtAnonymousFunctionSymbol -> null
is KtFunctionSymbol -> when {
@@ -66,8 +62,8 @@ internal class FunctionCallHighlightingVisitor(
}
else -> Colors.FUNCTION_CALL //TODO ()
}
callInfo is VariableAsFunctionCallInfo -> Colors.VARIABLE_AS_FUNCTION_CALL
callInfo is VariableAsFunctionLikeCallInfo -> Colors.VARIABLE_AS_FUNCTION_LIKE_CALL
call is KtFunctionalTypeVariableCall -> Colors.VARIABLE_AS_FUNCTION_CALL
call is KtVariableWithInvokeFunctionCall -> Colors.VARIABLE_AS_FUNCTION_LIKE_CALL
else -> null
}
}
@@ -1,30 +0,0 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.frontend.api
import org.jetbrains.kotlin.idea.frontend.api.symbols.*
sealed class CallInfo {
abstract val isSuspendCall: Boolean
abstract val targetFunction: KtFunctionLikeSymbol?
}
data class VariableAsFunctionCallInfo(val target: KtVariableLikeSymbol, override val isSuspendCall: Boolean) : CallInfo() {
override val targetFunction: KtFunctionLikeSymbol? = null
}
data class VariableAsFunctionLikeCallInfo(val target: KtVariableLikeSymbol, val invokeFunction: KtFunctionSymbol) : CallInfo() {
override val isSuspendCall: Boolean get() = invokeFunction.isSuspend
override val targetFunction get() = invokeFunction
}
data class FunctionCallInfo(override val targetFunction: KtFunctionLikeSymbol) : CallInfo() {
override val isSuspendCall: Boolean
get() = when (targetFunction) {
is KtFunctionSymbol -> targetFunction.isSuspend
else -> false
}
}
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.idea.frontend.api
import org.jetbrains.kotlin.diagnostics.Diagnostic
import org.jetbrains.kotlin.idea.frontend.api.calls.KtCall
import org.jetbrains.kotlin.idea.frontend.api.components.*
import org.jetbrains.kotlin.idea.frontend.api.scopes.*
import org.jetbrains.kotlin.idea.frontend.api.symbols.*
@@ -118,9 +119,9 @@ abstract class KtAnalysisSession(override val token: ValidityToken) : ValidityTo
fun <S : KtSymbol> KtSymbolPointer<S>.restoreSymbol(): S? = restoreSymbol(this@KtAnalysisSession)
fun KtCallExpression.resolveCall(): CallInfo? = callResolver.resolveCall(this)
fun KtCallExpression.resolveCall(): KtCall? = callResolver.resolveCall(this)
fun KtBinaryExpression.resolveCall(): CallInfo? = callResolver.resolveCall(this)
fun KtBinaryExpression.resolveCall(): KtCall? = callResolver.resolveCall(this)
fun KtReference.resolveToSymbols(): Collection<KtSymbol> {
check(this is KtSymbolBasedReference) { "To get reference symbol the one should be KtSymbolBasedReference" }
@@ -0,0 +1,81 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.frontend.api.calls
import org.jetbrains.kotlin.idea.frontend.api.diagnostics.KtDiagnostic
import org.jetbrains.kotlin.idea.frontend.api.symbols.*
/**
* Represents direct or indirect (via invoke) function call from Kotlin code
*/
sealed class KtCall {
abstract val isErrorCall: Boolean
}
/**
* Call using `()` of some variable of functional type, e.g.,
*
* fun x(f: () -> Int) {
* f() // functional type call
* }
*/
class KtFunctionalTypeVariableCall(val target: KtVariableLikeSymbol) : KtCall() {
override val isErrorCall: Boolean get() = false
}
/**
* Direct or indirect call of function declared by user
*/
sealed class KtDeclaredFunctionCall : KtCall() {
abstract val targetFunction: KtCallTarget
override val isErrorCall: Boolean
get() = targetFunction is KtErrorCallTarget
}
/**
* Call using () on variable on some non-functional type, considers that `invoke` function is declared somewhere
*
* fun x(y: Int) {
* y() // variable with invoke function call
* }
*
* fun Int.invoke() {}
*/
class KtVariableWithInvokeFunctionCall(
val target: KtVariableLikeSymbol,
val invokeFunction: KtCallTarget,
) : KtDeclaredFunctionCall() {
override val targetFunction: KtCallTarget get() = invokeFunction
}
/**
* Simple function call, e.g.,
*
* x.toString() // function call
*/
data class KtFunctionCall(override val targetFunction: KtCallTarget) : KtDeclaredFunctionCall()
/**
* Represents function(s) in which call was resolved,
* Can be success [KtSuccessCallTarget] in this case there only one such function
* Or erroneous [KtErrorCallTarget] in this case there can be any count of candidates
*/
sealed class KtCallTarget {
abstract val candidates: Collection<KtFunctionLikeSymbol>
}
/**
* Success call of [symbol]
*/
class KtSuccessCallTarget(val symbol: KtFunctionLikeSymbol) : KtCallTarget() {
override val candidates: Collection<KtFunctionLikeSymbol>
get() = listOf(symbol)
}
/**
* Function all with errors, possible candidates are [candidates]
*/
class KtErrorCallTarget(override val candidates: Collection<KtFunctionLikeSymbol>, val diagnostic: KtDiagnostic) : KtCallTarget()
@@ -0,0 +1,20 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.frontend.api.calls
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtFunctionLikeSymbol
fun KtCallTarget.getSuccessCallSymbolOrNull(): KtFunctionLikeSymbol? = when (this) {
is KtSuccessCallTarget -> symbol
is KtErrorCallTarget -> null
}
inline fun <reified S : KtFunctionLikeSymbol> KtCall.isSuccessCallOf(predicate: (S) -> Boolean): Boolean {
if (this !is KtFunctionCall) return false
val symbol = targetFunction.getSuccessCallSymbolOrNull() ?: return false
if (symbol !is S) return false
return predicate(symbol)
}
@@ -5,11 +5,11 @@
package org.jetbrains.kotlin.idea.frontend.api.components
import org.jetbrains.kotlin.idea.frontend.api.CallInfo
import org.jetbrains.kotlin.idea.frontend.api.calls.KtCall
import org.jetbrains.kotlin.psi.KtBinaryExpression
import org.jetbrains.kotlin.psi.KtCallExpression
abstract class KtCallResolver : KtAnalysisSessionComponent() {
abstract fun resolveCall(call: KtCallExpression): CallInfo?
abstract fun resolveCall(call: KtBinaryExpression): CallInfo?
abstract fun resolveCall(call: KtCallExpression): KtCall?
abstract fun resolveCall(call: KtBinaryExpression): KtCall?
}
@@ -0,0 +1,12 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.frontend.api.diagnostics
sealed class KtDiagnostic {
abstract val message: String
}
data class KtSimpleDiagnostic(override val message: String): KtDiagnostic()
@@ -8,8 +8,11 @@ package org.jetbrains.kotlin.idea.fir
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
import org.jetbrains.kotlin.fir.expressions.FirFunctionCall
import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression
import org.jetbrains.kotlin.fir.references.FirErrorNamedReference
import org.jetbrains.kotlin.fir.references.FirNamedReference
import org.jetbrains.kotlin.fir.references.FirReference
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
import org.jetbrains.kotlin.fir.resolve.diagnostics.*
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
import org.jetbrains.kotlin.idea.frontend.api.fir.KtSymbolByFirBuilder
@@ -19,10 +22,12 @@ import org.jetbrains.kotlin.util.OperatorNameConventions
fun FirFunctionCall.isImplicitFunctionCall(): Boolean {
if (dispatchReceiver !is FirQualifiedAccessExpression) return false
val resolvedCalleeSymbol = (calleeReference as? FirResolvedNamedReference)?.resolvedSymbol
return (resolvedCalleeSymbol as? FirNamedFunctionSymbol)?.fir?.name == OperatorNameConventions.INVOKE
return calleeReference.getCandidateSymbols().any(FirBasedSymbol<*>::isInvokeFunction)
}
private fun FirBasedSymbol<*>.isInvokeFunction() =
(this as? FirNamedFunctionSymbol)?.fir?.name == OperatorNameConventions.INVOKE
fun FirFunctionCall.getCalleeSymbol(): FirBasedSymbol<*>? =
calleeReference.getResolvedSymbolOfNameReference()
@@ -33,3 +38,19 @@ internal fun FirReference.getResolvedKtSymbolOfNameReference(builder: KtSymbolBy
(getResolvedSymbolOfNameReference()?.fir as? FirDeclaration)?.let { firDeclaration ->
builder.buildSymbol(firDeclaration)
}
internal fun FirErrorNamedReference.getCandidateSymbols(): Collection<FirBasedSymbol<*>> =
when (val diagnostic = diagnostic) {
is ConeInapplicableCandidateError -> listOf(diagnostic.candidateSymbol)
is ConeHiddenCandidateError -> listOf(diagnostic.candidateSymbol)
is ConeAmbiguityError -> diagnostic.candidates
is ConeOperatorAmbiguityError -> diagnostic.candidates
is ConeUnsupportedCallableReferenceTarget -> listOf(diagnostic.fir.symbol)
else -> emptyList()
}
internal fun FirNamedReference.getCandidateSymbols(): Collection<FirBasedSymbol<*>> = when(this) {
is FirResolvedNamedReference -> listOf(resolvedSymbol)
is FirErrorNamedReference -> getCandidateSymbols()
else -> emptyList()
}
@@ -191,7 +191,11 @@ internal class KtSymbolByFirBuilder private constructor(
}
fun buildKtType(coneType: FirTypeRef): KtType = buildKtType(coneType.coneTypeUnsafe<ConeKotlinType>())
fun buildKtType(coneType: FirTypeRef): KtType =
buildKtType(
coneType.coneTypeSafe<ConeKotlinType>()
?: error("")
)
fun buildKtType(coneType: ConeKotlinType): KtType = typesCache.cache(coneType) {
when (coneType) {
@@ -6,84 +6,102 @@
package org.jetbrains.kotlin.idea.frontend.api.fir.components
import org.jetbrains.kotlin.builtins.StandardNames
import org.jetbrains.kotlin.fir.declarations.isSuspend
import org.jetbrains.kotlin.fir.expressions.FirFunctionCall
import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression
import org.jetbrains.kotlin.fir.expressions.FirSafeCallExpression
import org.jetbrains.kotlin.fir.references.FirErrorNamedReference
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
import org.jetbrains.kotlin.fir.symbols.CallableId
import org.jetbrains.kotlin.fir.symbols.impl.FirConstructorSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
import org.jetbrains.kotlin.idea.fir.getCandidateSymbols
import org.jetbrains.kotlin.idea.fir.isImplicitFunctionCall
import org.jetbrains.kotlin.idea.fir.low.level.api.api.getOrBuildFir
import org.jetbrains.kotlin.idea.fir.low.level.api.api.getOrBuildFirSafe
import org.jetbrains.kotlin.idea.frontend.api.*
import org.jetbrains.kotlin.idea.frontend.api.calls.*
import org.jetbrains.kotlin.idea.frontend.api.components.KtCallResolver
import org.jetbrains.kotlin.idea.frontend.api.diagnostics.KtSimpleDiagnostic
import org.jetbrains.kotlin.idea.frontend.api.fir.KtFirAnalysisSession
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtFunctionSymbol
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtSymbol
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtVariableLikeSymbol
import org.jetbrains.kotlin.idea.frontend.api.fir.buildSymbol
import org.jetbrains.kotlin.idea.frontend.api.symbols.*
import org.jetbrains.kotlin.idea.references.FirReferenceResolveHelper
import org.jetbrains.kotlin.idea.references.FirReferenceResolveHelper.toTargetSymbol
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.KtBinaryExpression
import org.jetbrains.kotlin.psi.KtCallExpression
import org.jetbrains.kotlin.psi.KtExpression
internal class KtFirCallResolver(
override val analysisSession: KtFirAnalysisSession,
override val token: ValidityToken,
) : KtCallResolver(), KtFirAnalysisSessionComponent {
override fun resolveCall(call: KtBinaryExpression): CallInfo? = withValidityAssertion {
override fun resolveCall(call: KtBinaryExpression): KtCall? = withValidityAssertion {
val firCall = call.getOrBuildFirSafe<FirFunctionCall>(firResolveState) ?: return null
resolveCall(firCall, call)
resolveCall(firCall)
}
override fun resolveCall(call: KtCallExpression): CallInfo? = withValidityAssertion {
override fun resolveCall(call: KtCallExpression): KtCall? = withValidityAssertion {
val firCall = when (val fir = call.getOrBuildFir(firResolveState)) {
is FirFunctionCall -> fir
is FirSafeCallExpression -> fir.regularQualifiedAccess as? FirFunctionCall
else -> null
} ?: return null
return resolveCall(firCall, call)
return resolveCall(firCall)
}
private fun resolveCall(firCall: FirFunctionCall, callExpression: KtExpression): CallInfo? {
private fun resolveCall(firCall: FirFunctionCall): KtCall? {
val session = firResolveState.rootModuleSession
val resolvedFunctionSymbol = firCall.calleeReference.toTargetSymbol(session, firSymbolBuilder)
val resolvedCalleeSymbol = (firCall.calleeReference as? FirResolvedNamedReference)?.resolvedSymbol
return when {
resolvedCalleeSymbol is FirConstructorSymbol -> {
val fir = resolvedCalleeSymbol.fir
FunctionCallInfo(firSymbolBuilder.buildConstructorSymbol(fir))
}
firCall.dispatchReceiver is FirQualifiedAccessExpression && firCall.isImplicitFunctionCall() -> {
firCall.isImplicitFunctionCall() -> {
val target = with(FirReferenceResolveHelper) {
val calleeReference = (firCall.dispatchReceiver as FirQualifiedAccessExpression).calleeReference
calleeReference.toTargetSymbol(session, firSymbolBuilder)
calleeReference.toTargetSymbol(session, firSymbolBuilder).singleOrNull()
}
when (target) {
is KtVariableLikeSymbol -> firCall.createCallByVariableLikeSymbolCall(target)
null -> null
is KtVariableLikeSymbol -> {
val functionSymbol =
(firCall.calleeReference as? FirResolvedNamedReference)?.resolvedSymbol as? FirNamedFunctionSymbol
when (functionSymbol?.callableId) {
null -> null
in kotlinFunctionInvokeCallableIds -> VariableAsFunctionCallInfo(target, functionSymbol.fir.isSuspend)
else -> (resolvedFunctionSymbol as? KtFunctionSymbol)
?.let { VariableAsFunctionLikeCallInfo(target, it) }
}
}
else -> resolvedFunctionSymbol?.asSimpleFunctionCall()
else -> firCall.asSimpleFunctionCall()
}
}
else -> resolvedFunctionSymbol?.asSimpleFunctionCall()
else -> firCall.asSimpleFunctionCall()
}
}
private fun KtSymbol.asSimpleFunctionCall() =
(this as? KtFunctionSymbol)?.let(::FunctionCallInfo)
private fun FirFunctionCall.createCallByVariableLikeSymbolCall(variableLikeSymbol: KtVariableLikeSymbol) =
when (val callReference = calleeReference) {
is FirResolvedNamedReference -> {
val functionSymbol = callReference.resolvedSymbol as? FirNamedFunctionSymbol
when (functionSymbol?.callableId) {
null -> null
in kotlinFunctionInvokeCallableIds -> KtFunctionalTypeVariableCall(variableLikeSymbol)
else -> (callReference.resolvedSymbol.fir.buildSymbol(firSymbolBuilder) as? KtFunctionSymbol)
?.let { KtVariableWithInvokeFunctionCall(variableLikeSymbol, KtSuccessCallTarget(it)) }
}
}
is FirErrorNamedReference -> KtVariableWithInvokeFunctionCall(
variableLikeSymbol,
callReference.createErrorCallTarget()
)
else -> error("Unexpected call reference ${callReference::class.simpleName}")
}
private fun FirFunctionCall.asSimpleFunctionCall(): KtFunctionCall? {
val target = when (val calleeReference = calleeReference) {
is FirResolvedNamedReference -> calleeReference.getKtFunctionOrConstructorSymbol()?.let { KtSuccessCallTarget(it) }
is FirErrorNamedReference -> calleeReference.createErrorCallTarget()
else -> error("Unexpected call reference ${calleeReference::class.simpleName}")
} ?: return null
return KtFunctionCall(target)
}
private fun FirErrorNamedReference.createErrorCallTarget(): KtErrorCallTarget =
KtErrorCallTarget(
getCandidateSymbols().mapNotNull { it.fir.buildSymbol(firSymbolBuilder) as? KtFunctionLikeSymbol },
KtSimpleDiagnostic(diagnostic.reason)
)
private fun FirResolvedNamedReference.getKtFunctionOrConstructorSymbol(): KtFunctionLikeSymbol? =
resolvedSymbol.fir.buildSymbol(firSymbolBuilder) as? KtFunctionLikeSymbol
companion object {
private val kotlinFunctionInvokeCallableIds = (0..23).flatMapTo(hashSetOf()) { arity ->
@@ -61,7 +61,7 @@ internal object FirReferenceResolveHelper {
return classLikeDeclaration?.buildSymbol(symbolBuilder)
}
fun FirReference.toTargetSymbol(session: FirSession, symbolBuilder: KtSymbolByFirBuilder): KtSymbol? {
fun FirReference.toTargetSymbol(session: FirSession, symbolBuilder: KtSymbolByFirBuilder): Collection<KtSymbol> {
return when (this) {
is FirResolvedNamedReference -> {
val fir = when (val symbol = resolvedSymbol) {
@@ -75,20 +75,21 @@ internal object FirReferenceResolveHelper {
}
else -> symbol.fir as? FirDeclaration
}
fir?.buildSymbol(symbolBuilder)
listOfNotNull(fir?.buildSymbol(symbolBuilder))
}
is FirResolvedCallableReference -> {
resolvedSymbol.fir.buildSymbol(symbolBuilder)
listOfNotNull(resolvedSymbol.fir.buildSymbol(symbolBuilder))
}
is FirThisReference -> {
boundSymbol?.fir?.buildSymbol(symbolBuilder)
listOfNotNull(boundSymbol?.fir?.buildSymbol(symbolBuilder))
}
is FirSuperReference -> {
(superTypeRef as? FirResolvedTypeRef)?.toTargetSymbol(session, symbolBuilder)
listOfNotNull((superTypeRef as? FirResolvedTypeRef)?.toTargetSymbol(session, symbolBuilder))
}
else -> {
null
is FirErrorNamedReference -> {
getCandidateSymbols().mapNotNull { it.fir.buildSymbol(symbolBuilder) }
}
else -> emptyList()
}
}
@@ -195,7 +196,7 @@ internal object FirReferenceResolveHelper {
expression: KtSimpleNameExpression,
session: FirSession,
symbolBuilder: KtSymbolByFirBuilder
): List<KtSymbol> {
): Collection<KtSymbol> {
val calleeReference =
if (fir is FirFunctionCall
&& fir.isImplicitFunctionCall()
@@ -207,7 +208,7 @@ internal object FirReferenceResolveHelper {
// }
(fir.dispatchReceiver as FirQualifiedAccessExpression).calleeReference
} else fir.calleeReference
return listOfNotNull(calleeReference.toTargetSymbol(session, symbolBuilder))
return calleeReference.toTargetSymbol(session, symbolBuilder)
}
private fun getSymbolsByErrorNamedReference(
@@ -6,7 +6,7 @@
package org.jetbrains.kotlin.idea.references
import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession
import org.jetbrains.kotlin.idea.frontend.api.VariableAsFunctionLikeCallInfo
import org.jetbrains.kotlin.idea.frontend.api.calls.KtVariableWithInvokeFunctionCall
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtSymbol
import org.jetbrains.kotlin.psi.KtCallExpression
import org.jetbrains.kotlin.psi.KtExpression
@@ -18,8 +18,8 @@ class KtFirInvokeFunctionReference(expression: KtCallExpression) : KtInvokeFunct
override fun KtAnalysisSession.resolveToSymbols(): Collection<KtSymbol> {
val call = expression.resolveCall() ?: return emptyList()
if (call is VariableAsFunctionLikeCallInfo) {
return listOf(call.invokeFunction)
if (call is KtVariableWithInvokeFunctionCall) {
return call.invokeFunction.candidates
}
return emptyList()
}
@@ -4,4 +4,4 @@ fun call() {
<selection>function(1)</selection>
}
// CALL: FunctionCallInfo: targetFunction = function(a: kotlin.Int): kotlin.Unit
// CALL: KtFunctionCall: targetFunction = function(a: kotlin.Int): kotlin.Unit
@@ -4,4 +4,4 @@ fun call() {
"str".<selection>function(1)</selection>
}
// CALL: FunctionCallInfo: targetFunction = function(<receiver>: kotlin.String, a: kotlin.Int): kotlin.Unit
// CALL: KtFunctionCall: targetFunction = function(<receiver>: kotlin.String, a: kotlin.Int): kotlin.Unit
@@ -4,4 +4,4 @@ fun call() {
"str"?.<selection>function(1)</selection>
}
// CALL: FunctionCallInfo: targetFunction = function(<receiver>: kotlin.String, a: kotlin.Int): kotlin.Unit
// CALL: KtFunctionCall: targetFunction = function(<receiver>: kotlin.String, a: kotlin.Int): kotlin.Unit
@@ -4,4 +4,4 @@ fun call() {
val a = <selection>A()</selection>
}
// CALL: FunctionCallInfo: targetFunction = <constructor>(): A
// CALL: KtFunctionCall: targetFunction = <constructor>(): A
@@ -3,4 +3,4 @@ fun call() {
val a = <selection>A()</selection>
}
// CALL: FunctionCallInfo: targetFunction = <constructor>(): A
// CALL: KtFunctionCall: targetFunction = <constructor>(): A
@@ -3,4 +3,4 @@ fun call() {
javaClass.<selection>javaMethod()</selection>
}
// CALL: FunctionCallInfo: targetFunction = JavaClass.javaMethod(): kotlin.Unit
// CALL: KtFunctionCall: targetFunction = JavaClass.javaMethod(): kotlin.Unit
@@ -0,0 +1,7 @@
fun x() {
<selection>foo(1)</selection>
}
fun foo(){}
// CALL: KtFunctionCall: targetFunction = ERR<Inapplicable(INAPPLICABLE_ARGUMENTS_MAPPING_ERROR): /foo, [foo(): kotlin.Unit]>
@@ -2,4 +2,4 @@ fun call(x: (Int) -> String) {
<selection>x(1)</selection>
}
// CALL: VariableAsFunctionCallInfo: target = x: kotlin.Function1<kotlin.Int, kotlin.String>, isSuspendCall = false
// CALL: KtFunctionalTypeVariableCall: target = x: kotlin.Function1<kotlin.Int, kotlin.String>
@@ -4,4 +4,4 @@ fun call(x: kotlin.int) {
<selection>x()</selection>
}
// CALL: FunctionCallInfo: targetFunction = invoke(<receiver>: kotlin.Int): kotlin.String
// CALL: KtFunctionCall: targetFunction = invoke(<receiver>: kotlin.Int): kotlin.String
@@ -11,8 +11,10 @@ import com.intellij.psi.PsiElement
import com.intellij.testFramework.LightCodeInsightTestCase
import org.jetbrains.kotlin.idea.addExternalTestFiles
import org.jetbrains.kotlin.idea.executeOnPooledThreadInReadAction
import org.jetbrains.kotlin.idea.frontend.api.CallInfo
import org.jetbrains.kotlin.idea.frontend.api.analyze
import org.jetbrains.kotlin.idea.frontend.api.calls.KtCall
import org.jetbrains.kotlin.idea.frontend.api.calls.KtErrorCallTarget
import org.jetbrains.kotlin.idea.frontend.api.calls.KtSuccessCallTarget
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtFunctionLikeSymbol
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtFunctionSymbol
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtParameterSymbol
@@ -86,9 +88,9 @@ abstract class AbstractResolveCallTest : @Suppress("DEPRECATION") LightCodeInsig
)
}
private fun CallInfo.stringRepresentation(): String {
private fun KtCall.stringRepresentation(): String {
fun KtType.render() = asStringForDebugging().replace('/', '.')
fun Any.stringValue(): String? = when (this) {
fun Any.stringValue(): String = when (this) {
is KtFunctionLikeSymbol -> buildString {
append(if (this@stringValue is KtFunctionSymbol) callableIdIfNonLocal ?: name else "<constructor>")
append("(")
@@ -103,6 +105,8 @@ private fun CallInfo.stringRepresentation(): String {
append(": ${type.render()}")
}
is KtParameterSymbol -> "$name: ${type.render()}"
is KtSuccessCallTarget -> symbol.stringValue()
is KtErrorCallTarget -> "ERR<${this.diagnostic.message}, [${candidates.joinToString { it.stringValue() }}]>"
is Boolean -> toString()
else -> error("unexpected parameter type ${this::class}")
}
@@ -58,6 +58,11 @@ public class ResolveCallTestGenerated extends AbstractResolveCallTest {
runTest("idea/idea-frontend-fir/testData/analysisSession/resolveCall/javaFunctionCall.kt");
}
@TestMetadata("simpleCallWithNonMatchingArgs.kt")
public void testSimpleCallWithNonMatchingArgs() throws Exception {
runTest("idea/idea-frontend-fir/testData/analysisSession/resolveCall/simpleCallWithNonMatchingArgs.kt");
}
@TestMetadata("variableAsFunction.kt")
public void testVariableAsFunction() throws Exception {
runTest("idea/idea-frontend-fir/testData/analysisSession/resolveCall/variableAsFunction.kt");
@@ -1,5 +1,3 @@
// IGNORE_FIR
class Foo {
fun invoke(vararg a: Any) {}
}
@@ -1,5 +1,3 @@
// IGNORE_FIR
class Foo {
fun invoke(vararg a: Any) {}
}
@@ -1,5 +1,3 @@
// IGNORE_FIR
class Foo {
fun invoke(vararg a: Any) {}
}
@@ -1,5 +1,3 @@
// IGNORE_FIR
class Foo {
fun invoke(vararg a: Any) {}
}