[FIR] Implement FUNCTION_CALL_EXPECTED

This commit is contained in:
Ivan Kochurkin
2021-06-22 23:23:02 +03:00
parent d048bccfa2
commit 59257e47c9
21 changed files with 106 additions and 65 deletions
@@ -102,6 +102,10 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
val CALL_RESOLUTION by object : DiagnosticGroup("Call resolution") {
val CREATING_AN_INSTANCE_OF_ABSTRACT_CLASS by error<KtExpression>()
val FUNCTION_CALL_EXPECTED by error<PsiElement>(PositioningStrategy.REFERENCED_NAME_BY_QUALIFIED) {
parameter<String>("functionName")
parameter<Boolean>("hasValueParameters")
}
}
val SUPER by object : DiagnosticGroup("Super") {
@@ -119,6 +119,7 @@ object FirErrors {
// Call resolution
val CREATING_AN_INSTANCE_OF_ABSTRACT_CLASS by error0<KtExpression>()
val FUNCTION_CALL_EXPECTED by error2<PsiElement, String, Boolean>(SourceElementPositioningStrategies.REFERENCED_NAME_BY_QUALIFIED)
// Super
val SUPER_IS_NOT_AN_EXPRESSION by error0<PsiElement>(SourceElementPositioningStrategies.REFERENCED_NAME_BY_QUALIFIED)
@@ -13,6 +13,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.AMBI
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.DECLARATION_NAME
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.FIR
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.FQ_NAMES_IN_TYPES
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.FUNCTION_PARAMETERS
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.NAME
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.NOT_RENDERED
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.NULLABLE_STRING
@@ -149,6 +150,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.EXTENSION_PROPERT
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.FINAL_SUPERTYPE
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.FUN_INTERFACE_ABSTRACT_METHOD_WITH_DEFAULT_VALUE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.FUN_INTERFACE_ABSTRACT_METHOD_WITH_TYPE_PARAMETERS
@@ -443,6 +445,7 @@ class FirDefaultErrorMessages {
TO_STRING
)
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)
// Supertypes
map.put(ENUM_AS_SUPERTYPE, "Enum as supertype")
@@ -119,4 +119,6 @@ object FirDiagnosticRenderers {
val NOT_RENDERED = Renderer<Any?> {
""
}
val FUNCTION_PARAMETERS = Renderer { hasValueParameters: Boolean -> if (hasValueParameters) "..." else "" }
}
@@ -40,6 +40,7 @@ private fun ConeDiagnostic.toFirDiagnostic(
is ConeUnresolvedSymbolError -> FirErrors.UNRESOLVED_REFERENCE.createOn(source, this.classId.asString())
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 ConeHiddenCandidateError -> FirErrors.INVISIBLE_REFERENCE.createOn(source, this.candidateSymbol)
is ConeAmbiguityError -> when {
applicability.isSuccess -> FirErrors.OVERLOAD_RESOLUTION_AMBIGUITY.createOn(source, this.candidates.map { it.symbol })
@@ -136,14 +136,18 @@ class FirCallResolver(
val info: CallInfo, val applicability: CandidateApplicability, val candidates: Collection<Candidate>,
)
private fun <T : FirQualifiedAccess> collectCandidates(qualifiedAccess: T, name: Name): ResolutionResult {
private fun <T : FirQualifiedAccess> collectCandidates(
qualifiedAccess: T,
name: Name,
forceCallKind: CallKind? = null
): ResolutionResult {
val explicitReceiver = qualifiedAccess.explicitReceiver
val argumentList = (qualifiedAccess as? FirFunctionCall)?.argumentList ?: FirEmptyArgumentList
val typeArguments = (qualifiedAccess as? FirFunctionCall)?.typeArguments.orEmpty()
val info = CallInfo(
qualifiedAccess,
if (qualifiedAccess is FirFunctionCall) CallKind.Function else CallKind.VariableAccess,
if (qualifiedAccess is FirFunctionCall || forceCallKind == CallKind.Function) CallKind.Function else CallKind.VariableAccess,
name,
explicitReceiver,
argumentList,
@@ -195,16 +199,7 @@ class FirCallResolver(
return resolvedQualifierPart
}
val result = collectCandidates(qualifiedAccess, callee.name)
val reducedCandidates = result.candidates
val nameReference = createResolvedNamedReference(
callee,
callee.name,
result.info,
reducedCandidates,
result.applicability,
qualifiedAccess.explicitReceiver,
)
var result = collectCandidates(qualifiedAccess, callee.name)
if (qualifiedAccess.explicitReceiver == null) {
if (!result.applicability.isSuccess) {
@@ -217,6 +212,26 @@ class FirCallResolver(
qualifiedResolver.reset()
}
var functionCallExpected = false
if (result.candidates.isEmpty() && qualifiedAccess !is FirFunctionCall) {
val newResult = collectCandidates(qualifiedAccess, callee.name, CallKind.Function)
if (newResult.candidates.isNotEmpty()) {
result = newResult
functionCallExpected = true
}
}
val reducedCandidates = result.candidates
val nameReference = createResolvedNamedReference(
callee,
callee.name,
result.info,
reducedCandidates,
result.applicability,
qualifiedAccess.explicitReceiver,
functionCallExpected = functionCallExpected
)
val referencedSymbol = when (nameReference) {
is FirResolvedNamedReference -> nameReference.resolvedSymbol
is FirNamedReferenceWithCandidate -> nameReference.candidateSymbol
@@ -544,10 +559,34 @@ class FirCallResolver(
candidates: Collection<Candidate>,
applicability: CandidateApplicability,
explicitReceiver: FirExpression? = null,
createResolvedReferenceWithoutCandidateForLocalVariables: Boolean = true
createResolvedReferenceWithoutCandidateForLocalVariables: Boolean = true,
functionCallExpected: Boolean = false
): FirNamedReference {
val source = reference.source
return when {
functionCallExpected -> {
fun isValueParametersNotEmpty(candidate: Candidate): Boolean {
return (candidate.symbol.fir as? FirFunction)?.valueParameters?.size?.let { it > 0 } ?: false
}
val candidate = candidates.singleOrNull()
if (candidate != null) {
createErrorReferenceWithExistingCandidate(
candidate,
ConeFunctionCallExpectedError(name, isValueParametersNotEmpty(candidate)),
source,
transformer.resolutionContext,
components.resolutionStageRunner
)
} else {
buildErrorReference(
callInfo,
ConeFunctionCallExpectedError(name, candidates.any { isValueParametersNotEmpty(it) }),
source
)
}
}
candidates.isEmpty() -> buildErrorReference(
callInfo,
ConeUnresolvedNameError(name),
@@ -44,6 +44,10 @@ class ConeUnresolvedNameError(val name: Name) : ConeUnresolvedError() {
override val reason: String get() = "Unresolved name: $name"
}
class ConeFunctionCallExpectedError(val name: Name, val hasValueParameters: Boolean) : ConeDiagnostic() {
override val reason: String get() = "Function call expected: $name(${if (hasValueParameters) "..." else ""})"
}
class ConeHiddenCandidateError(
val candidateSymbol: FirBasedSymbol<*>
) : ConeDiagnostic() {