mirror of
https://github.com/dart-lang/sdk
synced 2024-10-04 20:39:38 +00:00
Patterns parsing: fix error recovery for switch expression using :
If a user attempts to use `:` instead of `=>` in a switch expression, we now generate an error at the location of each `:` token (and otherwise interpret the switch expression in the way the user intended). Fixes #50930. Bug: https://github.com/dart-lang/sdk/issues/50930 Change-Id: I3687fb464b6875424745e664b0d28346b3cd6375 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/292982 Reviewed-by: Konstantin Shcheglov <scheglov@google.com> Commit-Queue: Paul Berry <paulberry@google.com>
This commit is contained in:
parent
40973edbb5
commit
d78fe25542
|
@ -10325,7 +10325,16 @@ class Parser {
|
|||
when = token = next;
|
||||
token = parseExpression(token);
|
||||
}
|
||||
Token arrow = token = ensureFunctionArrow(token);
|
||||
Token arrow;
|
||||
if (optional(':', next)) {
|
||||
// User accidentally used `:` instead of `=>`
|
||||
arrow = next;
|
||||
reportRecoverableError(
|
||||
arrow, codes.templateExpectedButGot.withArguments('=>'));
|
||||
} else {
|
||||
arrow = ensureFunctionArrow(token);
|
||||
}
|
||||
token = arrow;
|
||||
mayParseFunctionExpressions = true;
|
||||
token = parseExpression(token);
|
||||
mayParseFunctionExpressions = false;
|
||||
|
|
|
@ -9237,6 +9237,46 @@ SwitchExpression
|
|||
''');
|
||||
}
|
||||
|
||||
test_switchExpression_recovery_colonInsteadOfArrow() {
|
||||
_parse('''
|
||||
f(x) => switch (x) {
|
||||
1: 'one',
|
||||
2: 'two'
|
||||
};
|
||||
''', errors: [
|
||||
error(ParserErrorCode.EXPECTED_TOKEN, 24, 1),
|
||||
error(ParserErrorCode.EXPECTED_TOKEN, 36, 1),
|
||||
]);
|
||||
var node = findNode.switchExpression('switch');
|
||||
assertParsedNodeText(node, r'''
|
||||
SwitchExpression
|
||||
switchKeyword: switch
|
||||
leftParenthesis: (
|
||||
expression: SimpleIdentifier
|
||||
token: x
|
||||
rightParenthesis: )
|
||||
leftBracket: {
|
||||
cases
|
||||
SwitchExpressionCase
|
||||
guardedPattern: GuardedPattern
|
||||
pattern: ConstantPattern
|
||||
expression: IntegerLiteral
|
||||
literal: 1
|
||||
arrow: :
|
||||
expression: SimpleStringLiteral
|
||||
literal: 'one'
|
||||
SwitchExpressionCase
|
||||
guardedPattern: GuardedPattern
|
||||
pattern: ConstantPattern
|
||||
expression: IntegerLiteral
|
||||
literal: 2
|
||||
arrow: :
|
||||
expression: SimpleStringLiteral
|
||||
literal: 'two'
|
||||
rightBracket: }
|
||||
''');
|
||||
}
|
||||
|
||||
test_switchExpression_recovery_missingComma() {
|
||||
// If the extra tokens after a switch case look like they could be a
|
||||
// pattern, the parser assumes there's a missing comma.
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
f(x) => switch (x) {
|
||||
1: 'one',
|
||||
2: 'two'
|
||||
};
|
|
@ -0,0 +1,59 @@
|
|||
Problems reported:
|
||||
|
||||
parser/patterns/switchExpression_recovery_colonInsteadOfArrow:2:4: Expected '=>' before this.
|
||||
1: 'one',
|
||||
^
|
||||
|
||||
parser/patterns/switchExpression_recovery_colonInsteadOfArrow:3:4: Expected '=>' before this.
|
||||
2: 'two'
|
||||
^
|
||||
|
||||
beginCompilationUnit(f)
|
||||
beginMetadataStar(f)
|
||||
endMetadataStar(0)
|
||||
beginTopLevelMember(f)
|
||||
beginTopLevelMethod(, null, null)
|
||||
handleNoType()
|
||||
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)
|
||||
beginSwitchExpression(switch)
|
||||
handleIdentifier(x, expression)
|
||||
handleNoTypeArguments())
|
||||
handleNoArguments())
|
||||
handleSend(x, ))
|
||||
handleParenthesizedCondition((, null, null)
|
||||
beginSwitchExpressionBlock({)
|
||||
beginSwitchExpressionCase()
|
||||
beginConstantPattern(null)
|
||||
handleLiteralInt(1)
|
||||
endConstantPattern(null)
|
||||
handleSwitchExpressionCasePattern(1)
|
||||
handleRecoverableError(Message[ExpectedButGot, Expected '=>' before this., null, {string: =>}], :, :)
|
||||
beginLiteralString('one')
|
||||
endLiteralString(0, ,)
|
||||
endSwitchExpressionCase(null, :, 'one')
|
||||
beginSwitchExpressionCase()
|
||||
beginConstantPattern(null)
|
||||
handleLiteralInt(2)
|
||||
endConstantPattern(null)
|
||||
handleSwitchExpressionCasePattern(2)
|
||||
handleRecoverableError(Message[ExpectedButGot, Expected '=>' before this., null, {string: =>}], :, :)
|
||||
beginLiteralString('two')
|
||||
endLiteralString(0, })
|
||||
endSwitchExpressionCase(null, :, 'two')
|
||||
endSwitchExpressionBlock(2, {, })
|
||||
endSwitchExpression(switch, })
|
||||
handleExpressionFunctionBody(=>, ;)
|
||||
endTopLevelMethod(f, null, ;)
|
||||
endTopLevelDeclaration()
|
||||
endCompilationUnit(1, )
|
|
@ -0,0 +1,126 @@
|
|||
parseUnit(f)
|
||||
skipErrorTokens(f)
|
||||
listener: beginCompilationUnit(f)
|
||||
syntheticPreviousToken(f)
|
||||
parseTopLevelDeclarationImpl(, Instance of 'DirectiveContext')
|
||||
parseMetadataStar()
|
||||
listener: beginMetadataStar(f)
|
||||
listener: endMetadataStar(0)
|
||||
parseTopLevelMemberImpl()
|
||||
listener: beginTopLevelMember(f)
|
||||
isReservedKeyword(()
|
||||
parseTopLevelMethod(, null, null, , Instance of 'NoType', null, f, false)
|
||||
listener: beginTopLevelMethod(, null, null)
|
||||
listener: handleNoType()
|
||||
ensureIdentifierPotentiallyRecovered(, 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)
|
||||
parseExpressionFunctionBody(=>, false)
|
||||
parseExpression(=>)
|
||||
looksLikeOuterPatternEquals(=>)
|
||||
skipOuterPattern(=>)
|
||||
parsePrecedenceExpression(=>, 1, true, ConstantPatternContext.none)
|
||||
parseUnaryExpression(=>, true, ConstantPatternContext.none)
|
||||
parsePrimary(=>, expression, ConstantPatternContext.none)
|
||||
inPlainSync()
|
||||
parseSwitchExpression(=>)
|
||||
listener: beginSwitchExpression(switch)
|
||||
ensureParenthesizedCondition(switch, allowCase: false)
|
||||
parseExpressionInParenthesisRest((, allowCase: false)
|
||||
parseExpression(()
|
||||
looksLikeOuterPatternEquals(()
|
||||
skipOuterPattern(()
|
||||
skipObjectPatternRest(x)
|
||||
parsePrecedenceExpression((, 1, true, ConstantPatternContext.none)
|
||||
parseUnaryExpression((, true, ConstantPatternContext.none)
|
||||
parsePrimary((, expression, ConstantPatternContext.none)
|
||||
parseSendOrFunctionLiteral((, expression, ConstantPatternContext.none)
|
||||
parseSend((, expression, ConstantPatternContext.none)
|
||||
isNextIdentifier(()
|
||||
ensureIdentifier((, expression)
|
||||
listener: handleIdentifier(x, expression)
|
||||
listener: handleNoTypeArguments())
|
||||
parseArgumentsOpt(x)
|
||||
listener: handleNoArguments())
|
||||
listener: handleSend(x, ))
|
||||
ensureCloseParen(x, ()
|
||||
listener: handleParenthesizedCondition((, null, null)
|
||||
ensureBlock(), null, switch expression)
|
||||
listener: beginSwitchExpressionBlock({)
|
||||
listener: beginSwitchExpressionCase()
|
||||
parsePattern({, PatternContext.matching, precedence: 1)
|
||||
parsePrimaryPattern({, PatternContext.matching)
|
||||
listener: beginConstantPattern(null)
|
||||
parsePrecedenceExpression({, 7, false, ConstantPatternContext.implicit)
|
||||
parseUnaryExpression({, false, ConstantPatternContext.implicit)
|
||||
parsePrimary({, expression, ConstantPatternContext.implicit)
|
||||
parseLiteralInt({)
|
||||
listener: handleLiteralInt(1)
|
||||
listener: endConstantPattern(null)
|
||||
listener: handleSwitchExpressionCasePattern(1)
|
||||
reportRecoverableError(:, Message[ExpectedButGot, Expected '=>' before this., null, {string: =>}])
|
||||
listener: handleRecoverableError(Message[ExpectedButGot, Expected '=>' before this., null, {string: =>}], :, :)
|
||||
parseExpression(:)
|
||||
looksLikeOuterPatternEquals(:)
|
||||
skipOuterPattern(:)
|
||||
parsePrecedenceExpression(:, 1, true, ConstantPatternContext.none)
|
||||
parseUnaryExpression(:, true, ConstantPatternContext.none)
|
||||
parsePrimary(:, expression, ConstantPatternContext.none)
|
||||
parseLiteralString(:)
|
||||
parseSingleLiteralString(:)
|
||||
listener: beginLiteralString('one')
|
||||
listener: endLiteralString(0, ,)
|
||||
listener: endSwitchExpressionCase(null, :, 'one')
|
||||
listener: beginSwitchExpressionCase()
|
||||
parsePattern(,, PatternContext.matching, precedence: 1)
|
||||
parsePrimaryPattern(,, PatternContext.matching)
|
||||
listener: beginConstantPattern(null)
|
||||
parsePrecedenceExpression(,, 7, false, ConstantPatternContext.implicit)
|
||||
parseUnaryExpression(,, false, ConstantPatternContext.implicit)
|
||||
parsePrimary(,, expression, ConstantPatternContext.implicit)
|
||||
parseLiteralInt(,)
|
||||
listener: handleLiteralInt(2)
|
||||
listener: endConstantPattern(null)
|
||||
listener: handleSwitchExpressionCasePattern(2)
|
||||
reportRecoverableError(:, Message[ExpectedButGot, Expected '=>' before this., null, {string: =>}])
|
||||
listener: handleRecoverableError(Message[ExpectedButGot, Expected '=>' before this., null, {string: =>}], :, :)
|
||||
parseExpression(:)
|
||||
looksLikeOuterPatternEquals(:)
|
||||
skipOuterPattern(:)
|
||||
parsePrecedenceExpression(:, 1, true, ConstantPatternContext.none)
|
||||
parseUnaryExpression(:, true, ConstantPatternContext.none)
|
||||
parsePrimary(:, expression, ConstantPatternContext.none)
|
||||
parseLiteralString(:)
|
||||
parseSingleLiteralString(:)
|
||||
listener: beginLiteralString('two')
|
||||
listener: endLiteralString(0, })
|
||||
listener: endSwitchExpressionCase(null, :, 'two')
|
||||
listener: endSwitchExpressionBlock(2, {, })
|
||||
listener: endSwitchExpression(switch, })
|
||||
ensureSemicolon(})
|
||||
listener: handleExpressionFunctionBody(=>, ;)
|
||||
inGenerator()
|
||||
listener: endTopLevelMethod(f, null, ;)
|
||||
listener: endTopLevelDeclaration()
|
||||
reportAllErrorTokens(f)
|
||||
listener: endCompilationUnit(1, )
|
|
@ -0,0 +1,11 @@
|
|||
f(x) => switch (x) {
|
||||
1: 'one',
|
||||
2: 'two'
|
||||
};
|
||||
|
||||
|
||||
f[StringToken]([BeginToken]x[StringToken])[SimpleToken] =>[SimpleToken] switch[KeywordToken] ([BeginToken]x[StringToken])[SimpleToken] {[BeginToken]
|
||||
1[StringToken]:[SimpleToken] 'one'[StringToken],[SimpleToken]
|
||||
2[StringToken]:[SimpleToken] 'two'[StringToken]
|
||||
}[SimpleToken];[SimpleToken]
|
||||
[SimpleToken]
|
|
@ -0,0 +1,11 @@
|
|||
f(x) => switch (x) {
|
||||
1: 'one',
|
||||
2: 'two'
|
||||
};
|
||||
|
||||
|
||||
f[StringToken]([BeginToken]x[StringToken])[SimpleToken] =>[SimpleToken] switch[KeywordToken] ([BeginToken]x[StringToken])[SimpleToken] {[BeginToken]
|
||||
1[StringToken]:[SimpleToken] 'one'[StringToken],[SimpleToken]
|
||||
2[StringToken]:[SimpleToken] 'two'[StringToken]
|
||||
}[SimpleToken];[SimpleToken]
|
||||
[SimpleToken]
|
Loading…
Reference in a new issue