diff --git a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/Main.kt b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/Main.kt index 1901fc9de3c..5aa202e41f5 100644 --- a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/Main.kt +++ b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/Main.kt @@ -34,6 +34,7 @@ fun main(args: Array) { alias("TryExpressionChecker") alias("WhenExpressionChecker") alias("LoopExpressionChecker") + alias("LoopJumpChecker") alias("LogicExpressionChecker") alias("ReturnExpressionChecker") alias("BlockChecker") diff --git a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt index a9d06496f65..9369e3e8d7e 100644 --- a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt +++ b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt @@ -59,6 +59,7 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") { val ASSIGNMENT_IN_EXPRESSION_CONTEXT by error() val BREAK_OR_CONTINUE_OUTSIDE_A_LOOP by error() val NOT_A_LOOP_LABEL by error() + val BREAK_OR_CONTINUE_JUMPS_ACROSS_FUNCTION_BOUNDARY by error() val VARIABLE_EXPECTED by error(PositioningStrategy.ASSIGNMENT_LHS) val DELEGATION_IN_INTERFACE by error() val DELEGATION_NOT_TO_INTERFACE by error() diff --git a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/checkers/expression/ComposedExpressionCheckers.kt b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/checkers/expression/ComposedExpressionCheckers.kt index 6e0a9520cf5..36a9bd54c81 100644 --- a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/checkers/expression/ComposedExpressionCheckers.kt +++ b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/checkers/expression/ComposedExpressionCheckers.kt @@ -29,6 +29,8 @@ class ComposedExpressionCheckers : ExpressionCheckers() { get() = _whenExpressionCheckers override val loopExpressionCheckers: Set get() = _loopExpressionCheckers + override val loopJumpCheckers: Set + get() = _loopJumpCheckers override val logicExpressionCheckers: Set get() = _logicExpressionCheckers override val returnExpressionCheckers: Set @@ -76,6 +78,7 @@ class ComposedExpressionCheckers : ExpressionCheckers() { private val _tryExpressionCheckers: MutableSet = mutableSetOf() private val _whenExpressionCheckers: MutableSet = mutableSetOf() private val _loopExpressionCheckers: MutableSet = mutableSetOf() + private val _loopJumpCheckers: MutableSet = mutableSetOf() private val _logicExpressionCheckers: MutableSet = mutableSetOf() private val _returnExpressionCheckers: MutableSet = mutableSetOf() private val _blockCheckers: MutableSet = mutableSetOf() @@ -106,6 +109,7 @@ class ComposedExpressionCheckers : ExpressionCheckers() { _tryExpressionCheckers += checkers.tryExpressionCheckers _whenExpressionCheckers += checkers.whenExpressionCheckers _loopExpressionCheckers += checkers.loopExpressionCheckers + _loopJumpCheckers += checkers.loopJumpCheckers _logicExpressionCheckers += checkers.logicExpressionCheckers _returnExpressionCheckers += checkers.returnExpressionCheckers _blockCheckers += checkers.blockCheckers diff --git a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/checkers/expression/ExpressionCheckers.kt b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/checkers/expression/ExpressionCheckers.kt index 08b0da58aa5..73fdb57468e 100644 --- a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/checkers/expression/ExpressionCheckers.kt +++ b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/checkers/expression/ExpressionCheckers.kt @@ -25,6 +25,7 @@ abstract class ExpressionCheckers { open val tryExpressionCheckers: Set = emptySet() open val whenExpressionCheckers: Set = emptySet() open val loopExpressionCheckers: Set = emptySet() + open val loopJumpCheckers: Set = emptySet() open val logicExpressionCheckers: Set = emptySet() open val returnExpressionCheckers: Set = emptySet() open val blockCheckers: Set = emptySet() @@ -53,6 +54,7 @@ abstract class ExpressionCheckers { @CheckersComponentInternal internal val allTryExpressionCheckers: Set by lazy { tryExpressionCheckers + basicExpressionCheckers } @CheckersComponentInternal internal val allWhenExpressionCheckers: Set by lazy { whenExpressionCheckers + basicExpressionCheckers } @CheckersComponentInternal internal val allLoopExpressionCheckers: Set by lazy { loopExpressionCheckers + basicExpressionCheckers } + @CheckersComponentInternal internal val allLoopJumpCheckers: Set by lazy { loopJumpCheckers + basicExpressionCheckers } @CheckersComponentInternal internal val allLogicExpressionCheckers: Set by lazy { logicExpressionCheckers + basicExpressionCheckers } @CheckersComponentInternal internal val allReturnExpressionCheckers: Set by lazy { returnExpressionCheckers + basicExpressionCheckers } @CheckersComponentInternal internal val allBlockCheckers: Set by lazy { blockCheckers + basicExpressionCheckers } diff --git a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirExpressionCheckerAliases.kt b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirExpressionCheckerAliases.kt index bfb907fabbb..c687cd5bb4f 100644 --- a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirExpressionCheckerAliases.kt +++ b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirExpressionCheckerAliases.kt @@ -24,6 +24,7 @@ import org.jetbrains.kotlin.fir.expressions.FirEqualityOperatorCall import org.jetbrains.kotlin.fir.expressions.FirFunctionCall import org.jetbrains.kotlin.fir.expressions.FirGetClassCall import org.jetbrains.kotlin.fir.expressions.FirLoop +import org.jetbrains.kotlin.fir.expressions.FirLoopJump import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccess import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression import org.jetbrains.kotlin.fir.expressions.FirResolvedQualifier @@ -46,6 +47,7 @@ typealias FirVariableAssignmentChecker = FirExpressionChecker typealias FirWhenExpressionChecker = FirExpressionChecker typealias FirLoopExpressionChecker = FirExpressionChecker +typealias FirLoopJumpChecker = FirExpressionChecker typealias FirLogicExpressionChecker = FirExpressionChecker typealias FirReturnExpressionChecker = FirExpressionChecker typealias FirBlockChecker = FirExpressionChecker diff --git a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt index a842b8081fd..57837edb110 100644 --- a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt +++ b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt @@ -51,6 +51,7 @@ import org.jetbrains.kotlin.psi.KtDestructuringDeclaration import org.jetbrains.kotlin.psi.KtElement import org.jetbrains.kotlin.psi.KtEnumEntry import org.jetbrains.kotlin.psi.KtExpression +import org.jetbrains.kotlin.psi.KtExpressionWithLabel import org.jetbrains.kotlin.psi.KtFunction import org.jetbrains.kotlin.psi.KtIfExpression import org.jetbrains.kotlin.psi.KtImportDirective @@ -97,6 +98,7 @@ object FirErrors { val ASSIGNMENT_IN_EXPRESSION_CONTEXT by error0() val BREAK_OR_CONTINUE_OUTSIDE_A_LOOP by error0() val NOT_A_LOOP_LABEL by error0() + val BREAK_OR_CONTINUE_JUMPS_ACROSS_FUNCTION_BOUNDARY by error0() val VARIABLE_EXPECTED by error0(SourceElementPositioningStrategies.ASSIGNMENT_LHS) val DELEGATION_IN_INTERFACE by error0() val DELEGATION_NOT_TO_INTERFACE by error0() diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/cfa/FirReturnsImpliesAnalyzer.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/cfa/FirReturnsImpliesAnalyzer.kt index aa1780356b0..bf3b1b44e44 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/cfa/FirReturnsImpliesAnalyzer.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/cfa/FirReturnsImpliesAnalyzer.kt @@ -218,7 +218,7 @@ object FirReturnsImpliesAnalyzer : FirControlFlowChecker() { } private val CheckerContext.containingProperty: FirProperty? - get() = (containingDeclarations.asReversed().firstOrNull { it is FirProperty } as? FirProperty) + get() = (containingDeclarations.lastOrNull { it is FirProperty } as? FirProperty) private fun FirFunction.getParameterType(symbol: FirBasedSymbol<*>, context: CheckerContext): ConeKotlinType? { val typeRef = if (this.symbol == symbol) { diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/CommonExpressionCheckers.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/CommonExpressionCheckers.kt index 87096599629..2864c9bfde1 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/CommonExpressionCheckers.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/CommonExpressionCheckers.kt @@ -20,7 +20,7 @@ object CommonExpressionCheckers : ExpressionCheckers() { get() = setOf( FirReservedUnderscoreExpressionChecker, FirExpressionAnnotationChecker, - FirDeprecationChecker, + FirDeprecationChecker ) override val qualifiedAccessCheckers: Set @@ -76,6 +76,11 @@ object CommonExpressionCheckers : ExpressionCheckers() { FirLoopConditionChecker, ) + override val loopJumpCheckers: Set + get() = setOf( + FirBreakOrContinueJumpsAcrossFunctionBoundaryChecker + ) + override val logicExpressionCheckers: Set get() = setOf( FirLogicExpressionTypeChecker, diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirLoopJumpChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirLoopJumpChecker.kt new file mode 100644 index 00000000000..c032976ae27 --- /dev/null +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirLoopJumpChecker.kt @@ -0,0 +1,101 @@ +/* + * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.fir.analysis.checkers.expression + +import org.jetbrains.kotlin.fir.FirElement +import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext +import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors +import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn +import org.jetbrains.kotlin.fir.declarations.* +import org.jetbrains.kotlin.fir.expressions.* + +object FirBreakOrContinueJumpsAcrossFunctionBoundaryChecker : FirLoopJumpChecker() { + override fun check(expression: FirLoopJump, context: CheckerContext, reporter: DiagnosticReporter) { + val errorPathElements = ArrayDeque() + + fun findPathAndCheck(element: FirElement?): Boolean { + fun findPathAndCheckWithAddingErrorElement(errorElement: FirElement, checkElement: FirElement?): Boolean { + errorPathElements.addLast(errorElement) + val result = findPathAndCheck(checkElement) + errorPathElements.removeLast() + return result + } + + if (element == null) { + return false + } + + when (element) { + expression -> { + if (errorPathElements.any()) { + reporter.reportOn(expression.source, FirErrors.BREAK_OR_CONTINUE_JUMPS_ACROSS_FUNCTION_BOUNDARY, context) + } + return true + } + is FirBlock -> { + for (statement in element.statements) { + if (findPathAndCheck(statement)) { + return true + } + } + } + is FirWhenExpression -> { + for (branch in element.branches) { + if (findPathAndCheck(branch.result)) { + return true + } + } + } + is FirVariable -> return findPathAndCheck(element.initializer) + is FirWrappedExpression -> return findPathAndCheck(element.expression) + is FirCall -> { + for (argument in element.arguments) { + if (findPathAndCheck(argument)) { + return true + } + } + } + is FirClass -> { + errorPathElements.addLast(element) + for (declaration in element.declarations) { + if (findPathAndCheck(declaration)) { + errorPathElements.removeLast() + return true + } + } + errorPathElements.removeLast() + } + is FirFunction -> { + if (findPathAndCheckWithAddingErrorElement(element, element.body)) { + return true + } + + if (element is FirConstructor) { + val argumentList = element.delegatedConstructor?.argumentList + if (argumentList != null) { + errorPathElements.addLast(element) + for (argument in argumentList.arguments) { + if (findPathAndCheck(argument)) { + errorPathElements.removeLast() + return true + } + } + errorPathElements.removeLast() + } + } + } + is FirAnonymousInitializer -> return findPathAndCheckWithAddingErrorElement(element, element.body) + is FirAnonymousObjectExpression -> return findPathAndCheckWithAddingErrorElement(element, element.anonymousObject) + is FirAnonymousFunctionExpression -> return findPathAndCheckWithAddingErrorElement(element, element.anonymousFunction) + } + + return false + } + + findPathAndCheck(expression.target.labeledElement.block) + } +} \ No newline at end of file diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/collectors/components/ExpressionCheckersDiagnosticComponent.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/collectors/components/ExpressionCheckersDiagnosticComponent.kt index 2c5ec5c5dcb..441fe21b023 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/collectors/components/ExpressionCheckersDiagnosticComponent.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/collectors/components/ExpressionCheckersDiagnosticComponent.kt @@ -117,6 +117,14 @@ class ExpressionCheckersDiagnosticComponent( checkers.allReturnExpressionCheckers.check(returnExpression, data, reporter) } + override fun visitBreakExpression(breakExpression: FirBreakExpression, data: CheckerContext) { + checkers.allLoopJumpCheckers.check(breakExpression, data, reporter) + } + + override fun visitContinueExpression(continueExpression: FirContinueExpression, data: CheckerContext) { + checkers.allLoopJumpCheckers.check(continueExpression, data, reporter) + } + override fun visitBlock(block: FirBlock, data: CheckerContext) { checkers.allBlockCheckers.check(block, data, reporter) } diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt index 64aa4902e2f..1586a4b76bc 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt @@ -67,6 +67,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ASSIGN_OPERATOR_A import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.BACKING_FIELD_IN_INTERFACE import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.BOUNDS_NOT_ALLOWED_IF_BOUNDED_BY_TYPE_PARAMETER import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.BOUND_ON_TYPE_ALIAS_PARAMETER_NOT_ALLOWED +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.BREAK_OR_CONTINUE_JUMPS_ACROSS_FUNCTION_BOUNDARY import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.BREAK_OR_CONTINUE_OUTSIDE_A_LOOP import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.CALLABLE_REFERENCE_LHS_NOT_A_CLASS import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.CALLABLE_REFERENCE_TO_ANNOTATION_CONSTRUCTOR @@ -456,6 +457,7 @@ class FirDefaultErrorMessages { // map.put(EXPRESSION_REQUIRED, ...) // & map.put(BREAK_OR_CONTINUE_OUTSIDE_A_LOOP, "'break' and 'continue' are only allowed inside a loop") map.put(NOT_A_LOOP_LABEL, "The label does not denote a loop") // * + map.put(BREAK_OR_CONTINUE_JUMPS_ACROSS_FUNCTION_BOUNDARY, "'break' or 'continue' jumps across a function or a class boundary") map.put(VARIABLE_EXPECTED, "Variable expected") map.put(DELEGATION_IN_INTERFACE, "Interfaces cannot use delegation") map.put(DELEGATION_NOT_TO_INTERFACE, "Only interfaces can be delegated to") diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/breakInsideLocal.fir.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/breakInsideLocal.fir.kt index 1cd78622a47..3ca844f3c3e 100644 --- a/compiler/testData/diagnostics/tests/controlFlowAnalysis/breakInsideLocal.fir.kt +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/breakInsideLocal.fir.kt @@ -1,7 +1,7 @@ fun test() { while (true) { fun local1() { - break + break } } } @@ -9,7 +9,7 @@ fun test() { fun test2() { while (true) { { - continue + continue } } } @@ -18,11 +18,11 @@ fun test3() { while (true) { class LocalClass { init { - continue + continue } fun foo() { - break + break } } } @@ -32,7 +32,7 @@ fun test4() { while (true) { object: Any() { init { - break + break } } } @@ -42,10 +42,10 @@ fun test5() { while (true) { class LocalClass(val x: Int) { constructor() : this(42) { - break + break } constructor(y: Double) : this(y.toInt()) { - continue + continue } } } @@ -55,10 +55,10 @@ fun test6() { while (true) { class LocalClass(val x: Int) { init { - break + break } init { - continue + continue } } } @@ -68,10 +68,10 @@ fun test7() { while (true) { class LocalClass { val x: Int = if (true) { - break + break } else { - continue + continue } } } @@ -80,7 +80,7 @@ fun test7() { fun test8() { while (true) { class LocalClass(val x: Int) { - constructor() : this(if (true) { 42 } else { break }) + constructor() : this(if (true) { 42 } else { break }) } } -} \ No newline at end of file +} diff --git a/compiler/testData/diagnostics/tests/controlStructures/jumpAcrossFunctionBoundary.fir.kt b/compiler/testData/diagnostics/tests/controlStructures/jumpAcrossFunctionBoundary.fir.kt deleted file mode 100644 index 188f881d28d..00000000000 --- a/compiler/testData/diagnostics/tests/controlStructures/jumpAcrossFunctionBoundary.fir.kt +++ /dev/null @@ -1,17 +0,0 @@ -fun call(f: () -> Unit) = f() - -fun f1() { - outer@ while (true) { - call { - break@outer - } - } -} - -fun f2() { - do { - fun inner() { - continue - } - } while (true) -} diff --git a/compiler/testData/diagnostics/tests/controlStructures/jumpAcrossFunctionBoundary.kt b/compiler/testData/diagnostics/tests/controlStructures/jumpAcrossFunctionBoundary.kt index 98b7d7f6f5f..55fb5d90900 100644 --- a/compiler/testData/diagnostics/tests/controlStructures/jumpAcrossFunctionBoundary.kt +++ b/compiler/testData/diagnostics/tests/controlStructures/jumpAcrossFunctionBoundary.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL fun call(f: () -> Unit) = f() fun f1() { diff --git a/compiler/testData/diagnostics/tests/incompleteCode/pseudocodeTraverseNextInstructions.fir.kt b/compiler/testData/diagnostics/tests/incompleteCode/pseudocodeTraverseNextInstructions.fir.kt index 40f20ac8c18..129cbe93a7f 100644 --- a/compiler/testData/diagnostics/tests/incompleteCode/pseudocodeTraverseNextInstructions.fir.kt +++ b/compiler/testData/diagnostics/tests/incompleteCode/pseudocodeTraverseNextInstructions.fir.kt @@ -3,6 +3,6 @@ package b fun foo() { for (i in collection) { { - break + break } } 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 4488031200f..38f68df291a 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 @@ -5,14 +5,14 @@ fun case_1(value_1: Boolean) { while (value_1) { funWithExactlyOnceCallsInPlace { - break + break } println("1") } loop@ for (i in 0..10) { funWithExactlyOnceCallsInPlace { - break@loop + break@loop } println("1") } @@ -22,14 +22,14 @@ fun case_1(value_1: Boolean) { fun case_2(value_1: Boolean) { for (i in 0..10) { funWithExactlyOnceCallsInPlace { - continue + continue } println("1") } loop@ while (value_1) { funWithExactlyOnceCallsInPlace { - continue@loop + continue@loop } println("1") } diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt index 057f335888d..d24067fc164 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt @@ -36,6 +36,7 @@ import org.jetbrains.kotlin.psi.KtDestructuringDeclaration import org.jetbrains.kotlin.psi.KtElement import org.jetbrains.kotlin.psi.KtEnumEntry import org.jetbrains.kotlin.psi.KtExpression +import org.jetbrains.kotlin.psi.KtExpressionWithLabel import org.jetbrains.kotlin.psi.KtFunction import org.jetbrains.kotlin.psi.KtIfExpression import org.jetbrains.kotlin.psi.KtImportDirective @@ -132,6 +133,12 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert token, ) } + add(FirErrors.BREAK_OR_CONTINUE_JUMPS_ACROSS_FUNCTION_BOUNDARY) { firDiagnostic -> + BreakOrContinueJumpsAcrossFunctionBoundaryImpl( + firDiagnostic as FirPsiDiagnostic, + token, + ) + } add(FirErrors.VARIABLE_EXPECTED) { firDiagnostic -> VariableExpectedImpl( firDiagnostic as FirPsiDiagnostic, diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt index 76ecec8d595..b18417a8f5d 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt @@ -45,6 +45,7 @@ import org.jetbrains.kotlin.psi.KtDestructuringDeclaration import org.jetbrains.kotlin.psi.KtElement import org.jetbrains.kotlin.psi.KtEnumEntry import org.jetbrains.kotlin.psi.KtExpression +import org.jetbrains.kotlin.psi.KtExpressionWithLabel import org.jetbrains.kotlin.psi.KtFunction import org.jetbrains.kotlin.psi.KtIfExpression import org.jetbrains.kotlin.psi.KtImportDirective @@ -122,6 +123,10 @@ sealed class KtFirDiagnostic : KtDiagnosticWithPsi { override val diagnosticClass get() = NotALoopLabel::class } + abstract class BreakOrContinueJumpsAcrossFunctionBoundary : KtFirDiagnostic() { + override val diagnosticClass get() = BreakOrContinueJumpsAcrossFunctionBoundary::class + } + abstract class VariableExpected : KtFirDiagnostic() { override val diagnosticClass get() = VariableExpected::class } diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt index 67338879666..b047d3be3fc 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt @@ -47,6 +47,7 @@ import org.jetbrains.kotlin.psi.KtDestructuringDeclaration import org.jetbrains.kotlin.psi.KtElement import org.jetbrains.kotlin.psi.KtEnumEntry import org.jetbrains.kotlin.psi.KtExpression +import org.jetbrains.kotlin.psi.KtExpressionWithLabel import org.jetbrains.kotlin.psi.KtFunction import org.jetbrains.kotlin.psi.KtIfExpression import org.jetbrains.kotlin.psi.KtImportDirective @@ -156,6 +157,13 @@ internal class NotALoopLabelImpl( override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic) } +internal class BreakOrContinueJumpsAcrossFunctionBoundaryImpl( + firDiagnostic: FirPsiDiagnostic, + override val token: ValidityToken, +) : KtFirDiagnostic.BreakOrContinueJumpsAcrossFunctionBoundary(), KtAbstractFirDiagnostic { + override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic) +} + internal class VariableExpectedImpl( firDiagnostic: FirPsiDiagnostic, override val token: ValidityToken,