Skip to content

Commit

Permalink
Add support for ... inside map patterns
Browse files Browse the repository at this point in the history
Bug: #50035
Change-Id: I350eb526e67e4cc42c94b4776e5d38315932ddc5
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/272382
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
  • Loading branch information
stereotype441 authored and Commit Queue committed Nov 28, 2022
1 parent eaff33f commit a3befd3
Show file tree
Hide file tree
Showing 8 changed files with 211 additions and 11 deletions.
32 changes: 21 additions & 11 deletions pkg/_fe_analyzer_shared/lib/src/parser/parser_impl.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9571,18 +9571,28 @@ class Parser {

int count = 0;
while (true) {
token = parseExpression(token);
Token colon = token.next!;
if (!optional(':', colon)) {
// TODO(paulberry): test this error recovery logic
// Recover from a missing colon by inserting one.
colon = rewriteAndRecover(
token,
codes.templateExpectedButGot.withArguments(':'),
new SyntheticToken(TokenType.PERIOD, next.charOffset));
if (optional('...', next)) {
Token dots = next;
token = next;
// TODO(paulberry): users might assume that `...pattern` is valid inside
// a map (because it's valid inside a list). Consider accepting this
// and reporting the error at a later stage of the compilation pipeline
// so that the error is more useful.
listener.handleRestPattern(dots, hasSubPattern: false);
} else {
token = parseExpression(token);
Token colon = token.next!;
if (!optional(':', colon)) {
// TODO(paulberry): test this error recovery logic
// Recover from a missing colon by inserting one.
colon = rewriteAndRecover(
token,
codes.templateExpectedButGot.withArguments(':'),
new SyntheticToken(TokenType.PERIOD, next.charOffset));
}
token = parsePattern(colon, isRefutableContext: isRefutableContext);
listener.handleMapPatternEntry(colon, token.next!);
}
token = parsePattern(colon, isRefutableContext: isRefutableContext);
listener.handleMapPatternEntry(colon, token.next!);
++count;
next = token.next!;

Expand Down
20 changes: 20 additions & 0 deletions pkg/analyzer/test/generated/patterns_parser_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6711,6 +6711,26 @@ ListPattern
''');
}

test_rest_withoutSubpattern_insideMap() {
_parse('''
void f(x) {
switch (x) {
case {...}:
break;
}
}
''');
var node = findNode.singleGuardedPattern.pattern;
assertParsedNodeText(node, r'''
MapPattern
leftBracket: {
elements
RestPatternElement
operator: ...
rightBracket: }
''');
}

test_rest_withSubpattern_insideList() {
_parse('''
void f(x) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
void f(x) {
switch (x) {
case {...}:
break;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
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({)
beginSwitchStatement(switch)
handleIdentifier(x, expression)
handleNoTypeArguments())
handleNoArguments())
handleSend(x, ))
handleParenthesizedCondition((, null, null)
beginSwitchBlock({)
beginCaseExpression(case)
handleNoTypeArguments({)
handleRestPattern(..., false)
handleMapPattern(1, {, })
endCaseExpression(case, null, :)
beginSwitchCase(0, 1, case)
handleBreakStatement(false, break, ;)
endSwitchCase(0, 1, null, null, 1, case, })
endSwitchBlock(1, {, })
endSwitchStatement(switch, })
endBlockFunctionBody(1, {, })
endTopLevelMethod(void, null, })
endTopLevelDeclaration()
endCompilationUnit(1, )
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
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(}, switch)
parseStatement({)
parseStatementX({)
parseSwitchStatement({)
listener: beginSwitchStatement(switch)
ensureParenthesizedCondition(switch, allowCase: false)
parseExpressionInParenthesisRest((, allowCase: false)
parseExpression(()
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, ))
ensureCloseParen(x, ()
listener: handleParenthesizedCondition((, null, null)
parseSwitchBlock())
ensureBlock(), null, switch statement)
listener: beginSwitchBlock({)
notEofOrValue(}, case)
peekPastLabels(case)
listener: beginCaseExpression(case)
parsePattern(case, precedence: 1, isRefutableContext: true)
parsePrimaryPattern(case, isRefutableContext: true)
listener: handleNoTypeArguments({)
parseMapPatternSuffix(case, isRefutableContext: true)
listener: handleRestPattern(..., false)
listener: handleMapPattern(1, {, })
ensureColon(})
listener: endCaseExpression(case, null, :)
peekPastLabels(break)
parseStatementsInSwitchCase(:, break, case, 0, 1, null, null)
listener: beginSwitchCase(0, 1, case)
parseStatement(:)
parseStatementX(:)
parseBreakStatement(:)
isBreakAllowed()
ensureSemicolon(break)
listener: handleBreakStatement(false, break, ;)
peekPastLabels(})
listener: endSwitchCase(0, 1, null, null, 1, case, })
notEofOrValue(}, })
listener: endSwitchBlock(1, {, })
listener: endSwitchStatement(switch, })
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,15 @@
void f(x) {
switch (x) {
case {...}:
break;
}
}


void[KeywordToken] f[StringToken]([BeginToken]x[StringToken])[SimpleToken] {[BeginToken]
switch[KeywordToken] ([BeginToken]x[StringToken])[SimpleToken] {[BeginToken]
case[KeywordToken] {[BeginToken]...[SimpleToken]}[SimpleToken]:[SimpleToken]
break[KeywordToken];[SimpleToken]
}[SimpleToken]
}[SimpleToken]
[SimpleToken]
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
void f(x) {
switch (x) {
case {...}:
break;
}
}


void[KeywordToken] f[StringToken]([BeginToken]x[StringToken])[SimpleToken] {[BeginToken]
switch[KeywordToken] ([BeginToken]x[StringToken])[SimpleToken] {[BeginToken]
case[KeywordToken] {[BeginToken]...[SimpleToken]}[SimpleToken]:[SimpleToken]
break[KeywordToken];[SimpleToken]
}[SimpleToken]
}[SimpleToken]
[SimpleToken]
1 change: 1 addition & 0 deletions pkg/front_end/test/spell_checking_list_code.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ abbreviate
abc
abcdef
abs
accepting
accounting
accounts
accumulated
Expand Down

0 comments on commit a3befd3

Please sign in to comment.