[FIR] Implement FUNCTION_EXPECTED
This commit is contained in:
+1
-1
@@ -32,7 +32,7 @@ FILE: threeReceivers.kt
|
||||
}
|
||||
|
||||
public final fun R|FooBar|.chk(buz: R|Buz|): R|kotlin/Unit| {
|
||||
R|<local>/buz|.<Unresolved name: foobar>#()
|
||||
R|<local>/buz|.<Expression 'foobar' of type 'Bar' cannot be invoked as a function>#()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
-1
@@ -17,6 +17,6 @@ class Foo {
|
||||
// this@Foo is dispatch receiver of foobar
|
||||
// Foo/foobar is dispatch receiver of invoke
|
||||
// this@chk is extension receiver of invoke
|
||||
buz.<!UNRESOLVED_REFERENCE!>foobar<!>()
|
||||
buz.<!FUNCTION_EXPECTED!>foobar<!>()
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -28,5 +28,5 @@ FILE: test.kt
|
||||
lval res1: R|kotlin/String| = R|<local>/descriptor|.R|/WrappedPropertyDescriptor.setter|
|
||||
lval res2: <ERROR TYPE REF: Unresolved name: getSetter> = R|<local>/descriptor|.<Unresolved name: getSetter>#()
|
||||
lval res3: R|kotlin/Boolean| = R|<local>/descriptor|.R|/WrappedPropertyDescriptor.isDelegated|
|
||||
lval res4: <ERROR TYPE REF: Unresolved name: isDelegated> = R|<local>/descriptor|.<Unresolved name: isDelegated>#()
|
||||
lval res4: <ERROR TYPE REF: Expression 'isDelegated' of type 'kotlin/Boolean' cannot be invoked as a function> = R|<local>/descriptor|.<Expression 'isDelegated' of type 'kotlin/Boolean' cannot be invoked as a function>#()
|
||||
}
|
||||
|
||||
+1
-1
@@ -22,5 +22,5 @@ fun test() {
|
||||
val res1 = descriptor.setter
|
||||
val res2 = descriptor.<!UNRESOLVED_REFERENCE!>getSetter<!>() // Should be error
|
||||
val res3 = descriptor.isDelegated
|
||||
val res4 = descriptor.<!UNRESOLVED_REFERENCE!>isDelegated<!>() // Should be error
|
||||
val res4 = descriptor.<!FUNCTION_EXPECTED!>isDelegated<!>() // Should be error
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
FILE: main.kt
|
||||
public final fun test(ann: R|Ann|): R|kotlin/Unit| {
|
||||
R|<local>/ann|.R|/Ann.value|
|
||||
R|<local>/ann|.<Unresolved name: value>#()
|
||||
R|<local>/ann|.<Expression 'value' of type 'kotlin/String' cannot be invoked as a function>#()
|
||||
}
|
||||
|
||||
Vendored
+2
-2
@@ -3,12 +3,12 @@
|
||||
// FILE: Ann.java
|
||||
|
||||
public @interface Ann {
|
||||
String value()
|
||||
String value();
|
||||
}
|
||||
|
||||
// FILE: main.kt
|
||||
|
||||
fun test(ann: Ann) {
|
||||
ann.value
|
||||
ann.<!UNRESOLVED_REFERENCE!>value<!>() // should be an error
|
||||
ann.<!FUNCTION_EXPECTED!>value<!>() // should be an error
|
||||
}
|
||||
|
||||
+4
@@ -120,6 +120,10 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
|
||||
}
|
||||
val ILLEGAL_SELECTOR by error<PsiElement>()
|
||||
val NO_RECEIVER_ALLOWED by error<PsiElement>()
|
||||
val FUNCTION_EXPECTED by error<PsiElement>(PositioningStrategy.REFERENCED_NAME_BY_QUALIFIED) {
|
||||
parameter<String>("expression")
|
||||
parameter<ConeKotlinType>("type")
|
||||
}
|
||||
}
|
||||
|
||||
val SUPER by object : DiagnosticGroup("Super") {
|
||||
|
||||
@@ -133,6 +133,7 @@ object FirErrors {
|
||||
val FUNCTION_CALL_EXPECTED by error2<PsiElement, String, Boolean>(SourceElementPositioningStrategies.REFERENCED_NAME_BY_QUALIFIED)
|
||||
val ILLEGAL_SELECTOR by error0<PsiElement>()
|
||||
val NO_RECEIVER_ALLOWED by error0<PsiElement>()
|
||||
val FUNCTION_EXPECTED by error2<PsiElement, String, ConeKotlinType>(SourceElementPositioningStrategies.REFERENCED_NAME_BY_QUALIFIED)
|
||||
|
||||
// Super
|
||||
val SUPER_IS_NOT_AN_EXPRESSION by error0<PsiElement>(SourceElementPositioningStrategies.REFERENCED_NAME_BY_QUALIFIED)
|
||||
|
||||
+6
@@ -177,6 +177,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.FINAL_UPPER_BOUND
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.FORBIDDEN_VARARG_PARAMETER_TYPE
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.FUNCTION_CALL_EXPECTED
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.FUNCTION_DECLARATION_WITH_NO_NAME
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.FUNCTION_EXPECTED
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.FUN_INTERFACE_ABSTRACT_METHOD_WITH_DEFAULT_VALUE
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.FUN_INTERFACE_ABSTRACT_METHOD_WITH_TYPE_PARAMETERS
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.FUN_INTERFACE_CANNOT_HAVE_ABSTRACT_PROPERTIES
|
||||
@@ -434,6 +435,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.WRONG_MODIFIER_TA
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.WRONG_NUMBER_OF_TYPE_ARGUMENTS
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.WRONG_SETTER_PARAMETER_TYPE
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.WRONG_SETTER_RETURN_TYPE
|
||||
import org.jetbrains.kotlin.name.StandardClassIds
|
||||
|
||||
@Suppress("unused")
|
||||
class FirDefaultErrorMessages {
|
||||
@@ -496,6 +498,10 @@ class FirDefaultErrorMessages {
|
||||
)
|
||||
map.put(CREATING_AN_INSTANCE_OF_ABSTRACT_CLASS, "Cannot create an instance of an abstract class")
|
||||
map.put(FUNCTION_CALL_EXPECTED, "Function invocation ''{0}({1})'' expected", TO_STRING, FUNCTION_PARAMETERS)
|
||||
map.put(
|
||||
FUNCTION_EXPECTED,
|
||||
"Expression ''{0}'' of type {1} cannot be invoked as a function. The function 'invoke()' is not found", TO_STRING, RENDER_TYPE
|
||||
)
|
||||
map.put(ILLEGAL_SELECTOR, "The expression cannot be a selector (occur after a dot)")
|
||||
map.put(NO_RECEIVER_ALLOWED, "No receiver can be passed to this function or property")
|
||||
|
||||
|
||||
+1
@@ -41,6 +41,7 @@ private fun ConeDiagnostic.toFirDiagnostic(
|
||||
is ConeUnresolvedNameError -> FirErrors.UNRESOLVED_REFERENCE.createOn(source, this.name.asString())
|
||||
is ConeUnresolvedQualifierError -> FirErrors.UNRESOLVED_REFERENCE.createOn(source, this.qualifier)
|
||||
is ConeFunctionCallExpectedError -> FirErrors.FUNCTION_CALL_EXPECTED.createOn(source, this.name.asString(), this.hasValueParameters)
|
||||
is ConeFunctionExpectedError -> FirErrors.FUNCTION_EXPECTED.createOn(source, this.expression, this.type)
|
||||
is ConeHiddenCandidateError -> FirErrors.INVISIBLE_REFERENCE.createOn(source, this.candidateSymbol)
|
||||
is ConeAmbiguityError -> when {
|
||||
applicability.isSuccess -> FirErrors.OVERLOAD_RESOLUTION_AMBIGUITY.createOn(source, this.candidates.map { it.symbol })
|
||||
|
||||
@@ -21,6 +21,7 @@ import org.jetbrains.kotlin.fir.resolve.*
|
||||
import org.jetbrains.kotlin.fir.resolve.calls.*
|
||||
import org.jetbrains.kotlin.fir.resolve.calls.tower.FirTowerResolver
|
||||
import org.jetbrains.kotlin.fir.resolve.calls.tower.TowerResolveManager
|
||||
import org.jetbrains.kotlin.fir.resolve.dfa.symbol
|
||||
import org.jetbrains.kotlin.fir.resolve.diagnostics.*
|
||||
import org.jetbrains.kotlin.fir.resolve.inference.ResolvedCallableReferenceAtom
|
||||
import org.jetbrains.kotlin.fir.resolve.inference.inferenceComponents
|
||||
@@ -84,6 +85,14 @@ class FirCallResolver(
|
||||
val name = functionCall.calleeReference.name
|
||||
val result = collectCandidates(functionCall, name)
|
||||
|
||||
var forceCandidates: Collection<Candidate>? = null
|
||||
if (result.candidates.isEmpty()) {
|
||||
val newResult = collectCandidates(functionCall, name, CallKind.VariableAccess)
|
||||
if (newResult.candidates.isNotEmpty()) {
|
||||
forceCandidates = newResult.candidates
|
||||
}
|
||||
}
|
||||
|
||||
val nameReference = createResolvedNamedReference(
|
||||
functionCall.calleeReference,
|
||||
name,
|
||||
@@ -91,6 +100,8 @@ class FirCallResolver(
|
||||
result.candidates,
|
||||
result.applicability,
|
||||
functionCall.explicitReceiver,
|
||||
expectedCallKind = if (forceCandidates != null) CallKind.VariableAccess else null,
|
||||
expectedCandidates = forceCandidates
|
||||
)
|
||||
|
||||
val resultExpression = functionCall.transformCalleeReference(StoreNameReference, nameReference)
|
||||
@@ -147,7 +158,7 @@ class FirCallResolver(
|
||||
|
||||
val info = CallInfo(
|
||||
qualifiedAccess,
|
||||
if (qualifiedAccess is FirFunctionCall || forceCallKind == CallKind.Function) CallKind.Function else CallKind.VariableAccess,
|
||||
forceCallKind ?: if (qualifiedAccess is FirFunctionCall) CallKind.Function else CallKind.VariableAccess,
|
||||
name,
|
||||
explicitReceiver,
|
||||
argumentList,
|
||||
@@ -229,7 +240,7 @@ class FirCallResolver(
|
||||
reducedCandidates,
|
||||
result.applicability,
|
||||
qualifiedAccess.explicitReceiver,
|
||||
functionCallExpected = functionCallExpected
|
||||
expectedCallKind = if (functionCallExpected) CallKind.Function else null
|
||||
)
|
||||
|
||||
val referencedSymbol = when (nameReference) {
|
||||
@@ -576,38 +587,66 @@ class FirCallResolver(
|
||||
applicability: CandidateApplicability,
|
||||
explicitReceiver: FirExpression? = null,
|
||||
createResolvedReferenceWithoutCandidateForLocalVariables: Boolean = true,
|
||||
functionCallExpected: Boolean = false
|
||||
expectedCallKind: CallKind? = null,
|
||||
expectedCandidates: Collection<Candidate>? = null
|
||||
): FirNamedReference {
|
||||
val source = reference.source
|
||||
return when {
|
||||
functionCallExpected -> {
|
||||
expectedCallKind != null -> {
|
||||
fun isValueParametersNotEmpty(candidate: Candidate): Boolean {
|
||||
return (candidate.symbol.fir as? FirFunction)?.valueParameters?.size?.let { it > 0 } ?: false
|
||||
}
|
||||
|
||||
val candidate = candidates.singleOrNull()
|
||||
|
||||
val diagnostic = if (expectedCallKind == CallKind.Function) {
|
||||
ConeFunctionCallExpectedError(
|
||||
name,
|
||||
candidate?.let { isValueParametersNotEmpty(it) } ?: candidates.any { isValueParametersNotEmpty(it) })
|
||||
} else {
|
||||
val singleExpectedCandidate = expectedCandidates?.singleOrNull()
|
||||
if (singleExpectedCandidate?.symbol?.fir is FirRegularClass) {
|
||||
ConeUnresolvedNameError(name)
|
||||
// TODO: ConeResolutionToClassifierError()
|
||||
} else {
|
||||
val coneType = explicitReceiver?.typeRef?.coneType
|
||||
if (coneType != null && !coneType.isUnit) {
|
||||
ConeFunctionExpectedError(
|
||||
name.asString(),
|
||||
(singleExpectedCandidate?.symbol?.fir as? FirTypedDeclaration)?.returnTypeRef?.coneType ?: coneType
|
||||
)
|
||||
} else {
|
||||
ConeUnresolvedNameError(name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (candidate != null) {
|
||||
createErrorReferenceWithExistingCandidate(
|
||||
candidate,
|
||||
ConeFunctionCallExpectedError(name, isValueParametersNotEmpty(candidate)),
|
||||
diagnostic,
|
||||
source,
|
||||
transformer.resolutionContext,
|
||||
components.resolutionStageRunner
|
||||
)
|
||||
} else {
|
||||
buildErrorReference(
|
||||
callInfo,
|
||||
ConeFunctionCallExpectedError(name, candidates.any { isValueParametersNotEmpty(it) }),
|
||||
source
|
||||
)
|
||||
buildErrorReference(callInfo, diagnostic, source)
|
||||
}
|
||||
}
|
||||
|
||||
candidates.isEmpty() -> buildErrorReference(
|
||||
callInfo,
|
||||
ConeUnresolvedNameError(name),
|
||||
source
|
||||
)
|
||||
candidates.isEmpty() -> {
|
||||
val diagnostic = if (name.asString() == "invoke" && explicitReceiver is FirConstExpression<*>) {
|
||||
ConeFunctionExpectedError(explicitReceiver.value?.toString() ?: "", explicitReceiver.typeRef.coneType)
|
||||
} else {
|
||||
ConeUnresolvedNameError(name)
|
||||
}
|
||||
|
||||
buildErrorReference(
|
||||
callInfo,
|
||||
diagnostic,
|
||||
source
|
||||
)
|
||||
}
|
||||
|
||||
candidates.size > 1 -> buildErrorReference(
|
||||
callInfo,
|
||||
|
||||
+5
@@ -12,6 +12,7 @@ import org.jetbrains.kotlin.fir.declarations.FirCallableDeclaration
|
||||
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
|
||||
import org.jetbrains.kotlin.fir.declarations.FirVariable
|
||||
import org.jetbrains.kotlin.fir.diagnostics.ConeDiagnostic
|
||||
import org.jetbrains.kotlin.fir.expressions.FirExpression
|
||||
import org.jetbrains.kotlin.fir.render
|
||||
import org.jetbrains.kotlin.fir.resolve.calls.Candidate
|
||||
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
|
||||
@@ -49,6 +50,10 @@ class ConeFunctionCallExpectedError(val name: Name, val hasValueParameters: Bool
|
||||
override val reason: String get() = "Function call expected: $name(${if (hasValueParameters) "..." else ""})"
|
||||
}
|
||||
|
||||
class ConeFunctionExpectedError(val expression: String, val type: ConeKotlinType) : ConeDiagnostic() {
|
||||
override val reason: String get() = "Expression '$expression' of type '$type' cannot be invoked as a function"
|
||||
}
|
||||
|
||||
class ConeHiddenCandidateError(
|
||||
val candidateSymbol: FirBasedSymbol<*>
|
||||
) : ConeDiagnostic() {
|
||||
|
||||
Reference in New Issue
Block a user