[FIR] Implement FUNCTION_CALL_EXPECTED
This commit is contained in:
+4
@@ -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)
|
||||
|
||||
+3
@@ -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")
|
||||
|
||||
+2
@@ -119,4 +119,6 @@ object FirDiagnosticRenderers {
|
||||
val NOT_RENDERED = Renderer<Any?> {
|
||||
""
|
||||
}
|
||||
|
||||
val FUNCTION_PARAMETERS = Renderer { hasValueParameters: Boolean -> if (hasValueParameters) "..." else "" }
|
||||
}
|
||||
|
||||
+1
@@ -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),
|
||||
|
||||
+4
@@ -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() {
|
||||
|
||||
@@ -21,8 +21,8 @@ fun test(l : java.util.List<Int>) {
|
||||
|
||||
val f : java.io.File? = null
|
||||
|
||||
Collections.<!UNRESOLVED_REFERENCE!>emptyList<!>
|
||||
Collections.<!UNRESOLVED_REFERENCE!>emptyList<!><Int>
|
||||
Collections.<!FUNCTION_CALL_EXPECTED!>emptyList<!>
|
||||
Collections.<!FUNCTION_CALL_EXPECTED!>emptyList<!><<!CANNOT_INFER_PARAMETER_TYPE!>Int<!>>
|
||||
Collections.emptyList<Int>()
|
||||
Collections.<!NEW_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>emptyList<!>()
|
||||
|
||||
|
||||
Vendored
-1
@@ -1 +0,0 @@
|
||||
fun test() = ("").<!UNRESOLVED_REFERENCE!>hashCode<!>::hashCode
|
||||
Vendored
+1
@@ -1 +1,2 @@
|
||||
// FIR_IDENTICAL
|
||||
fun test() = ("").<!FUNCTION_CALL_EXPECTED!>hashCode<!>::hashCode
|
||||
|
||||
@@ -9,8 +9,8 @@ class Foo {
|
||||
}
|
||||
|
||||
fun x(f : Foo) {
|
||||
f.<!UNRESOLVED_REFERENCE!>prop<!>
|
||||
f.<!UNRESOLVED_REFERENCE!>bar<!>
|
||||
f.<!FUNCTION_CALL_EXPECTED!>prop<!>
|
||||
f.<!FUNCTION_CALL_EXPECTED!>bar<!>
|
||||
|
||||
f.<!UNRESOLVED_REFERENCE!>a<!>()
|
||||
<!UNRESOLVED_REFERENCE!>c<!>()
|
||||
|
||||
@@ -7,8 +7,8 @@ import checkSubtype
|
||||
fun main(args : Array<String>) {
|
||||
val x = checkSubtype<Any>(args[0])
|
||||
if(x is java.lang.CharSequence) {
|
||||
if (<!EQUALITY_NOT_APPLICABLE!>"a" == x<!>) x.<!UNRESOLVED_REFERENCE!>length<!> else x.length() // OK
|
||||
if (<!EQUALITY_NOT_APPLICABLE!>"a" == x<!> || <!EQUALITY_NOT_APPLICABLE!>"b" == x<!>) x.<!UNRESOLVED_REFERENCE!>length<!> else x.length() // <– THEN ERROR
|
||||
if (<!EQUALITY_NOT_APPLICABLE!>"a" == x<!> && <!EQUALITY_NOT_APPLICABLE!>"a" == x<!>) x.<!UNRESOLVED_REFERENCE!>length<!> else x.length() // <– ELSE ERROR
|
||||
if (<!EQUALITY_NOT_APPLICABLE!>"a" == x<!>) x.<!FUNCTION_CALL_EXPECTED!>length<!> else x.length() // OK
|
||||
if (<!EQUALITY_NOT_APPLICABLE!>"a" == x<!> || <!EQUALITY_NOT_APPLICABLE!>"b" == x<!>) x.<!FUNCTION_CALL_EXPECTED!>length<!> else x.length() // <– THEN ERROR
|
||||
if (<!EQUALITY_NOT_APPLICABLE!>"a" == x<!> && <!EQUALITY_NOT_APPLICABLE!>"a" == x<!>) x.<!FUNCTION_CALL_EXPECTED!>length<!> else x.length() // <– ELSE ERROR
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
fun import() {
|
||||
<!UNRESOLVED_REFERENCE!>import<!> a<!SYNTAX!>.<!><!UNRESOLVED_REFERENCE!>*<!><!SYNTAX!><!>
|
||||
<!FUNCTION_CALL_EXPECTED!>import<!> <!UNRESOLVED_REFERENCE!>a<!><!SYNTAX!>.<!><!UNRESOLVED_REFERENCE!>*<!><!SYNTAX!><!>
|
||||
}
|
||||
|
||||
fun composite() {
|
||||
@@ -7,9 +7,9 @@ fun composite() {
|
||||
}
|
||||
|
||||
fun html() {
|
||||
<!SYNTAX!><<!><!UNRESOLVED_REFERENCE!>html<!>><!SYNTAX!><<!><!UNRESOLVED_REFERENCE!>/<!><!UNRESOLVED_REFERENCE!>html<!>><!SYNTAX!><!>
|
||||
<!SYNTAX!><<!><!FUNCTION_CALL_EXPECTED!>html<!><!UNRESOLVED_REFERENCE!>><!><!SYNTAX!><<!><!UNRESOLVED_REFERENCE!>/<!><!FUNCTION_CALL_EXPECTED!>html<!>><!SYNTAX!><!>
|
||||
}
|
||||
|
||||
fun html1() {
|
||||
<!SYNTAX!><<!><!UNRESOLVED_REFERENCE!>html<!>><!SYNTAX!><<!><!UNRESOLVED_REFERENCE!>/<!><!UNRESOLVED_REFERENCE!>html<!>><!UNRESOLVED_REFERENCE!>html<!>
|
||||
<!SYNTAX!><<!><!FUNCTION_CALL_EXPECTED!>html<!><!UNRESOLVED_REFERENCE!>><!><!SYNTAX!><<!><!UNRESOLVED_REFERENCE!>/<!><!FUNCTION_CALL_EXPECTED!>html<!>><!ARGUMENT_TYPE_MISMATCH, FUNCTION_CALL_EXPECTED!>html<!>
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// NI_EXPECTED_FILE
|
||||
// See EA-76890 / KT-10843: NPE during analysis
|
||||
fun lambda(x : Int?) = x?.<!UNRESOLVED_REFERENCE!>let<!> l {
|
||||
fun lambda(x : Int?) = x?.<!FUNCTION_CALL_EXPECTED!>let<!> <!UNRESOLVED_REFERENCE!>l<!> {
|
||||
y ->
|
||||
if (y <!UNRESOLVED_REFERENCE!>><!> 0) return@l x
|
||||
y
|
||||
|
||||
Vendored
+1
-1
@@ -58,7 +58,7 @@ fun test(a: A, b: B) {
|
||||
|
||||
b.(foo)()
|
||||
|
||||
(b.<!UNRESOLVED_REFERENCE!>foo<!>)()
|
||||
(<!UNRESOLVED_REFERENCE!>b.<!FUNCTION_CALL_EXPECTED!>foo<!><!>)()
|
||||
|
||||
foo(b)
|
||||
(foo)(b)
|
||||
|
||||
-37
@@ -1,37 +0,0 @@
|
||||
// FILE: KotlinFile.kt
|
||||
fun foo(javaClass: JavaClass) {
|
||||
javaClass.isSomething = !javaClass.isSomething
|
||||
javaClass.isSomething2 = !javaClass.isSomething2
|
||||
|
||||
javaClass.<!UNRESOLVED_REFERENCE!>something<!>
|
||||
javaClass.isSomethingWrong
|
||||
javaClass.<!UNRESOLVED_REFERENCE!>somethingWrong<!>
|
||||
|
||||
javaClass.<!UNRESOLVED_REFERENCE!>issueFlag<!>
|
||||
javaClass.<!UNRESOLVED_REFERENCE!>isSueFlag<!>
|
||||
}
|
||||
|
||||
// FILE: JavaClass.java
|
||||
public class JavaClass {
|
||||
public boolean isSomething() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public void setSomething(boolean value) {
|
||||
}
|
||||
|
||||
public boolean getIsSomething2() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public void setIsSomething2(boolean value) {
|
||||
}
|
||||
|
||||
public int isSomethingWrong() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
public boolean issueFlag() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
+1
@@ -1,3 +1,4 @@
|
||||
// FIR_IDENTICAL
|
||||
// FILE: KotlinFile.kt
|
||||
fun foo(javaClass: JavaClass) {
|
||||
javaClass.isSomething = !javaClass.isSomething
|
||||
|
||||
+8
@@ -283,6 +283,14 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
|
||||
token,
|
||||
)
|
||||
}
|
||||
add(FirErrors.FUNCTION_CALL_EXPECTED) { firDiagnostic ->
|
||||
FunctionCallExpectedImpl(
|
||||
firDiagnostic.a,
|
||||
firDiagnostic.b,
|
||||
firDiagnostic as FirPsiDiagnostic,
|
||||
token,
|
||||
)
|
||||
}
|
||||
add(FirErrors.SUPER_IS_NOT_AN_EXPRESSION) { firDiagnostic ->
|
||||
SuperIsNotAnExpressionImpl(
|
||||
firDiagnostic as FirPsiDiagnostic,
|
||||
|
||||
+6
@@ -220,6 +220,12 @@ sealed class KtFirDiagnostic<PSI: PsiElement> : KtDiagnosticWithPsi<PSI> {
|
||||
override val diagnosticClass get() = CreatingAnInstanceOfAbstractClass::class
|
||||
}
|
||||
|
||||
abstract class FunctionCallExpected : KtFirDiagnostic<PsiElement>() {
|
||||
override val diagnosticClass get() = FunctionCallExpected::class
|
||||
abstract val functionName: String
|
||||
abstract val hasValueParameters: Boolean
|
||||
}
|
||||
|
||||
abstract class SuperIsNotAnExpression : KtFirDiagnostic<PsiElement>() {
|
||||
override val diagnosticClass get() = SuperIsNotAnExpression::class
|
||||
}
|
||||
|
||||
+9
@@ -329,6 +329,15 @@ internal class CreatingAnInstanceOfAbstractClassImpl(
|
||||
override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic)
|
||||
}
|
||||
|
||||
internal class FunctionCallExpectedImpl(
|
||||
override val functionName: String,
|
||||
override val hasValueParameters: Boolean,
|
||||
firDiagnostic: FirPsiDiagnostic,
|
||||
override val token: ValidityToken,
|
||||
) : KtFirDiagnostic.FunctionCallExpected(), KtAbstractFirDiagnostic<PsiElement> {
|
||||
override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic)
|
||||
}
|
||||
|
||||
internal class SuperIsNotAnExpressionImpl(
|
||||
firDiagnostic: FirPsiDiagnostic,
|
||||
override val token: ValidityToken,
|
||||
|
||||
+2
-2
@@ -18,8 +18,8 @@ fun test(l : List<Int>) {
|
||||
|
||||
val f : java.io.File? = null
|
||||
|
||||
Collections.<error descr="[UNRESOLVED_REFERENCE] Unresolved reference: emptyList">emptyList</error>
|
||||
Collections.<error descr="[UNRESOLVED_REFERENCE] Unresolved reference: emptyList">emptyList</error><Int>
|
||||
Collections.<error descr="[FUNCTION_CALL_EXPECTED] Function invocation 'emptyList()' expected">emptyList</error>
|
||||
Collections.<error descr="[FUNCTION_CALL_EXPECTED] Function invocation 'emptyList()' expected">emptyList</error><<error descr="[CANNOT_INFER_PARAMETER_TYPE] cannot infer a type for this parameter. Please specify it explicitly.">Int</error>>
|
||||
Collections.emptyList<Int>()
|
||||
Collections.<error descr="[NEW_INFERENCE_NO_INFORMATION_FOR_PARAMETER] T">emptyList</error>()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user