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