From d0fc6ec27bbd7507d5abd5fcfba917208706a9d3 Mon Sep 17 00:00:00 2001 From: Paul Berry Date: Fri, 9 Dec 2022 17:20:38 +0000 Subject: [PATCH] Flow analysis: account for implicit `break` at the end of switch statement cases. Bug: https://github.com/dart-lang/sdk/issues/50419 Change-Id: Ie9b0dd3319dd4e28f53082794aa18ed422b0894b Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/274605 Reviewed-by: Konstantin Shcheglov Commit-Queue: Paul Berry --- .../lib/src/flow_analysis/flow_analysis.dart | 35 ++++++++++++++++--- .../lib/src/type_inference/type_analyzer.dart | 2 ++ .../flow_analysis/flow_analysis_test.dart | 27 ++++++++++++++ pkg/nnbd_migration/lib/src/edge_builder.dart | 1 + 4 files changed, 60 insertions(+), 5 deletions(-) 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);