Skip to content

Commit

Permalink
Add parser support for patternAssignment.
Browse files Browse the repository at this point in the history
Bug: #50035, #50502, #50575
Change-Id: Idd3bdcae7cbb95c6f2016bb5d3efc638867f52af
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/272383
Reviewed-by: Konstantin Shcheglov <[email protected]>
Commit-Queue: Paul Berry <[email protected]>
  • Loading branch information
stereotype441 authored and Commit Queue committed Nov 29, 2022
1 parent e1acab8 commit 7b41b62
Show file tree
Hide file tree
Showing 396 changed files with 3,311 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1994,6 +1994,11 @@ class ForwardingListener implements Listener {
keyword, equals, semicolon);
}

@override
void handlePatternAssignment(Token equals) {
listener?.handlePatternAssignment(equals);
}

@override
void logEvent(String name) {
listener?.logEvent(name);
Expand Down
9 changes: 9 additions & 0 deletions pkg/_fe_analyzer_shared/lib/src/parser/listener.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2139,4 +2139,13 @@ class Listener implements UnescapeErrorListener {
Token keyword, Token equals, Token semicolon) {
logEvent('PatternVariableDeclarationStatement');
}

/// Called after the parser has processed a pattern assignment consisting of
/// `PATTERN EQUALS EXPRESSION`.
///
/// PATTERN may only be one of the patterns accepted by the `outerPattern`
/// grammar rule defined in the patterns spec.
void handlePatternAssignment(Token equals) {
logEvent("PatternAssignment");
}
}
26 changes: 21 additions & 5 deletions pkg/_fe_analyzer_shared/lib/src/parser/parser_impl.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5466,10 +5466,14 @@ class Parser {
listener.handleIdentifier(token, IdentifierContext.expression);
}
} else {
token = optional('throw', token.next!)
? parseThrowExpression(token, /* allowCascades = */ true)
: parsePrecedenceExpression(
token, ASSIGNMENT_PRECEDENCE, /* allowCascades = */ true);
if (allowPatterns && looksLikeOuterPatternEquals(token)) {
token = parsePatternAssignment(token);
} else {
token = optional('throw', token.next!)
? parseThrowExpression(token, /* allowCascades = */ true)
: parsePrecedenceExpression(
token, ASSIGNMENT_PRECEDENCE, /* allowCascades = */ true);
}
}
expressionDepth--;
return token;
Expand Down Expand Up @@ -9796,7 +9800,8 @@ class Parser {
if (next.isIdentifier) {
return skipObjectPatternRest(next);
} else {
throw new UnimplementedError('TODO(paulberry)');
// IDENTIFIER `.` NON-IDENTIFIER (not a pattern)
return null;
}
}
TypeParamOrArgInfo typeParamOrArg = computeTypeParamOrArg(token);
Expand Down Expand Up @@ -9847,6 +9852,17 @@ class Parser {
return semicolon;
}

/// patternAssignment ::= outerPattern '=' expression
Token parsePatternAssignment(Token token) {
token = parsePattern(token, isRefutableContext: false);
Token equals = token.next!;
// Caller should have assured that the pattern was followed by an `=`.
assert(optional('=', equals));
token = parseExpression(equals);
listener.handlePatternAssignment(equals);
return token;
}

/// switchExpression ::= 'switch' '(' expression ')' '{'
/// switchExpressionCase ( ',' switchExpressionCase )*
/// ','? '}'
Expand Down
8 changes: 8 additions & 0 deletions pkg/analyzer/lib/src/fasta/ast_builder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4810,6 +4810,14 @@ class AstBuilder extends StackListener {
rightParenthesis: leftParenthesis.endGroup!));
}

@override
void handlePatternAssignment(Token equals) {
var expression = pop() as ExpressionImpl;
var pattern = pop() as DartPatternImpl;
push(PatternAssignmentImpl(
pattern: pattern, equals: equals, expression: expression));
}

@override
void handlePatternField(Token? colon) {
debugEvent("PatternField");
Expand Down
254 changes: 254 additions & 0 deletions pkg/analyzer/test/generated/patterns_parser_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2913,6 +2913,79 @@ f() {
]);
}

test_list_insideAssignment_typed_nonEmpty() {
_parse('''
void f(x) {
<int>[a, b] = x;
}
''');
var node = findNode.patternAssignment('= x').pattern;
assertParsedNodeText(node, r'''
ListPattern
typeArguments: TypeArgumentList
leftBracket: <
arguments
NamedType
name: SimpleIdentifier
token: int
rightBracket: >
leftBracket: [
elements
VariablePattern
name: a
VariablePattern
name: b
rightBracket: ]
''');
}

test_list_insideAssignment_untyped_empty() {
_parse('''
void f(x) {
[] = x;
}
''');
var node = findNode.patternAssignment('= x').pattern;
assertParsedNodeText(node, r'''
ListPattern
leftBracket: [
rightBracket: ]
''');
}

test_list_insideAssignment_untyped_emptyWithWhitespace() {
_parse('''
void f(x) {
[ ] = x;
}
''');
var node = findNode.patternAssignment('= x').pattern;
assertParsedNodeText(node, r'''
ListPattern
leftBracket: [
rightBracket: ]
''');
}

test_list_insideAssignment_untyped_nonEmpty() {
_parse('''
void f(x) {
[a, b] = x;
}
''');
var node = findNode.patternAssignment('= x').pattern;
assertParsedNodeText(node, r'''
ListPattern
leftBracket: [
elements
VariablePattern
name: a
VariablePattern
name: b
rightBracket: ]
''');
}

