Skip to content

Commit 40a7c7c

Browse files
chloestefantsovaCommit Queue
authored and
Commit Queue
committed
[cfe] Support guard clauses in if-case statements
Part of #49749 Change-Id: I2988ba4dda65b4e54282f892fa377cffed70f25c Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/277984 Reviewed-by: Konstantin Shcheglov <[email protected]> Reviewed-by: Johnni Winther <[email protected]> Commit-Queue: Chloe Stefantsova <[email protected]>
1 parent 166c54b commit 40a7c7c

File tree

131 files changed

+760
-750
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

131 files changed

+760
-750
lines changed

pkg/_fe_analyzer_shared/lib/src/parser/forwarding_listener.dart

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1826,6 +1826,11 @@ class ForwardingListener implements Listener {
18261826
listener?.handleParenthesizedCondition(token, case_, when);
18271827
}
18281828

1829+
@override
1830+
void beginPatternGuard(Token when) {
1831+
listener?.beginPatternGuard(when);
1832+
}
1833+
18291834
@override
18301835
void beginParenthesizedExpressionOrRecordLiteral(Token token) {
18311836
listener?.beginParenthesizedExpressionOrRecordLiteral(token);
@@ -1841,6 +1846,11 @@ class ForwardingListener implements Listener {
18411846
listener?.handleRecordPattern(token, count);
18421847
}
18431848

1849+
@override
1850+
void endPatternGuard(Token token) {
1851+
listener?.endPatternGuard(token);
1852+
}
1853+
18441854
@override
18451855
void endParenthesizedExpression(Token token) {
18461856
listener?.endParenthesizedExpression(token);

pkg/_fe_analyzer_shared/lib/src/parser/listener.dart

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1898,6 +1898,11 @@ class Listener implements UnescapeErrorListener {
18981898
logEvent("ParenthesizedCondition");
18991899
}
19001900

1901+
/// Starts a pattern guard, the expression that follows the 'when' keyword
1902+
void beginPatternGuard(Token when) {
1903+
logEvent("PatternGuard");
1904+
}
1905+
19011906
/// Starts a parenthesized expression or a record literal. Will be ended with
19021907
/// either [endParenthesizedExpression] or [endRecordLiteral].
19031908
void beginParenthesizedExpressionOrRecordLiteral(Token token) {}
@@ -1913,6 +1918,11 @@ class Listener implements UnescapeErrorListener {
19131918
logEvent("RecordPattern");
19141919
}
19151920

1921+
/// End a pattern guard, the expression that follows the 'when' keyword
1922+
void endPatternGuard(Token token) {
1923+
logEvent("PatternGuard");
1924+
}
1925+
19161926
/// End a parenthesized expression.
19171927
/// These may be within the condition expression of a control structure
19181928
/// but will not be the condition of a control structure.

pkg/_fe_analyzer_shared/lib/src/parser/parser_impl.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6324,7 +6324,9 @@ class Parser {
63246324
Token? when;
63256325
if (optional('when', next)) {
63266326
when = token = next;
6327+
listener.beginPatternGuard(when);
63276328
token = parseExpression(token);
6329+
listener.endPatternGuard(when);
63286330
}
63296331
token = ensureCloseParen(token, begin);
63306332
listener.handleParenthesizedCondition(begin, case_, when);

pkg/analyzer/lib/src/fasta/ast_builder.dart

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -471,6 +471,11 @@ class AstBuilder extends StackListener {
471471
push(mixinToken ?? NullValue.Token);
472472
}
473473

474+
@override
475+
void beginPatternGuard(Token when) {
476+
debugEvent("PatternGuard");
477+
}
478+
474479
@override
475480
void beginTopLevelMethod(
476481
Token lastConsumed, Token? augmentToken, Token? externalToken) {
@@ -2797,6 +2802,11 @@ class AstBuilder extends StackListener {
27972802
);
27982803
}
27992804

2805+
@override
2806+
void endPatternGuard(Token token) {
2807+
debugEvent("PatternGuard");
2808+
}
2809+
28002810
@override
28012811
void endRecordLiteral(Token token, int count, Token? constKeyword) {
28022812
debugEvent("RecordLiteral");

pkg/front_end/lib/src/fasta/kernel/body_builder.dart

Lines changed: 46 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2252,13 +2252,15 @@ class BodyBuilder extends StackListenerImpl
22522252
if (case_ != null) {
22532253
// ignore: unused_local_variable
22542254
Expression? guard;
2255+
Scope? scope;
22552256
if (when != null) {
22562257
assert(checkState(token, [
22572258
unionOfKinds([
22582259
ValueKinds.Expression,
22592260
ValueKinds.Generator,
22602261
ValueKinds.ProblemBuilder,
22612262
]),
2263+
ValueKinds.Scope,
22622264
unionOfKinds([
22632265
ValueKinds.Expression,
22642266
ValueKinds.Pattern,
@@ -2270,6 +2272,7 @@ class BodyBuilder extends StackListenerImpl
22702272
]),
22712273
]));
22722274
guard = popForValue();
2275+
scope = pop() as Scope;
22732276
}
22742277
assert(checkState(token, [
22752278
unionOfKinds([
@@ -2286,6 +2289,9 @@ class BodyBuilder extends StackListenerImpl
22862289
libraryFeatures.patterns, case_.charOffset, case_.charCount);
22872290
Pattern pattern = toPattern(pop());
22882291
Expression expression = popForValue();
2292+
if (scope != null) {
2293+
push(scope);
2294+
}
22892295
push(new Condition(expression, new PatternGuard(pattern, guard)));
22902296
} else {
22912297
assert(checkState(token, [
@@ -3430,21 +3436,54 @@ class BodyBuilder extends StackListenerImpl
34303436
}
34313437
}
34323438

3439+
@override
3440+
void beginPatternGuard(Token when) {
3441+
debugEvent("PatternGuard");
3442+
assert(checkState(when, [
3443+
unionOfKinds([
3444+
ValueKinds.Expression,
3445+
ValueKinds.ProblemBuilder,
3446+
ValueKinds.Pattern,
3447+
])
3448+
]));
3449+
3450+
Pattern pattern = toPattern(peek());
3451+
enterLocalScope("then");
3452+
for (VariableDeclaration variable in pattern.declaredVariables) {
3453+
declareVariable(variable, scope);
3454+
typeInferrer.assignedVariables.declare(variable);
3455+
}
3456+
}
3457+
3458+
@override
3459+
void endPatternGuard(Token token) {
3460+
debugEvent("PatternGuard");
3461+
}
3462+
34333463
@override
34343464
void beginThenStatement(Token token) {
34353465
debugEvent("beginThenStatement");
34363466
assert(checkState(token, [ValueKinds.Condition]));
34373467
// This is matched by the call to [deferNode] in
34383468
// [endThenStatement].
34393469
typeInferrer.assignedVariables.beginNode();
3440-
Condition condition = peek() as Condition;
3441-
enterLocalScope("then");
3470+
Condition condition = pop() as Condition;
34423471
PatternGuard? patternGuard = condition.patternGuard;
3443-
if (patternGuard != null) {
3444-
for (VariableDeclaration variable
3445-
in patternGuard.pattern.declaredVariables) {
3446-
declareVariable(variable, scope);
3447-
typeInferrer.assignedVariables.declare(variable);
3472+
if (patternGuard != null && patternGuard.guard != null) {
3473+
assert(checkState(token, [ValueKinds.Scope]));
3474+
Scope scope = pop() as Scope;
3475+
push(condition);
3476+
push(scope);
3477+
} else {
3478+
push(condition);
3479+
// There is no guard, so the scope for "then" isn't entered yet.
3480+
enterLocalScope("then");
3481+
if (patternGuard != null) {
3482+
for (VariableDeclaration variable
3483+
in patternGuard.pattern.declaredVariables) {
3484+
declareVariable(variable, scope);
3485+
typeInferrer.assignedVariables.declare(variable);
3486+
}
34483487
}
34493488
}
34503489
}

pkg/front_end/lib/src/fasta/kernel/macro/annotation_parser.dart

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2054,6 +2054,11 @@ class _MacroListener implements Listener {
20542054
_unknown();
20552055
}
20562056

2057+
@override
2058+
void beginPatternGuard(Token token) {
2059+
_unhandled();
2060+
}
2061+
20572062
@override
20582063
void beginParenthesizedExpressionOrRecordLiteral(Token token) {
20592064
_unhandled();
@@ -2069,6 +2074,11 @@ class _MacroListener implements Listener {
20692074
_unsupported();
20702075
}
20712076

2077+
@override
2078+
void endPatternGuard(Token token) {
2079+
_unhandled();
2080+
}
2081+
20722082
@override
20732083
void endParenthesizedExpression(Token token) {
20742084
_unhandled();

pkg/front_end/lib/src/fasta/type_inference/inference_visitor.dart

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1802,24 +1802,10 @@ class InferenceVisitorImpl extends InferenceVisitorBase
18021802
null);
18031803
}
18041804

1805-
List<Statement> replacementStatements;
1806-
if (node.patternGuard.pattern.declaredVariables.isEmpty) {
1807-
replacementStatements = [
1808-
if (otherwise != null) patternMatchedSet,
1809-
if (then is! Block || then.statements.isNotEmpty) then
1810-
];
1811-
} else {
1812-
replacementStatements = [
1813-
if (otherwise != null) patternMatchedSet,
1814-
1815-
// The block is created to avoid having variables with the same name in
1816-
// the same scope.
1817-
engine.forest.createBlock(node.fileOffset, node.fileOffset, [
1818-
...node.patternGuard.pattern.declaredVariables,
1819-
if (then is! Block || then.statements.isNotEmpty) then
1820-
])
1821-
];
1822-
}
1805+
List<Statement> replacementStatements = [
1806+
if (otherwise != null) patternMatchedSet,
1807+
if (then is! Block || then.statements.isNotEmpty) then
1808+
];
18231809

18241810
PatternTransformationResult transformationResult = node.patternGuard.pattern
18251811
.transform(this,
@@ -1828,6 +1814,20 @@ class InferenceVisitorImpl extends InferenceVisitorBase
18281814
matchedType: scrutineeType,
18291815
variableInitializingContext: engine.forest
18301816
.createVariableGet(node.fileOffset, matchedExpressionVariable));
1817+
transformationResult = transformationResult.combine(
1818+
new PatternTransformationResult([
1819+
new PatternTransformationElement(
1820+
kind: PatternTransformationElementKind.regular,
1821+
condition: null,
1822+
variableInitializers:
1823+
node.patternGuard.pattern.declaredVariables),
1824+
new PatternTransformationElement(
1825+
kind: PatternTransformationElementKind.regular,
1826+
condition: node.patternGuard.guard,
1827+
variableInitializers: [])
1828+
]),
1829+
this);
1830+
18311831
replacementStatements = _transformationResultToStatements(
18321832
node.fileOffset, transformationResult, replacementStatements);
18331833

pkg/front_end/lib/src/fasta/util/parser_ast_helper.dart

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2588,6 +2588,13 @@ abstract class AbstractParserAstListener implements Listener {
25882588
seen(data);
25892589
}
25902590

2591+
@override
2592+
void beginPatternGuard(Token when) {
2593+
PatternGuardBegin data =
2594+
new PatternGuardBegin(ParserAstType.BEGIN, when: when);
2595+
seen(data);
2596+
}
2597+
25912598
@override
25922599
void beginParenthesizedExpressionOrRecordLiteral(Token token) {
25932600
ParenthesizedExpressionOrRecordLiteralBegin data =
@@ -2610,6 +2617,12 @@ abstract class AbstractParserAstListener implements Listener {
26102617
seen(data);
26112618
}
26122619

2620+
@override
2621+
void endPatternGuard(Token token) {
2622+
PatternGuardEnd data = new PatternGuardEnd(ParserAstType.END, token: token);
2623+
seen(data);
2624+
}
2625+
26132626
@override
26142627
void endParenthesizedExpression(Token token) {
26152628
ParenthesizedExpressionEnd data =
@@ -7546,6 +7559,18 @@ class ParenthesizedConditionHandle extends ParserAstNode {
75467559
};
75477560
}
75487561

7562+
class PatternGuardBegin extends ParserAstNode {
7563+
final Token when;
7564+
7565+
PatternGuardBegin(ParserAstType type, {required this.when})
7566+
: super("PatternGuard", type);
7567+
7568+
@override
7569+
Map<String, Object?> get deprecatedArguments => {
7570+
"when": when,
7571+
};
7572+
}
7573+
75497574
class ParenthesizedExpressionOrRecordLiteralBegin extends ParserAstNode {
75507575
final Token token;
75517576

@@ -7591,6 +7616,18 @@ class RecordPatternHandle extends ParserAstNode {
75917616
};
75927617
}
75937618

7619+
class PatternGuardEnd extends ParserAstNode {
7620+
final Token token;
7621+
7622+
PatternGuardEnd(ParserAstType type, {required this.token})
7623+
: super("PatternGuard", type);
7624+
7625+
@override
7626+
Map<String, Object?> get deprecatedArguments => {
7627+
"token": token,
7628+
};
7629+
}
7630+
75947631
class ParenthesizedExpressionEnd extends ParserAstNode {
75957632
final Token token;
75967633

pkg/front_end/parser_testcases/patterns/caseHead_withClassicPattern_guarded_insideIfStatement.dart.expect

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,9 @@ beginCompilationUnit(void)
2424
handleSend(x, case)
2525
handleLiteralInt(0)
2626
handleConstantPattern(null)
27-
handleLiteralBool(true)
27+
beginPatternGuard(when)
28+
handleLiteralBool(true)
29+
endPatternGuard(when)
2830
handleParenthesizedCondition((, case, when)
2931
beginThenStatement({)
3032
beginBlock({, BlockKind(statement))

pkg/front_end/parser_testcases/patterns/caseHead_withClassicPattern_guarded_insideIfStatement.dart.intertwined.expect

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ parseUnit(void)
6666
parseLiteralInt(case)
6767
listener: handleLiteralInt(0)
6868
listener: handleConstantPattern(null)
69+
listener: beginPatternGuard(when)
6970
parseExpression(when)
7071
looksLikeOuterPatternEquals(when)
7172
skipOuterPattern(when)
@@ -74,6 +75,7 @@ parseUnit(void)
7475
parsePrimary(when, expression)
7576
parseLiteralBool(when)
7677
listener: handleLiteralBool(true)
78+
listener: endPatternGuard(when)
7779
ensureCloseParen(true, ()
7880
listener: handleParenthesizedCondition((, case, when)
7981
listener: beginThenStatement({)

pkg/front_end/parser_testcases/patterns/caseHead_withNewPattern_guarded_insideIfStatement.dart.expect

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,9 @@ beginCompilationUnit(void)
3030
handleType(int, null)
3131
endAsOperatorType(as)
3232
handleCastPattern(as)
33-
handleLiteralBool(true)
33+
beginPatternGuard(when)
34+
handleLiteralBool(true)
35+
endPatternGuard(when)
3436
handleParenthesizedCondition((, case, when)
3537
beginThenStatement({)
3638
beginBlock({, BlockKind(statement))

pkg/front_end/parser_testcases/patterns/caseHead_withNewPattern_guarded_insideIfStatement.dart.intertwined.expect

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ parseUnit(void)
7373
listener: handleType(int, null)
7474
listener: endAsOperatorType(as)
7575
listener: handleCastPattern(as)
76+
listener: beginPatternGuard(when)
7677
parseExpression(when)
7778
looksLikeOuterPatternEquals(when)
7879
skipOuterPattern(when)
@@ -81,6 +82,7 @@ parseUnit(void)
8182
parsePrimary(when, expression)
8283
parseLiteralBool(when)
8384
listener: handleLiteralBool(true)
85+
listener: endPatternGuard(when)
8486
ensureCloseParen(true, ()
8587
listener: handleParenthesizedCondition((, case, when)
8688
listener: beginThenStatement({)

0 commit comments

Comments
 (0)