FIR IDE: rework KtCall to work with error cals
This commit is contained in:
+17
-21
@@ -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
|
||||
}
|
||||
}
|
||||
+3
-2
@@ -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()
|
||||
+20
@@ -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)
|
||||
}
|
||||
+3
-3
@@ -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?
|
||||
}
|
||||
+12
@@ -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()
|
||||
}
|
||||
|
||||
+5
-1
@@ -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) {
|
||||
|
||||
+52
-34
@@ -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 ->
|
||||
|
||||
+10
-9
@@ -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(
|
||||
|
||||
+3
-3
@@ -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()
|
||||
}
|
||||
|
||||
+1
-1
@@ -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
|
||||
+1
-1
@@ -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
|
||||
Vendored
+1
-1
@@ -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
|
||||
+1
-1
@@ -4,4 +4,4 @@ fun call() {
|
||||
val a = <selection>A()</selection>
|
||||
}
|
||||
|
||||
// CALL: FunctionCallInfo: targetFunction = <constructor>(): A
|
||||
// CALL: KtFunctionCall: targetFunction = <constructor>(): A
|
||||
+1
-1
@@ -3,4 +3,4 @@ fun call() {
|
||||
val a = <selection>A()</selection>
|
||||
}
|
||||
|
||||
// CALL: FunctionCallInfo: targetFunction = <constructor>(): A
|
||||
// CALL: KtFunctionCall: targetFunction = <constructor>(): A
|
||||
+1
-1
@@ -3,4 +3,4 @@ fun call() {
|
||||
javaClass.<selection>javaMethod()</selection>
|
||||
}
|
||||
|
||||
// CALL: FunctionCallInfo: targetFunction = JavaClass.javaMethod(): kotlin.Unit
|
||||
// CALL: KtFunctionCall: targetFunction = JavaClass.javaMethod(): kotlin.Unit
|
||||
Vendored
+7
@@ -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]>
|
||||
+1
-1
@@ -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>
|
||||
+1
-1
@@ -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
|
||||
+7
-3
@@ -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}")
|
||||
}
|
||||
|
||||
+5
@@ -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) {}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user