test_list_insideCase_typed_nonEmpty() {
_parse('''
void f(x) {
Expand Down Expand Up @@ -3817,6 +3890,90 @@ BinaryPattern
''');
}

test_map_insideAssignment_typed_nonEmpty() {
_parse('''
void f(x) {
<String, int>{'a': a, 'b': b} = x;
}
''');
var node = findNode.patternAssignment('= x').pattern;
assertParsedNodeText(node, r'''
MapPattern
typeArguments: TypeArgumentList
leftBracket: <
arguments
NamedType
name: SimpleIdentifier
token: String
NamedType
name: SimpleIdentifier
token: int
rightBracket: >
leftBracket: {
elements
MapPatternEntry
key: SimpleStringLiteral
literal: 'a'
separator: :
value: VariablePattern
name: a
MapPatternEntry
key: SimpleStringLiteral
literal: 'b'
separator: :
value: VariablePattern
name: b
rightBracket: }
''');
}

test_map_insideAssignment_untyped_empty() {
// Note: statements aren't allowed to start with `{` so we need parens
// around the assignment. See
// https://github.com/dart-lang/language/issues/2662.
_parse('''
void f(x) {
({} = x);
}
''');
var node = findNode.patternAssignment('= x').pattern;
assertParsedNodeText(node, r'''
MapPattern
leftBracket: {
rightBracket: }
''');
}

test_map_insideAssignment_untyped_nonEmpty() {
// Note: statements aren't allowed to start with `{` so we need parens
// around the assignment. See
// https://github.com/dart-lang/language/issues/2662.
_parse('''
void f(x) {
({'a': a, 'b': b} = x);
}
''');
var node = findNode.patternAssignment('= x').pattern;
assertParsedNodeText(node, r'''
MapPattern
leftBracket: {
elements
MapPatternEntry
key: SimpleStringLiteral
literal: 'a'
separator: :
value: VariablePattern
name: a
MapPatternEntry
key: SimpleStringLiteral
literal: 'b'
separator: :
value: VariablePattern
name: b
rightBracket: }
''');
}

test_map_insideCase_typed_nonEmpty() {
_parse('''
void f(x) {
Expand Down Expand Up @@ -4792,6 +4949,34 @@ RecordPattern
''');
}

test_object_prefixed_withTypeArgs_insideAssignment() {
_parse('''
void f(x) {
async.Future<int>() = x;
}
''');
var node = findNode.patternAssignment('= x').pattern;
assertParsedNodeText(node, r'''
ObjectPattern
type: NamedType
name: PrefixedIdentifier
prefix: SimpleIdentifier
token: async
period: .
identifier: SimpleIdentifier
token: Future
typeArguments: TypeArgumentList
leftBracket: <
arguments
NamedType
name: SimpleIdentifier
token: int
rightBracket: >
leftParenthesis: (
rightParenthesis: )
''');
}

test_object_prefixed_withTypeArgs_insideCase() {
_parse('''
import 'dart:async' as async;
Expand Down Expand Up @@ -5335,6 +5520,22 @@ ObjectPattern
''');
}

test_parenthesized_insideAssignment() {
_parse('''
f(x) {
(a) = x;
}
''');
var node = findNode.patternAssignment('= x').pattern;
assertParsedNodeText(node, r'''
ParenthesizedPattern
leftParenthesis: (
pattern: VariablePattern
name: a
rightParenthesis: )
''');
}

test_parenthesized_insideCase() {
_parse('''
f(x) {
Expand Down Expand Up @@ -6040,6 +6241,59 @@ PatternVariableDeclarationStatement
''');
}

test_record_insideAssignment_empty() {
_parse('''
void f(x) {
() = x;
}
''');
var node = findNode.patternAssignment('= x').pattern;
assertParsedNodeText(node, r'''
RecordPattern
leftParenthesis: (
rightParenthesis: )
''');
}

test_record_insideAssignment_oneField() {
_parse('''
void f(x) {
(a,) = x;
}
''');
var node = findNode.patternAssignment('= x').pattern;
assertParsedNodeText(node, r'''
RecordPattern
leftParenthesis: (
fields
RecordPatternField
pattern: VariablePattern
name: a
rightParenthesis: )
''');
}

test_record_insideAssignment_twoFields() {
_parse('''
void f(x) {
(a, b) = x;
}
''');
var node = findNode.patternAssignment('= x').pattern;
assertParsedNodeText(node, r'''
RecordPattern
leftParenthesis: (
fields
RecordPatternField
pattern: VariablePattern
name: a
RecordPatternField
pattern: VariablePattern
name: b
rightParenthesis: )
''');
}

test_record_insideCase_empty() {
_parse('''
void f(x) {
Expand Down
7 changes: 5 additions & 2 deletions pkg/analyzer/test/src/dart/resolution/assignment_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -599,13 +599,16 @@ AssignmentExpression
}

test_notLValue_parenthesized_simple() async {
// TODO(paulberry): remove `// @dart = 2.18` - see
// https://github.com/dart-lang/sdk/issues/50502
await assertErrorsInCode(r'''
// @dart = 2.18
void f(int a, int b, double c) {
(a + b) = c;
}
''', [
error(ParserErrorCode.ILLEGAL_ASSIGNMENT_TO_NON_ASSIGNABLE, 35, 7),
error(ParserErrorCode.MISSING_ASSIGNABLE_SELECTOR, 35, 7),
error(ParserErrorCode.ILLEGAL_ASSIGNMENT_TO_NON_ASSIGNABLE, 51, 7),
error(ParserErrorCode.MISSING_ASSIGNABLE_SELECTOR, 51, 7),
]);

var assignment = findNode.assignment('= c');
Expand Down
Loading

0 comments on commit 7b41b62

Please sign in to comment.