KT-1436 Nicer compiler errors when the feature isn't supported
diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java
index 86c64d9..5ca8020 100644
--- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java
+++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java
@@ -5627,6 +5627,12 @@
}
@Test
+ @TestMetadata("inlinedBreakContinueFeatureDisabled.kt")
+ public void testInlinedBreakContinueFeatureDisabled() throws Exception {
+ runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/inlinedBreakContinueFeatureDisabled.kt");
+ }
+
+ @Test
@TestMetadata("kt1001.kt")
public void testKt1001() throws Exception {
runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/kt1001.kt");
diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java
index d18e89b..036e686 100644
--- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java
+++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java
@@ -5627,6 +5627,12 @@
}
@Test
+ @TestMetadata("inlinedBreakContinueFeatureDisabled.kt")
+ public void testInlinedBreakContinueFeatureDisabled() throws Exception {
+ runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/inlinedBreakContinueFeatureDisabled.kt");
+ }
+
+ @Test
@TestMetadata("kt1001.kt")
public void testKt1001() throws Exception {
runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/kt1001.kt");
diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java
index 72eefa6..1bcf440 100644
--- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java
+++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java
@@ -5627,6 +5627,12 @@
}
@Test
+ @TestMetadata("inlinedBreakContinueFeatureDisabled.kt")
+ public void testInlinedBreakContinueFeatureDisabled() throws Exception {
+ runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/inlinedBreakContinueFeatureDisabled.kt");
+ }
+
+ @Test
@TestMetadata("kt1001.kt")
public void testKt1001() throws Exception {
runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/kt1001.kt");
diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirBreakOrContinueJumpsAcrossFunctionBoundaryChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirBreakOrContinueJumpsAcrossFunctionBoundaryChecker.kt
index 40ca7c0..8284992 100644
--- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirBreakOrContinueJumpsAcrossFunctionBoundaryChecker.kt
+++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirBreakOrContinueJumpsAcrossFunctionBoundaryChecker.kt
@@ -14,8 +14,6 @@
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.expressions.impl.FirNoReceiverExpression
-import org.jetbrains.kotlin.fir.resolvedSymbol
-import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol
object FirBreakOrContinueJumpsAcrossFunctionBoundaryChecker : FirLoopJumpChecker() {
override fun check(expression: FirLoopJump, context: CheckerContext, reporter: DiagnosticReporter) {
@@ -36,8 +34,19 @@
when (element) {
expression -> {
- if (errorPathElements.any()) {
- reporter.reportOn(expression.source, FirErrors.BREAK_OR_CONTINUE_JUMPS_ACROSS_FUNCTION_BOUNDARY, context)
+ if (errorPathElements.isNotEmpty()) {
+ val hasNonInline = errorPathElements.any {
+ when(it) {
+ is FirAnonymousFunction -> it.inlineStatus != InlineStatus.Inline
+ is FirAnonymousFunctionExpression -> it.anonymousFunction.inlineStatus != InlineStatus.Inline
+ else -> true
+ }}
+ if (hasNonInline) {
+ reporter.reportOn(expression.source, FirErrors.BREAK_OR_CONTINUE_JUMPS_ACROSS_FUNCTION_BOUNDARY, context)
+ } else if (!allowInlined) {
+ reporter.reportOn(expression.source, FirErrors.UNSUPPORTED_FEATURE,
+ LanguageFeature.BreakContinueInInlineLambdas to context.languageVersionSettings, context)
+ }
}
return true
}
@@ -63,17 +72,7 @@
if (findPathAndCheck(element.extensionReceiver) || findPathAndCheck(element.dispatchReceiver)) {
return true
}
-
- val symbol = if (allowInlined) element.calleeReference.resolvedSymbol as? FirFunctionSymbol else null
- element.arguments.forEachIndexed { i, argument ->
- val expressionToCheck =
- if (symbol?.resolvedStatus?.isInline == true
- && !symbol.valueParameterSymbols[i].run { isNoinline || isCrossinline }
- ) argument.tryInline() else argument
- if (findPathAndCheck(expressionToCheck)) {
- return true
- }
- }
+ if (element.arguments.any(::findPathAndCheck)) return true
}
is FirCall -> {
for (argument in element.arguments) {
diff --git a/compiler/frontend/cfg/src/org/jetbrains/kotlin/cfg/ControlFlowProcessor.kt b/compiler/frontend/cfg/src/org/jetbrains/kotlin/cfg/ControlFlowProcessor.kt
index ed015f8..cc5b93c 100644
--- a/compiler/frontend/cfg/src/org/jetbrains/kotlin/cfg/ControlFlowProcessor.kt
+++ b/compiler/frontend/cfg/src/org/jetbrains/kotlin/cfg/ControlFlowProcessor.kt
@@ -938,7 +938,13 @@
// See generateInitializersForClassOrObject && generateDeclarationForLocalClassOrObjectIfNeeded
labelExprEnclosingFunc is ConstructorDescriptor && !labelExprEnclosingFunc.isPrimary
) {
- trace.report(BREAK_OR_CONTINUE_JUMPS_ACROSS_FUNCTION_BOUNDARY.on(jumpExpression))
+ val dependsOnInlineLambdas = !skipInlineFunctions &&
+ getEnclosingFunctionDescriptor(bindingContext, jumpExpression, true) == getEnclosingFunctionDescriptor(bindingContext, jumpTarget, true)
+ if (dependsOnInlineLambdas) {
+ trace.report(UNSUPPORTED_FEATURE.on(jumpExpression, BreakContinueInInlineLambdas to languageVersionSettings))
+ } else {
+ trace.report(BREAK_OR_CONTINUE_JUMPS_ACROSS_FUNCTION_BOUNDARY.on(jumpExpression))
+ }
}
false
} else {
diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/inlinedBreakContinueFeatureDisabled.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/inlinedBreakContinueFeatureDisabled.kt
new file mode 100644
index 0000000..28e8105
--- /dev/null
+++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/inlinedBreakContinueFeatureDisabled.kt
@@ -0,0 +1,39 @@
+// FIR_IDENTICAL
+// LANGUAGE: -BreakContinueInInlineLambdas
+// TARGET_BACKEND: JVM_IR
+// IGNORE_ERRORS
+// WITH_STDLIB
+
+inline fun foo(block: () -> Unit) { block() }
+
+inline fun bar(block1: () -> Unit, noinline block2: () -> Unit, crossinline block3: () -> Unit = {}) {
+ block1()
+ block2()
+ block3()
+}
+
+fun test1() {
+ while (true) {
+ foo { <!UNSUPPORTED_FEATURE!>break<!> }
+ foo { <!UNSUPPORTED_FEATURE!>continue<!> }
+ foo(fun () { <!UNSUPPORTED_FEATURE!>break<!> })
+ foo(fun () { <!UNSUPPORTED_FEATURE!>continue<!> })
+ }
+}
+
+fun test2() {
+ while (true) {
+ bar({<!UNSUPPORTED_FEATURE!>break<!>}, {})
+ bar({<!UNSUPPORTED_FEATURE!>continue<!>}, {})
+
+ bar(fun () {<!UNSUPPORTED_FEATURE!>break<!>}, fun () {})
+ bar(fun () {<!UNSUPPORTED_FEATURE!>continue<!>}, fun () {})
+ }
+}
+
+fun test3() {
+ while (true) {
+ bar({}, { <!BREAK_OR_CONTINUE_JUMPS_ACROSS_FUNCTION_BOUNDARY!>break<!> }, { <!BREAK_OR_CONTINUE_JUMPS_ACROSS_FUNCTION_BOUNDARY!>break<!> })
+ bar({}, { <!BREAK_OR_CONTINUE_JUMPS_ACROSS_FUNCTION_BOUNDARY!>continue<!> }, { <!BREAK_OR_CONTINUE_JUMPS_ACROSS_FUNCTION_BOUNDARY!>break<!> })
+ }
+}
diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/inlinedBreakContinueFeatureDisabled.txt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/inlinedBreakContinueFeatureDisabled.txt
new file mode 100644
index 0000000..3d91ae6
--- /dev/null
+++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/inlinedBreakContinueFeatureDisabled.txt
@@ -0,0 +1,8 @@
+package
+
+public inline fun bar(/*0*/ block1: () -> kotlin.Unit, /*1*/ noinline block2: () -> kotlin.Unit, /*2*/ crossinline block3: () -> kotlin.Unit = ...): kotlin.Unit
+public inline fun foo(/*0*/ block: () -> kotlin.Unit): kotlin.Unit
+public fun test1(): kotlin.Unit
+public fun test2(): kotlin.Unit
+public fun test3(): kotlin.Unit
+
diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java
index 0615dbd..6669f71 100644
--- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java
+++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java
@@ -5633,6 +5633,12 @@
}
@Test
+ @TestMetadata("inlinedBreakContinueFeatureDisabled.kt")
+ public void testInlinedBreakContinueFeatureDisabled() throws Exception {
+ runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/inlinedBreakContinueFeatureDisabled.kt");
+ }
+
+ @Test
@TestMetadata("kt1001.kt")
public void testKt1001() throws Exception {
runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/kt1001.kt");
diff --git a/compiler/tests-spec/testData/diagnostics/notLinked/contracts/analysis/controlFlow/unreachableCode/neg/1.fir.kt b/compiler/tests-spec/testData/diagnostics/notLinked/contracts/analysis/controlFlow/unreachableCode/neg/1.fir.kt
index 3c61b57..a9d8b20 100644
--- a/compiler/tests-spec/testData/diagnostics/notLinked/contracts/analysis/controlFlow/unreachableCode/neg/1.fir.kt
+++ b/compiler/tests-spec/testData/diagnostics/notLinked/contracts/analysis/controlFlow/unreachableCode/neg/1.fir.kt
@@ -6,14 +6,14 @@
fun case_1(value_1: Boolean) {
while (value_1) {
funWithExactlyOnceCallsInPlace {
- <!BREAK_OR_CONTINUE_JUMPS_ACROSS_FUNCTION_BOUNDARY!>break<!>
+ <!UNSUPPORTED_FEATURE!>break<!>
}
println("1")
}
loop@ for (i in 0..10) {
funWithExactlyOnceCallsInPlace {
- <!BREAK_OR_CONTINUE_JUMPS_ACROSS_FUNCTION_BOUNDARY!>break@loop<!>
+ <!UNSUPPORTED_FEATURE!>break@loop<!>
}
println("1")
}
@@ -23,14 +23,14 @@
fun case_2(value_1: Boolean) {
for (i in 0..10) {
funWithExactlyOnceCallsInPlace {
- <!BREAK_OR_CONTINUE_JUMPS_ACROSS_FUNCTION_BOUNDARY!>continue<!>
+ <!UNSUPPORTED_FEATURE!>continue<!>
}
println("1")
}
loop@ while (value_1) {
funWithExactlyOnceCallsInPlace {
- <!BREAK_OR_CONTINUE_JUMPS_ACROSS_FUNCTION_BOUNDARY!>continue@loop<!>
+ <!UNSUPPORTED_FEATURE!>continue@loop<!>
}
println("1")
}
diff --git a/compiler/tests-spec/testData/diagnostics/notLinked/contracts/analysis/controlFlow/unreachableCode/neg/1.kt b/compiler/tests-spec/testData/diagnostics/notLinked/contracts/analysis/controlFlow/unreachableCode/neg/1.kt
index 80cc42e..d9e8d21 100644
--- a/compiler/tests-spec/testData/diagnostics/notLinked/contracts/analysis/controlFlow/unreachableCode/neg/1.kt
+++ b/compiler/tests-spec/testData/diagnostics/notLinked/contracts/analysis/controlFlow/unreachableCode/neg/1.kt
@@ -14,14 +14,14 @@
fun case_1(value_1: Boolean) {
while (value_1) {
funWithExactlyOnceCallsInPlace {
- <!BREAK_OR_CONTINUE_JUMPS_ACROSS_FUNCTION_BOUNDARY!>break<!>
+ <!UNSUPPORTED_FEATURE!>break<!>
}
println("1")
}
loop@ for (i in 0..10) {
funWithExactlyOnceCallsInPlace {
- <!BREAK_OR_CONTINUE_JUMPS_ACROSS_FUNCTION_BOUNDARY!>break@loop<!>
+ <!UNSUPPORTED_FEATURE!>break@loop<!>
}
println("1")
}
@@ -31,14 +31,14 @@
fun case_2(value_1: Boolean) {
for (i in 0..10) {
funWithExactlyOnceCallsInPlace {
- <!BREAK_OR_CONTINUE_JUMPS_ACROSS_FUNCTION_BOUNDARY!>continue<!>
+ <!UNSUPPORTED_FEATURE!>continue<!>
}
println("1")
}
loop@ while (value_1) {
funWithExactlyOnceCallsInPlace {
- <!BREAK_OR_CONTINUE_JUMPS_ACROSS_FUNCTION_BOUNDARY!>continue@loop<!>
+ <!UNSUPPORTED_FEATURE!>continue@loop<!>
}
println("1")
}
diff --git a/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/checkers/FirSerializationPluginClassChecker.kt b/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/checkers/FirSerializationPluginClassChecker.kt
index 0550df6..0e7283b 100644
--- a/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/checkers/FirSerializationPluginClassChecker.kt
+++ b/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/checkers/FirSerializationPluginClassChecker.kt
@@ -8,6 +8,7 @@
import org.jetbrains.kotlin.KtSourceElement
import org.jetbrains.kotlin.config.KotlinCompilerVersion
import org.jetbrains.kotlin.diagnostics.*
+import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirClassChecker
import org.jetbrains.kotlin.fir.analysis.checkers.isInlineClass
@@ -18,6 +19,7 @@
import org.jetbrains.kotlin.fir.moduleData
import org.jetbrains.kotlin.fir.resolve.defaultType
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
+import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.impl.*
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.name.*
@@ -30,6 +32,7 @@
import org.jetbrains.kotlinx.serialization.compiler.fir.services.findTypeSerializerOrContextUnchecked
import org.jetbrains.kotlinx.serialization.compiler.fir.services.serializablePropertiesProvider
import org.jetbrains.kotlinx.serialization.compiler.fir.services.versionReader
+import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationAnnotations
object FirSerializationPluginClassChecker : FirClassChecker() {
private val JAVA_SERIALIZABLE_ID = ClassId.topLevel(FqName("java.io.Serializable"))