KT-1436 Nicer compiler errors when the feature isn't supported

This commit is contained in:
Pavel Mikhailovskii
2022-10-14 09:42:56 +02:00
committed by teamcity
parent e6900ec0f2
commit 1215ae0fe7
11 changed files with 103 additions and 24 deletions
@@ -5626,6 +5626,12 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag
runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/initializationInLocalViaExplicitThis_before.kt");
}
@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 {
@@ -5626,6 +5626,12 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti
runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/initializationInLocalViaExplicitThis_before.kt");
}
@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 {
@@ -5626,6 +5626,12 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac
runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/initializationInLocalViaExplicitThis_before.kt");
}
@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 {
@@ -14,8 +14,6 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
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 @@ object FirBreakOrContinueJumpsAcrossFunctionBoundaryChecker : FirLoopJumpChecker
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 @@ object FirBreakOrContinueJumpsAcrossFunctionBoundaryChecker : FirLoopJumpChecker
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) {
@@ -938,7 +938,13 @@ class ControlFlowProcessor(
// 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 {
@@ -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<!> })
}
}
@@ -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
@@ -5632,6 +5632,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest {
runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/initializationInLocalViaExplicitThis_before.kt");
}
@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 {
@@ -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_1(value_1: Boolean) {
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")
}
@@ -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_1(value_1: Boolean) {
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")
}
@@ -8,6 +8,7 @@ package org.jetbrains.kotlinx.serialization.compiler.fir.checkers
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.expressions.*
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.dependencySeria
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"))