diff --git a/pkg/_fe_analyzer_shared/lib/src/flow_analysis/flow_analysis.dart b/pkg/_fe_analyzer_shared/lib/src/flow_analysis/flow_analysis.dart index 22267b8897e..a67485318d6 100644 --- a/pkg/_fe_analyzer_shared/lib/src/flow_analysis/flow_analysis.dart +++ b/pkg/_fe_analyzer_shared/lib/src/flow_analysis/flow_analysis.dart @@ -600,6 +600,10 @@ abstract class FlowAnalysis? ssaNodeForTesting(Variable variable); + /// Call this method just after visiting a `case` or `default` body. See + /// [switchStatement_expressionEnd] for details. + void switchStatement_afterCase(); + /// Call this method just before visiting a `case` or `default` clause. See /// [switchStatement_expressionEnd] for details. void switchStatement_beginAlternative(); @@ -650,6 +654,7 @@ abstract class FlowAnalysis _wrapped.switchStatement_afterCase()); + } + @override void switchStatement_beginAlternative() { _wrap('switchStatement_beginAlternative()', @@ -3937,6 +3948,13 @@ class _FlowAnalysisImpl? ssaNodeForTesting(Variable variable) => _current .variableInfo[promotionKeyStore.keyForVariable(variable)]?.ssaNode; + @override + void switchStatement_afterCase() { + _SwitchStatementContext context = + _stack.last as _SwitchStatementContext; + context._breakModel = _join(context._breakModel, _current); + } + @override void switchStatement_beginAlternative() { _SwitchAlternativesContext context = @@ -3959,13 +3977,17 @@ class _FlowAnalysisImpl; FlowModel? breakState = context._breakModel; - // It is allowed to "fall off" the end of a switch statement, so join the - // current state to any breaks that were found previously. - breakState = _join(breakState, _current); - - // And, if there is an implicit fall-through default, join it to any breaks. + // If there is an implicit fall-through default, join it to any breaks. if (!isExhaustive) breakState = _join(breakState, context._previous); + // If there were no breaks (neither implicit nor explicit), then + // `breakState` will be `null`. This means this is an empty switch + // statement and the type of the scrutinee is an exhaustive type. This + // could happen, for instance, if the scrutinee type is the empty record + // type. We need to consider the code after the switch statement reachable + // if this happens. + breakState ??= context._previous; + _current = breakState.unsplit(); } @@ -4850,6 +4872,9 @@ class _LegacyTypePromotion hasDefault = true; } _dispatchList(member.statements); + _flowAnalysis!.switchStatement_afterCase(); }); } _flowAnalysis!.switchStatement_end(hasDefault);