From 32fa2fc4767ea3842012740b0341a34466303c32 Mon Sep 17 00:00:00 2001 From: Roman Golyshev Date: Mon, 11 Apr 2022 14:54:17 +0400 Subject: [PATCH] [FIR] Resolve receiver in qualified expressions with no selector In qualified expression like `foo().`, selector expression is null. Because of that the whole expression was marked as an error FIR expression, and `foo()` part was not resolved at all (including arguments and everything else). This commit fixes the problem by providing receiver's FIR expression as an underlying expression for error FIR expression. That way it will be seen by all resolve transformers and will be successfully resolved. ^KTIJ-21484 Fixed --- ...irSourceReferenceResolveTestGenerated.java | 18 +++++++++++ .../NoSelectorInDotQualifiedCall.kt | 11 +++++++ .../NoSelectorInDotQualifiedCall.txt | 2 ++ ...rInDotQualifiedCall_ResolveInsideLambda.kt | 15 ++++++++++ ...InDotQualifiedCall_ResolveInsideLambda.txt | 2 ++ .../NoSelectorInSafeQualifiedCall.kt | 11 +++++++ .../NoSelectorInSafeQualifiedCall.txt | 2 ++ ...nosisCompilerFirTestdataTestGenerated.java | 6 ++++ ...TouchedTilContractsPhaseTestGenerated.java | 5 ++++ ...emptySelectorInQualifiedExpression.fir.txt | 25 ++++++++++++++++ .../emptySelectorInQualifiedExpression.kt | 30 +++++++++++++++++++ .../runners/FirDiagnosticTestGenerated.java | 6 ++++ ...DiagnosticsWithLightTreeTestGenerated.java | 6 ++++ .../converter/ExpressionsConverter.kt | 11 ++++--- .../kotlin/fir/builder/RawFirBuilder.kt | 15 ++++++---- .../incompleteEnumReference.fir.kt | 11 +++++++ .../incompleteEnumReference.kt | 1 - .../testData/diagnostics/tests/kt34440.fir.kt | 9 ------ .../testData/diagnostics/tests/kt34440.kt | 1 + .../nullCalleeExpression.fir.kt | 2 +- 20 files changed, 169 insertions(+), 20 deletions(-) create mode 100644 analysis/analysis-api/testData/referenceResolve/withErrors/NoSelectorInDotQualifiedCall.kt create mode 100644 analysis/analysis-api/testData/referenceResolve/withErrors/NoSelectorInDotQualifiedCall.txt create mode 100644 analysis/analysis-api/testData/referenceResolve/withErrors/NoSelectorInDotQualifiedCall_ResolveInsideLambda.kt create mode 100644 analysis/analysis-api/testData/referenceResolve/withErrors/NoSelectorInDotQualifiedCall_ResolveInsideLambda.txt create mode 100644 analysis/analysis-api/testData/referenceResolve/withErrors/NoSelectorInSafeQualifiedCall.kt create mode 100644 analysis/analysis-api/testData/referenceResolve/withErrors/NoSelectorInSafeQualifiedCall.txt create mode 100644 compiler/fir/analysis-tests/testData/resolve/problems/emptySelectorInQualifiedExpression.fir.txt create mode 100644 compiler/fir/analysis-tests/testData/resolve/problems/emptySelectorInQualifiedExpression.kt create mode 100644 compiler/testData/diagnostics/tests/incompleteCode/diagnosticWithSyntaxError/incompleteEnumReference.fir.kt delete mode 100644 compiler/testData/diagnostics/tests/kt34440.fir.kt diff --git a/analysis/analysis-api-fir/tests/org/jetbrains/kotlin/analysis/api/fir/FirSourceReferenceResolveTestGenerated.java b/analysis/analysis-api-fir/tests/org/jetbrains/kotlin/analysis/api/fir/FirSourceReferenceResolveTestGenerated.java index c686d79f4f4..4bc2cb0222d 100644 --- a/analysis/analysis-api-fir/tests/org/jetbrains/kotlin/analysis/api/fir/FirSourceReferenceResolveTestGenerated.java +++ b/analysis/analysis-api-fir/tests/org/jetbrains/kotlin/analysis/api/fir/FirSourceReferenceResolveTestGenerated.java @@ -1335,6 +1335,24 @@ public class FirSourceReferenceResolveTestGenerated extends AbstractReferenceRes runTest("analysis/analysis-api/testData/referenceResolve/withErrors/InvisibleMember.kt"); } + @Test + @TestMetadata("NoSelectorInDotQualifiedCall.kt") + public void testNoSelectorInDotQualifiedCall() throws Exception { + runTest("analysis/analysis-api/testData/referenceResolve/withErrors/NoSelectorInDotQualifiedCall.kt"); + } + + @Test + @TestMetadata("NoSelectorInDotQualifiedCall_ResolveInsideLambda.kt") + public void testNoSelectorInDotQualifiedCall_ResolveInsideLambda() throws Exception { + runTest("analysis/analysis-api/testData/referenceResolve/withErrors/NoSelectorInDotQualifiedCall_ResolveInsideLambda.kt"); + } + + @Test + @TestMetadata("NoSelectorInSafeQualifiedCall.kt") + public void testNoSelectorInSafeQualifiedCall() throws Exception { + runTest("analysis/analysis-api/testData/referenceResolve/withErrors/NoSelectorInSafeQualifiedCall.kt"); + } + @Test @TestMetadata("PropertyPlaceInClassObjectInObject.kt") public void testPropertyPlaceInClassObjectInObject() throws Exception { diff --git a/analysis/analysis-api/testData/referenceResolve/withErrors/NoSelectorInDotQualifiedCall.kt b/analysis/analysis-api/testData/referenceResolve/withErrors/NoSelectorInDotQualifiedCall.kt new file mode 100644 index 00000000000..28b1ef90f04 --- /dev/null +++ b/analysis/analysis-api/testData/referenceResolve/withErrors/NoSelectorInDotQualifiedCall.kt @@ -0,0 +1,11 @@ +package testing + +interface Foo + +val otherFoo: Foo = makeFoo() + +fun makeFoo(): Foo = Foo() + +fun test() { + makeFoo(). +} \ No newline at end of file diff --git a/analysis/analysis-api/testData/referenceResolve/withErrors/NoSelectorInDotQualifiedCall.txt b/analysis/analysis-api/testData/referenceResolve/withErrors/NoSelectorInDotQualifiedCall.txt new file mode 100644 index 00000000000..7f992160c39 --- /dev/null +++ b/analysis/analysis-api/testData/referenceResolve/withErrors/NoSelectorInDotQualifiedCall.txt @@ -0,0 +1,2 @@ +Resolved to: +0: (in testing) fun makeFoo(): testing.Foo diff --git a/analysis/analysis-api/testData/referenceResolve/withErrors/NoSelectorInDotQualifiedCall_ResolveInsideLambda.kt b/analysis/analysis-api/testData/referenceResolve/withErrors/NoSelectorInDotQualifiedCall_ResolveInsideLambda.kt new file mode 100644 index 00000000000..f120e06f41d --- /dev/null +++ b/analysis/analysis-api/testData/referenceResolve/withErrors/NoSelectorInDotQualifiedCall_ResolveInsideLambda.kt @@ -0,0 +1,15 @@ +package testing + +interface Foo + +val otherFoo: Foo = makeFoo() + +fun makeFoo(action: () -> Unit): Foo = Foo() + +fun test() {} + +fun test() { + makeFoo { + test() + }. +} \ No newline at end of file diff --git a/analysis/analysis-api/testData/referenceResolve/withErrors/NoSelectorInDotQualifiedCall_ResolveInsideLambda.txt b/analysis/analysis-api/testData/referenceResolve/withErrors/NoSelectorInDotQualifiedCall_ResolveInsideLambda.txt new file mode 100644 index 00000000000..b2169f57728 --- /dev/null +++ b/analysis/analysis-api/testData/referenceResolve/withErrors/NoSelectorInDotQualifiedCall_ResolveInsideLambda.txt @@ -0,0 +1,2 @@ +Resolved to: +0: (in testing) fun test() \ No newline at end of file diff --git a/analysis/analysis-api/testData/referenceResolve/withErrors/NoSelectorInSafeQualifiedCall.kt b/analysis/analysis-api/testData/referenceResolve/withErrors/NoSelectorInSafeQualifiedCall.kt new file mode 100644 index 00000000000..1d147c10783 --- /dev/null +++ b/analysis/analysis-api/testData/referenceResolve/withErrors/NoSelectorInSafeQualifiedCall.kt @@ -0,0 +1,11 @@ +package testing + +interface Foo + +val otherFoo: Foo = makeFoo() + +fun makeFoo(): Foo = Foo() + +fun test() { + makeFoo()?. +} \ No newline at end of file diff --git a/analysis/analysis-api/testData/referenceResolve/withErrors/NoSelectorInSafeQualifiedCall.txt b/analysis/analysis-api/testData/referenceResolve/withErrors/NoSelectorInSafeQualifiedCall.txt new file mode 100644 index 00000000000..7f992160c39 --- /dev/null +++ b/analysis/analysis-api/testData/referenceResolve/withErrors/NoSelectorInSafeQualifiedCall.txt @@ -0,0 +1,2 @@ +Resolved to: +0: (in testing) fun makeFoo(): testing.Foo diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerFirTestdataTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerFirTestdataTestGenerated.java index 7c27585044a..d417674f93a 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerFirTestdataTestGenerated.java +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerFirTestdataTestGenerated.java @@ -3225,6 +3225,12 @@ public class DiagnosisCompilerFirTestdataTestGenerated extends AbstractDiagnosis runTest("compiler/fir/analysis-tests/testData/resolve/problems/doubleGenericDiamond.kt"); } + @Test + @TestMetadata("emptySelectorInQualifiedExpression.kt") + public void testEmptySelectorInQualifiedExpression() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolve/problems/emptySelectorInQualifiedExpression.kt"); + } + @Test @TestMetadata("expectConstructor.kt") public void testExpectConstructor() throws Exception { diff --git a/compiler/fir/analysis-tests/legacy-fir-tests/tests-gen/org/jetbrains/kotlin/fir/LazyBodyIsNotTouchedTilContractsPhaseTestGenerated.java b/compiler/fir/analysis-tests/legacy-fir-tests/tests-gen/org/jetbrains/kotlin/fir/LazyBodyIsNotTouchedTilContractsPhaseTestGenerated.java index 9d2cc48c458..94da56cbe73 100644 --- a/compiler/fir/analysis-tests/legacy-fir-tests/tests-gen/org/jetbrains/kotlin/fir/LazyBodyIsNotTouchedTilContractsPhaseTestGenerated.java +++ b/compiler/fir/analysis-tests/legacy-fir-tests/tests-gen/org/jetbrains/kotlin/fir/LazyBodyIsNotTouchedTilContractsPhaseTestGenerated.java @@ -2844,6 +2844,11 @@ public class LazyBodyIsNotTouchedTilContractsPhaseTestGenerated extends Abstract runTest("compiler/fir/analysis-tests/testData/resolve/problems/doubleGenericDiamond.kt"); } + @TestMetadata("emptySelectorInQualifiedExpression.kt") + public void testEmptySelectorInQualifiedExpression() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolve/problems/emptySelectorInQualifiedExpression.kt"); + } + @TestMetadata("expectConstructor.kt") public void testExpectConstructor() throws Exception { runTest("compiler/fir/analysis-tests/testData/resolve/problems/expectConstructor.kt"); diff --git a/compiler/fir/analysis-tests/testData/resolve/problems/emptySelectorInQualifiedExpression.fir.txt b/compiler/fir/analysis-tests/testData/resolve/problems/emptySelectorInQualifiedExpression.fir.txt new file mode 100644 index 00000000000..3af20ad9e28 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolve/problems/emptySelectorInQualifiedExpression.fir.txt @@ -0,0 +1,25 @@ +FILE: emptySelectorInQualifiedExpression.kt + public final fun foo(action: R|() -> kotlin/Unit| = fun (): R|kotlin/Unit| { + ^ Unit + } + ): R|kotlin/Int| { + ^foo Int(0) + } + public final fun usageResolved1(): R|kotlin/Unit| { + ERROR_EXPR(Qualified expression without selector) + } + public final fun usageResolved2(): R|kotlin/Unit| { + ERROR_EXPR(Qualified expression without selector) + } + public final fun usageResolved3(): R|kotlin/Unit| { + ERROR_EXPR(Qualified expression without selector) + } + public final fun usageUnresolved1(): R|kotlin/Unit| { + ERROR_EXPR(Qualified expression without selector) + } + public final fun usageUnresolved2(): R|kotlin/Unit| { + ERROR_EXPR(Qualified expression without selector) + } + public final fun usageUnresolved3(): R|kotlin/Unit| { + ERROR_EXPR(Qualified expression without selector) + } diff --git a/compiler/fir/analysis-tests/testData/resolve/problems/emptySelectorInQualifiedExpression.kt b/compiler/fir/analysis-tests/testData/resolve/problems/emptySelectorInQualifiedExpression.kt new file mode 100644 index 00000000000..99a2dc7a96d --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolve/problems/emptySelectorInQualifiedExpression.kt @@ -0,0 +1,30 @@ +fun foo(action: () -> Unit = {}): Int = 0 + +fun usageResolved1() { + foo(). +} + +fun usageResolved2() { + foo()?. +} + +fun usageResolved3() { + foo { + foo() + }. +} + +fun usageUnresolved1() { + bar(). +} + +fun usageUnresolved2() { + bar()?. +} + +fun usageUnresolved3() { + foo { + bar() + }. +} + diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirDiagnosticTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirDiagnosticTestGenerated.java index f71353f126d..df438c76cdc 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirDiagnosticTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirDiagnosticTestGenerated.java @@ -3225,6 +3225,12 @@ public class FirDiagnosticTestGenerated extends AbstractFirDiagnosticTest { runTest("compiler/fir/analysis-tests/testData/resolve/problems/doubleGenericDiamond.kt"); } + @Test + @TestMetadata("emptySelectorInQualifiedExpression.kt") + public void testEmptySelectorInQualifiedExpression() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolve/problems/emptySelectorInQualifiedExpression.kt"); + } + @Test @TestMetadata("expectConstructor.kt") public void testExpectConstructor() throws Exception { diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirDiagnosticsWithLightTreeTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirDiagnosticsWithLightTreeTestGenerated.java index fc511ab1f9e..0ea20b745d6 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirDiagnosticsWithLightTreeTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirDiagnosticsWithLightTreeTestGenerated.java @@ -3225,6 +3225,12 @@ public class FirDiagnosticsWithLightTreeTestGenerated extends AbstractFirDiagnos runTest("compiler/fir/analysis-tests/testData/resolve/problems/doubleGenericDiamond.kt"); } + @Test + @TestMetadata("emptySelectorInQualifiedExpression.kt") + public void testEmptySelectorInQualifiedExpression() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolve/problems/emptySelectorInQualifiedExpression.kt"); + } + @Test @TestMetadata("expectConstructor.kt") public void testExpectConstructor() throws Exception { diff --git a/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/converter/ExpressionsConverter.kt b/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/converter/ExpressionsConverter.kt index 0a8c722abe4..49f1ff5d691 100644 --- a/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/converter/ExpressionsConverter.kt +++ b/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/converter/ExpressionsConverter.kt @@ -554,10 +554,13 @@ class ExpressionsConverter( result = convertFirSelector(it, dotQualifiedExpression.toFirSourceElement(), firReceiver!!) as? FirExpression } - return result ?: buildErrorExpression( - null, - ConeSimpleDiagnostic("Qualified expression without selector", DiagnosticKind.Syntax) - ) + return result ?: buildErrorExpression { + source = null + diagnostic = ConeSimpleDiagnostic("Qualified expression without selector", DiagnosticKind.Syntax) + + // if there is no selector, we still want to resolve the receiver + expression = firReceiver + } } /** diff --git a/compiler/fir/raw-fir/psi2fir/src/org/jetbrains/kotlin/fir/builder/RawFirBuilder.kt b/compiler/fir/raw-fir/psi2fir/src/org/jetbrains/kotlin/fir/builder/RawFirBuilder.kt index 982be0573bd..ff10e1e6614 100644 --- a/compiler/fir/raw-fir/psi2fir/src/org/jetbrains/kotlin/fir/builder/RawFirBuilder.kt +++ b/compiler/fir/raw-fir/psi2fir/src/org/jetbrains/kotlin/fir/builder/RawFirBuilder.kt @@ -2389,14 +2389,19 @@ open class RawFirBuilder( } override fun visitQualifiedExpression(expression: KtQualifiedExpression, data: Unit): FirElement { + val receiver = expression.receiverExpression.toFirExpression("Incorrect receiver expression") + val selector = expression.selectorExpression - ?: return buildErrorExpression( - expression.toFirSourceElement(), ConeSimpleDiagnostic("Qualified expression without selector", DiagnosticKind.Syntax), - ) + ?: return buildErrorExpression { + source = expression.toFirSourceElement() + diagnostic = ConeSimpleDiagnostic("Qualified expression without selector", DiagnosticKind.Syntax) + + // if there is no selector, we still want to resolve the receiver + this.expression = receiver + } + val firSelector = selector.toFirExpression("Incorrect selector expression") if (firSelector is FirQualifiedAccess) { - val receiver = expression.receiverExpression.toFirExpression("Incorrect receiver expression") - if (expression is KtSafeQualifiedExpression) { @OptIn(FirImplementationDetail::class) firSelector.replaceSource(expression.toFirSourceElement(KtFakeSourceElementKind.DesugaredSafeCallExpression)) diff --git a/compiler/testData/diagnostics/tests/incompleteCode/diagnosticWithSyntaxError/incompleteEnumReference.fir.kt b/compiler/testData/diagnostics/tests/incompleteCode/diagnosticWithSyntaxError/incompleteEnumReference.fir.kt new file mode 100644 index 00000000000..cff3ab6f0dc --- /dev/null +++ b/compiler/testData/diagnostics/tests/incompleteCode/diagnosticWithSyntaxError/incompleteEnumReference.fir.kt @@ -0,0 +1,11 @@ +enum class E { + A, + B, + C +} + +fun foo() { + val e = E. +} + + diff --git a/compiler/testData/diagnostics/tests/incompleteCode/diagnosticWithSyntaxError/incompleteEnumReference.kt b/compiler/testData/diagnostics/tests/incompleteCode/diagnosticWithSyntaxError/incompleteEnumReference.kt index e135faf9ec4..ef56e1ca39a 100644 --- a/compiler/testData/diagnostics/tests/incompleteCode/diagnosticWithSyntaxError/incompleteEnumReference.kt +++ b/compiler/testData/diagnostics/tests/incompleteCode/diagnosticWithSyntaxError/incompleteEnumReference.kt @@ -1,4 +1,3 @@ -// FIR_IDENTICAL enum class E { A, B, diff --git a/compiler/testData/diagnostics/tests/kt34440.fir.kt b/compiler/testData/diagnostics/tests/kt34440.fir.kt deleted file mode 100644 index 0d2e2a2c894..00000000000 --- a/compiler/testData/diagnostics/tests/kt34440.fir.kt +++ /dev/null @@ -1,9 +0,0 @@ -// ISSUE: KT-34440 - -class BufferUtil { - fun isDirect(cond: Boolean): Boolean = - when (cond) { - else -> throw Exception("${buf.}") - } - private class BufferInfo(private val type: Class<*>) -} diff --git a/compiler/testData/diagnostics/tests/kt34440.kt b/compiler/testData/diagnostics/tests/kt34440.kt index 99e4caf5052..13c7b407f80 100644 --- a/compiler/testData/diagnostics/tests/kt34440.kt +++ b/compiler/testData/diagnostics/tests/kt34440.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // ISSUE: KT-34440 class BufferUtil { diff --git a/compiler/testData/diagnostics/tests/qualifiedExpression/nullCalleeExpression.fir.kt b/compiler/testData/diagnostics/tests/qualifiedExpression/nullCalleeExpression.fir.kt index a24577bee4e..76581cf6570 100644 --- a/compiler/testData/diagnostics/tests/qualifiedExpression/nullCalleeExpression.fir.kt +++ b/compiler/testData/diagnostics/tests/qualifiedExpression/nullCalleeExpression.fir.kt @@ -1,3 +1,3 @@ // NI_EXPECTED_FILE -val unwrapped = some.<cabc$Wrapper<out Any>::unwrap +val unwrapped = some.<cabc$Wrapper<out Any>::unwrap