Skip to content

Commit

Permalink
Patterns parsing: add support for guards.
Browse files Browse the repository at this point in the history
Bug: #50035
Change-Id: I21db09c9d8c1d359e1b125eea2bae2749cdb72fd
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/264101
Reviewed-by: Jens Johansen <[email protected]>
Reviewed-by: Konstantin Shcheglov <[email protected]>
Commit-Queue: Paul Berry <[email protected]>
  • Loading branch information
stereotype441 authored and Commit Queue committed Oct 14, 2022
1 parent 4cf63fb commit 9aab3c7
Show file tree
Hide file tree
Showing 48 changed files with 1,649 additions and 9 deletions.
18 changes: 15 additions & 3 deletions pkg/_fe_analyzer_shared/lib/src/parser/parser_impl.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6240,8 +6240,14 @@ class Parser {
if (allowPatterns && optional('case', next)) {
Token case_ = token = next;
token = parsePattern(token);
next = token.next!;
Token? when;
if (optional('when', next)) {
when = token = next;
token = parseExpression(token);
}
token = ensureCloseParen(token, begin);
listener.handleParenthesizedCondition(begin, case_, null);
listener.handleParenthesizedCondition(begin, case_, when);
} else {
token = ensureCloseParen(token, begin);
listener.handleParenthesizedCondition(
Expand Down Expand Up @@ -8258,9 +8264,15 @@ class Parser {
} else {
token = parseExpression(caseKeyword);
}
Token? next = token.next!;
Token? when;
if (optional('when', next)) {
when = token = next;
token = parseExpression(token);
}
token = ensureColon(token);
listener.endCaseExpression(null, token);
listener.handleCaseMatch(caseKeyword, null, token);
listener.endCaseExpression(when, token);
listener.handleCaseMatch(caseKeyword, when, token);
expressionCount++;
peek = peekPastLabels(token.next!);
} else if (expressionCount > 0) {
Expand Down
11 changes: 8 additions & 3 deletions pkg/_fe_analyzer_shared/lib/src/scanner/token.dart
Original file line number Diff line number Diff line change
Expand Up @@ -327,14 +327,17 @@ class Keyword extends TokenType {
static const Keyword VOID =
const Keyword(/* index = */ 145, "void", "VOID", KeywordStyle.reserved);

static const Keyword WHEN =
const Keyword(/* index = */ 146, "when", 'WHEN', KeywordStyle.pseudo);

static const Keyword WHILE =
const Keyword(/* index = */ 146, "while", "WHILE", KeywordStyle.reserved);
const Keyword(/* index = */ 147, "while", "WHILE", KeywordStyle.reserved);

static const Keyword WITH =
const Keyword(/* index = */ 147, "with", "WITH", KeywordStyle.reserved);
const Keyword(/* index = */ 148, "with", "WITH", KeywordStyle.reserved);

static const Keyword YIELD =
const Keyword(/* index = */ 148, "yield", "YIELD", KeywordStyle.pseudo);
const Keyword(/* index = */ 149, "yield", "YIELD", KeywordStyle.pseudo);

static const List<Keyword> values = const <Keyword>[
ABSTRACT,
Expand Down Expand Up @@ -404,6 +407,7 @@ class Keyword extends TokenType {
TYPEDEF,
VAR,
VOID,
WHEN,
WHILE,
WITH,
YIELD,
Expand Down Expand Up @@ -1978,6 +1982,7 @@ const List<TokenType> _tokenTypesByIndex = [
Keyword.TYPEDEF,
Keyword.VAR,
Keyword.VOID,
Keyword.WHEN,
Keyword.WHILE,
Keyword.WITH,
Keyword.YIELD,
Expand Down
1 change: 1 addition & 0 deletions pkg/analyzer/lib/src/dart/ast/ast.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1483,6 +1483,7 @@ class CaseClauseImpl extends AstNodeImpl implements CaseClause {
required this.whenClause,
}) {
_becomeParentOf(pattern);
_becomeParentOf(whenClause);
}

@override
Expand Down
15 changes: 12 additions & 3 deletions pkg/analyzer/lib/src/fasta/ast_builder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3282,12 +3282,17 @@ class AstBuilder extends StackListener {
debugEvent("CaseMatch");

if (_featureSet.isEnabled(Feature.patterns)) {
WhenClauseImpl? whenClause;
if (when != null) {
var expression = pop() as ExpressionImpl;
whenClause = WhenClauseImpl(whenKeyword: when, expression: expression);
}
var pattern = pop() as DartPatternImpl;
push(SwitchPatternCaseImpl(
labels: <Label>[],
keyword: caseKeyword,
pattern: pattern,
whenClause: null,
whenClause: whenClause,
colon: colon,
statements: <Statement>[]));
} else {
Expand Down Expand Up @@ -4561,10 +4566,14 @@ class AstBuilder extends StackListener {
ExpressionImpl condition;
CaseClauseImpl? caseClause;
if (case_ != null) {
// TODO(paulberry): what about a guard?
WhenClauseImpl? whenClause;
if (when != null) {
var expression = pop() as ExpressionImpl;
whenClause = WhenClauseImpl(whenKeyword: when, expression: expression);
}
var pattern = pop() as DartPatternImpl;
caseClause = CaseClauseImpl(
caseKeyword: case_, pattern: pattern, whenClause: null);
caseKeyword: case_, pattern: pattern, whenClause: whenClause);
}
condition = pop() as ExpressionImpl;
push(_ParenthesizedCondition(leftParenthesis, condition, caseClause));
Expand Down
232 changes: 232 additions & 0 deletions pkg/analyzer/test/generated/patterns_parser_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,238 @@ main() {
class PatternsTest extends ParserDiagnosticsTest {
late FindNode findNode;

test_caseHead_withClassicPattern_guarded_insideIfStatement() {
_parse('''
void f(x) {
if (x case 0 when true) {}
}
''');
var node = findNode.ifStatement('case');
assertParsedNodeText(node, r'''
IfStatement
ifKeyword: if
leftParenthesis: (
condition: SimpleIdentifier
token: x
caseClause: CaseClause
caseKeyword: case
pattern: ConstantPattern
expression: IntegerLiteral
literal: 0
whenClause: WhenClause
whenKeyword: when
expression: BooleanLiteral
literal: true
rightParenthesis: )
thenStatement: Block
leftBracket: {
rightBracket: }
''');
}

test_caseHead_withClassicPattern_guarded_insideSwitchStatement() {
_parse('''
void f(x) {
switch (x) {
case 0 when true:
break;
}
}
''');
var node = findNode.switchPatternCase('case');
assertParsedNodeText(node, r'''
SwitchPatternCase
keyword: case
pattern: ConstantPattern
expression: IntegerLiteral
literal: 0
whenClause: WhenClause
whenKeyword: when
expression: BooleanLiteral
literal: true
colon: :
statements
BreakStatement
breakKeyword: break
semicolon: ;
''');
}

test_caseHead_withClassicPattern_unguarded_insideIfStatement() {
_parse('''
void f(x) {
if (x case 0) {}
}
''');
var node = findNode.ifStatement('case');
assertParsedNodeText(node, r'''
IfStatement
ifKeyword: if
leftParenthesis: (
condition: SimpleIdentifier
token: x
caseClause: CaseClause
caseKeyword: case
pattern: ConstantPattern
expression: IntegerLiteral
literal: 0
rightParenthesis: )
thenStatement: Block
leftBracket: {
rightBracket: }
''');
}

test_caseHead_withClassicPattern_unguarded_insideSwitchStatement() {
_parse('''
void f(x) {
switch (x) {
case 0:
break;
}
}
''');
var node = findNode.switchPatternCase('case');
assertParsedNodeText(node, r'''
SwitchPatternCase
keyword: case
pattern: ConstantPattern
expression: IntegerLiteral
literal: 0
colon: :
statements
BreakStatement
breakKeyword: break
semicolon: ;
''');
}

test_caseHead_withNewPattern_guarded_insideIfStatement() {
_parse('''
void f(x) {
if (x case 0 as int when true) {}
}
''');
var node = findNode.ifStatement('case');
assertParsedNodeText(node, r'''
IfStatement
ifKeyword: if
leftParenthesis: (
condition: SimpleIdentifier
token: x
caseClause: CaseClause
caseKeyword: case
pattern: CastPattern
pattern: ConstantPattern
expression: IntegerLiteral
literal: 0
asToken: as
type: NamedType
name: SimpleIdentifier
token: int
whenClause: WhenClause
whenKeyword: when
expression: BooleanLiteral
literal: true
rightParenthesis: )
thenStatement: Block
leftBracket: {
rightBracket: }
''');
}

test_caseHead_withNewPattern_guarded_insideSwitchStatement() {
_parse('''
void f(x) {
switch (x) {
case 0 as int when true:
break;
}
}
''');
var node = findNode.switchPatternCase('case');
assertParsedNodeText(node, r'''
SwitchPatternCase
keyword: case
pattern: CastPattern
pattern: ConstantPattern
expression: IntegerLiteral
literal: 0
asToken: as
type: NamedType
name: SimpleIdentifier
token: int
whenClause: WhenClause
whenKeyword: when
expression: BooleanLiteral
literal: true
colon: :
statements
BreakStatement
breakKeyword: break
semicolon: ;
''');
}

test_caseHead_withNewPattern_unguarded_insideIfStatement() {
_parse('''
void f(x) {
if (x case 0 as int) {}
}
''');
var node = findNode.ifStatement('case');
assertParsedNodeText(node, r'''
IfStatement
ifKeyword: if
leftParenthesis: (
condition: SimpleIdentifier
token: x
caseClause: CaseClause
caseKeyword: case
pattern: CastPattern
pattern: ConstantPattern
expression: IntegerLiteral
literal: 0
asToken: as
type: NamedType
name: SimpleIdentifier
token: int
rightParenthesis: )
thenStatement: Block
leftBracket: {
rightBracket: }
''');
}

test_caseHead_withNewPattern_unguarded_insideSwitchStatement() {
_parse('''
void f(x) {
switch (x) {
case 0 as int:
break;
}
}
''');
var node = findNode.switchPatternCase('case');
assertParsedNodeText(node, r'''
SwitchPatternCase
keyword: case
pattern: CastPattern
pattern: ConstantPattern
expression: IntegerLiteral
literal: 0
asToken: as
type: NamedType
name: SimpleIdentifier
token: int
colon: :
statements
BreakStatement
breakKeyword: break
semicolon: ;
''');
}

test_cast_insideCase() {
_parse('''
void f(x) {
Expand Down
8 changes: 8 additions & 0 deletions pkg/analyzer/test/src/summary/resolved_ast_printer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1438,6 +1438,14 @@ class ResolvedAstPrinter extends ThrowingAstVisitor<void> {
});
}

@override
void visitWhenClause(WhenClause node) {
_writeln('WhenClause');
_withIndent(() {
_writeNamedChildEntities(node);
});
}

@override
void visitWhileStatement(WhileStatement node) {
_writeln('WhileStatement');
Expand Down
Loading

0 comments on commit 9aab3c7

Please sign in to comment.