Skip to content

Commit

Permalink
Add support for expression statements beginning with a map pattern.
Browse files Browse the repository at this point in the history
Bug: #50035, dart-lang/language#2662
Change-Id: Ifb141cf563848fc0035423a625e27585cce2ea57
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/272523
Commit-Queue: Paul Berry <[email protected]>
Reviewed-by: Jens Johansen <[email protected]>
  • Loading branch information
stereotype441 authored and Commit Queue committed Nov 30, 2022
1 parent 3e00e6a commit 701a0d2
Show file tree
Hide file tree
Showing 12 changed files with 343 additions and 1 deletion.
7 changes: 6 additions & 1 deletion pkg/_fe_analyzer_shared/lib/src/parser/parser_impl.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5240,7 +5240,12 @@ class Parser {
final String? value = token.next!.stringValue;
if (identical(value, '{')) {
// The scanner ensures that `{` always has a closing `}`.
return parseBlock(token, BlockKind.statement);
if (allowPatterns && optional('=', token.next!.endGroup!.next!)) {
// Expression statement beginning with a pattern assignment
return parseExpressionStatement(token);
} else {
return parseBlock(token, BlockKind.statement);
}
} else if (identical(value, 'return')) {
return parseReturnStatement(token);
} else if (identical(value, 'var') || identical(value, 'final')) {
Expand Down
41 changes: 41 additions & 0 deletions pkg/analyzer/test/generated/patterns_parser_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3944,6 +3944,20 @@ MapPattern
''');
}

test_map_insideAssignment_untyped_empty_beginningOfStatement() {
_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
Expand Down Expand Up @@ -3974,6 +3988,33 @@ MapPattern
''');
}

test_map_insideAssignment_untyped_nonEmpty_beginningOfStatement() {
_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
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
void f(x) {
{} = x;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
beginCompilationUnit(void)
beginMetadataStar(void)
endMetadataStar(0)
beginTopLevelMember(void)
beginTopLevelMethod(, null, null)
handleVoidKeyword(void)
handleIdentifier(f, topLevelFunctionDeclaration)
handleNoTypeVariables(()
beginFormalParameters((, MemberKind.TopLevelMethod)
beginMetadataStar(x)
endMetadataStar(0)
beginFormalParameter(x, MemberKind.TopLevelMethod, null, null, null)
handleNoType(()
handleIdentifier(x, formalParameterDeclaration)
handleFormalParameterWithoutValue())
endFormalParameter(null, null, null, x, null, null, FormalParameterKind.requiredPositional, MemberKind.TopLevelMethod)
endFormalParameters(1, (, ), MemberKind.TopLevelMethod)
handleAsyncModifier(null, null)
beginBlockFunctionBody({)
handleNoTypeArguments({)
handleMapPattern(0, {, })
handleIdentifier(x, expression)
handleNoTypeArguments(;)
handleNoArguments(;)
handleSend(x, ;)
handlePatternAssignment(=)
handleExpressionStatement(;)
endBlockFunctionBody(1, {, })
endTopLevelMethod(void, null, })
endTopLevelDeclaration()
endCompilationUnit(1, )
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
parseUnit(void)
skipErrorTokens(void)
listener: beginCompilationUnit(void)
syntheticPreviousToken(void)
parseTopLevelDeclarationImpl(, Instance of 'DirectiveContext')
parseMetadataStar()
listener: beginMetadataStar(void)
listener: endMetadataStar(0)
parseTopLevelMemberImpl()
listener: beginTopLevelMember(void)
parseTopLevelMethod(, null, null, , Instance of 'VoidType', null, f, false)
listener: beginTopLevelMethod(, null, null)
listener: handleVoidKeyword(void)
ensureIdentifierPotentiallyRecovered(void, topLevelFunctionDeclaration, false)
listener: handleIdentifier(f, topLevelFunctionDeclaration)
parseMethodTypeVar(f)
listener: handleNoTypeVariables(()
parseGetterOrFormalParameters(f, f, false, MemberKind.TopLevelMethod)
parseFormalParameters(f, MemberKind.TopLevelMethod)
parseFormalParametersRest((, MemberKind.TopLevelMethod)
listener: beginFormalParameters((, MemberKind.TopLevelMethod)
parseFormalParameter((, FormalParameterKind.requiredPositional, MemberKind.TopLevelMethod)
parseMetadataStar(()
listener: beginMetadataStar(x)
listener: endMetadataStar(0)
listener: beginFormalParameter(x, MemberKind.TopLevelMethod, null, null, null)
listener: handleNoType(()
ensureIdentifier((, formalParameterDeclaration)
listener: handleIdentifier(x, formalParameterDeclaration)
listener: handleFormalParameterWithoutValue())
listener: endFormalParameter(null, null, null, x, null, null, FormalParameterKind.requiredPositional, MemberKind.TopLevelMethod)
listener: endFormalParameters(1, (, ), MemberKind.TopLevelMethod)
parseAsyncModifierOpt())
listener: handleAsyncModifier(null, null)
inPlainSync()
parseFunctionBody(), false, false)
listener: beginBlockFunctionBody({)
notEofOrValue(}, {)
parseStatement({)
parseStatementX({)
parseExpressionStatement({)
parseExpression({)
looksLikeOuterPatternEquals({)
skipOuterPattern({)
parsePatternAssignment({)
parsePattern({, precedence: 1, isRefutableContext: false)
parsePrimaryPattern({, isRefutableContext: false)
listener: handleNoTypeArguments({)
parseMapPatternSuffix({, isRefutableContext: false)
listener: handleMapPattern(0, {, })
parseExpression(=)
looksLikeOuterPatternEquals(=)
skipOuterPattern(=)
skipObjectPatternRest(x)
parsePrecedenceExpression(=, 1, true)
parseUnaryExpression(=, true)
parsePrimary(=, expression)
parseSendOrFunctionLiteral(=, expression)
parseSend(=, expression)
isNextIdentifier(=)
ensureIdentifier(=, expression)
listener: handleIdentifier(x, expression)
listener: handleNoTypeArguments(;)
parseArgumentsOpt(x)
listener: handleNoArguments(;)
listener: handleSend(x, ;)
listener: handlePatternAssignment(=)
ensureSemicolon(x)
listener: handleExpressionStatement(;)
notEofOrValue(}, })
listener: endBlockFunctionBody(1, {, })
listener: endTopLevelMethod(void, null, })
listener: endTopLevelDeclaration()
reportAllErrorTokens(void)
listener: endCompilationUnit(1, )
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
void f(x) {
{} = x;
}


void[KeywordToken] f[StringToken]([BeginToken]x[StringToken])[SimpleToken] {[BeginToken]
{[BeginToken]}[SimpleToken] =[SimpleToken] x[StringToken];[SimpleToken]
}[SimpleToken]
[SimpleToken]
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
void f(x) {
{} = x;
}


void[KeywordToken] f[StringToken]([BeginToken]x[StringToken])[SimpleToken] {[BeginToken]
{[BeginToken]}[SimpleToken] =[SimpleToken] x[StringToken];[SimpleToken]
}[SimpleToken]
[SimpleToken]
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
void f(x) {
{'a': a, 'b': b} = x;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
beginCompilationUnit(void)
beginMetadataStar(void)
endMetadataStar(0)
beginTopLevelMember(void)
beginTopLevelMethod(, null, null)
handleVoidKeyword(void)
handleIdentifier(f, topLevelFunctionDeclaration)
handleNoTypeVariables(()
beginFormalParameters((, MemberKind.TopLevelMethod)
beginMetadataStar(x)
endMetadataStar(0)
beginFormalParameter(x, MemberKind.TopLevelMethod, null, null, null)
handleNoType(()
handleIdentifier(x, formalParameterDeclaration)
handleFormalParameterWithoutValue())
endFormalParameter(null, null, null, x, null, null, FormalParameterKind.requiredPositional, MemberKind.TopLevelMethod)
endFormalParameters(1, (, ), MemberKind.TopLevelMethod)
handleAsyncModifier(null, null)
beginBlockFunctionBody({)
handleNoTypeArguments({)
beginLiteralString('a')
endLiteralString(0, :)
handleNoType(:)
handleVariablePattern(null, a)
handleMapPatternEntry(:, ,)
beginLiteralString('b')
endLiteralString(0, :)
handleNoType(:)
handleVariablePattern(null, b)
handleMapPatternEntry(:, })
handleMapPattern(2, {, })
handleIdentifier(x, expression)
handleNoTypeArguments(;)
handleNoArguments(;)
handleSend(x, ;)
handlePatternAssignment(=)
handleExpressionStatement(;)
endBlockFunctionBody(1, {, })
endTopLevelMethod(void, null, })
endTopLevelDeclaration()
endCompilationUnit(1, )
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
parseUnit(void)
skipErrorTokens(void)
listener: beginCompilationUnit(void)
syntheticPreviousToken(void)
parseTopLevelDeclarationImpl(, Instance of 'DirectiveContext')
parseMetadataStar()
listener: beginMetadataStar(void)
listener: endMetadataStar(0)
parseTopLevelMemberImpl()
listener: beginTopLevelMember(void)
parseTopLevelMethod(, null, null, , Instance of 'VoidType', null, f, false)
listener: beginTopLevelMethod(, null, null)
listener: handleVoidKeyword(void)
ensureIdentifierPotentiallyRecovered(void, topLevelFunctionDeclaration, false)
listener: handleIdentifier(f, topLevelFunctionDeclaration)
parseMethodTypeVar(f)
listener: handleNoTypeVariables(()
parseGetterOrFormalParameters(f, f, false, MemberKind.TopLevelMethod)
parseFormalParameters(f, MemberKind.TopLevelMethod)
parseFormalParametersRest((, MemberKind.TopLevelMethod)
listener: beginFormalParameters((, MemberKind.TopLevelMethod)
parseFormalParameter((, FormalParameterKind.requiredPositional, MemberKind.TopLevelMethod)
parseMetadataStar(()
listener: beginMetadataStar(x)
listener: endMetadataStar(0)
listener: beginFormalParameter(x, MemberKind.TopLevelMethod, null, null, null)
listener: handleNoType(()
ensureIdentifier((, formalParameterDeclaration)
listener: handleIdentifier(x, formalParameterDeclaration)
listener: handleFormalParameterWithoutValue())
listener: endFormalParameter(null, null, null, x, null, null, FormalParameterKind.requiredPositional, MemberKind.TopLevelMethod)
listener: endFormalParameters(1, (, ), MemberKind.TopLevelMethod)
parseAsyncModifierOpt())
listener: handleAsyncModifier(null, null)
inPlainSync()
parseFunctionBody(), false, false)
listener: beginBlockFunctionBody({)
notEofOrValue(}, {)
parseStatement({)
parseStatementX({)
parseExpressionStatement({)
parseExpression({)
looksLikeOuterPatternEquals({)
skipOuterPattern({)
parsePatternAssignment({)
parsePattern({, precedence: 1, isRefutableContext: false)
parsePrimaryPattern({, isRefutableContext: false)
listener: handleNoTypeArguments({)
parseMapPatternSuffix({, isRefutableContext: false)
parseExpression({)
looksLikeOuterPatternEquals({)
skipOuterPattern({)
parsePrecedenceExpression({, 1, true)
parseUnaryExpression({, true)
parsePrimary({, expression)
parseLiteralString({)
parseSingleLiteralString({)
listener: beginLiteralString('a')
listener: endLiteralString(0, :)
parsePattern(:, precedence: 1, isRefutableContext: false)
parsePrimaryPattern(:, isRefutableContext: false)
parseVariablePattern(:, typeInfo: Instance of 'NoType')
listener: handleNoType(:)
listener: handleVariablePattern(null, a)
listener: handleMapPatternEntry(:, ,)
parseExpression(,)
looksLikeOuterPatternEquals(,)
skipOuterPattern(,)
parsePrecedenceExpression(,, 1, true)
parseUnaryExpression(,, true)
parsePrimary(,, expression)
parseLiteralString(,)
parseSingleLiteralString(,)
listener: beginLiteralString('b')
listener: endLiteralString(0, :)
parsePattern(:, precedence: 1, isRefutableContext: false)
parsePrimaryPattern(:, isRefutableContext: false)
parseVariablePattern(:, typeInfo: Instance of 'NoType')
listener: handleNoType(:)
listener: handleVariablePattern(null, b)
listener: handleMapPatternEntry(:, })
listener: handleMapPattern(2, {, })
parseExpression(=)
looksLikeOuterPatternEquals(=)
skipOuterPattern(=)
skipObjectPatternRest(x)
parsePrecedenceExpression(=, 1, true)
parseUnaryExpression(=, true)
parsePrimary(=, expression)
parseSendOrFunctionLiteral(=, expression)
parseSend(=, expression)
isNextIdentifier(=)
ensureIdentifier(=, expression)
listener: handleIdentifier(x, expression)
listener: handleNoTypeArguments(;)
parseArgumentsOpt(x)
listener: handleNoArguments(;)
listener: handleSend(x, ;)
listener: handlePatternAssignment(=)
ensureSemicolon(x)
listener: handleExpressionStatement(;)
notEofOrValue(}, })
listener: endBlockFunctionBody(1, {, })
listener: endTopLevelMethod(void, null, })
listener: endTopLevelDeclaration()
reportAllErrorTokens(void)
listener: endCompilationUnit(1, )
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
void f(x) {
{'a': a, 'b': b} = x;
}


void[KeywordToken] f[StringToken]([BeginToken]x[StringToken])[SimpleToken] {[BeginToken]
{[BeginToken]'a'[StringToken]:[SimpleToken] a[StringToken],[SimpleToken] 'b'[StringToken]:[SimpleToken] b[StringToken]}[SimpleToken] =[SimpleToken] x[StringToken];[SimpleToken]
}[SimpleToken]
[SimpleToken]
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
void f(x) {
{'a': a, 'b': b} = x;
}


void[KeywordToken] f[StringToken]([BeginToken]x[StringToken])[SimpleToken] {[BeginToken]
{[BeginToken]'a'[StringToken]:[SimpleToken] a[StringToken],[SimpleToken] 'b'[StringToken]:[SimpleToken] b[StringToken]}[SimpleToken] =[SimpleToken] x[StringToken];[SimpleToken]
}[SimpleToken]
[SimpleToken]

0 comments on commit 701a0d2

Please sign in to comment.