mirror of
https://github.com/dart-lang/sdk
synced 2024-10-06 12:57:42 +00:00
Patterns parsing: handle incomplete identifier .
in a pattern.
Bug: https://github.com/Dart-Code/Dart-Code/issues/4407 Change-Id: Ibac9822fa6cf2f15aa67b68a28f399cf4373e782 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/291043 Reviewed-by: Konstantin Shcheglov <scheglov@google.com> Reviewed-by: Johnni Winther <johnniwinther@google.com> Commit-Queue: Paul Berry <paulberry@google.com>
This commit is contained in:
parent
c927840920
commit
8f6325d650
|
@ -9785,7 +9785,8 @@ class Parser {
|
|||
// `typeIdentifier | qualifiedName`, but that permits `a.b.c`,
|
||||
// which doesn't make sense.
|
||||
} else {
|
||||
throw new UnimplementedError('TODO(paulberry)');
|
||||
secondIdentifier = IdentifierContext.expressionContinuation
|
||||
.ensureIdentifier(token, this);
|
||||
}
|
||||
}
|
||||
TypeParamOrArgInfo potentialTypeArg = computeTypeParamOrArg(token);
|
||||
|
|
|
@ -21,6 +21,35 @@ main() {
|
|||
class PatternsTest extends ParserDiagnosticsTest {
|
||||
late FindNode findNode;
|
||||
|
||||
test_case_identifier_dot_incomplete() {
|
||||
// Based on the repro from
|
||||
// https://github.com/Dart-Code/Dart-Code/issues/4407.
|
||||
_parse('''
|
||||
void f(x) {
|
||||
switch (x) {
|
||||
case A.
|
||||
}
|
||||
}
|
||||
''', errors: [
|
||||
error(ParserErrorCode.MISSING_IDENTIFIER, 41, 1),
|
||||
error(ParserErrorCode.EXPECTED_TOKEN, 41, 1),
|
||||
]);
|
||||
var node = findNode.switchPatternCase('case');
|
||||
assertParsedNodeText(node, r'''
|
||||
SwitchPatternCase
|
||||
keyword: case
|
||||
guardedPattern: GuardedPattern
|
||||
pattern: ConstantPattern
|
||||
expression: PrefixedIdentifier
|
||||
prefix: SimpleIdentifier
|
||||
token: A
|
||||
period: .
|
||||
identifier: SimpleIdentifier
|
||||
token: <empty> <synthetic>
|
||||
colon: : <synthetic>
|
||||
''');
|
||||
}
|
||||
|
||||
test_caseHead_withClassicPattern_guarded_insideIfElement() {
|
||||
_parse('''
|
||||
void f(x) {
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
void f(x) {
|
||||
switch (x) {
|
||||
case A.
|
||||
}
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
Problems reported:
|
||||
|
||||
parser/patterns/case_identifier_dot_incomplete:4:3: Expected an identifier, but got '}'.
|
||||
}
|
||||
^
|
||||
|
||||
parser/patterns/case_identifier_dot_incomplete:4:3: Expected ':' before this.
|
||||
}
|
||||
^
|
||||
|
||||
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)
|
||||
handleRecoverableError(Message[ExpectedIdentifier, Expected an identifier, but got '}'., Try inserting an identifier before '}'., {lexeme: }}], }, })
|
||||
beginConstantPattern(null)
|
||||
handleIdentifier(A, expression)
|
||||
handleNoTypeArguments(.)
|
||||
handleNoArguments(.)
|
||||
handleSend(A, .)
|
||||
handleIdentifier(, expressionContinuation)
|
||||
handleNoTypeArguments(})
|
||||
handleNoArguments(})
|
||||
handleSend(, })
|
||||
handleEndingBinaryExpression(.)
|
||||
endConstantPattern(null)
|
||||
handleSwitchCaseNoWhenClause()
|
||||
handleRecoverableError(Message[ExpectedButGot, Expected ':' before this., null, {string: :}], }, })
|
||||
endCaseExpression(case, null, :)
|
||||
beginSwitchCase(0, 1, case)
|
||||
endSwitchCase(0, 1, null, null, 0, case, })
|
||||
endSwitchBlock(1, {, })
|
||||
endSwitchStatement(switch, })
|
||||
endBlockFunctionBody(1, {, })
|
||||
endTopLevelMethod(void, null, })
|
||||
endTopLevelDeclaration()
|
||||
endCompilationUnit(1, )
|
|
@ -0,0 +1,118 @@
|
|||
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(()
|
||||
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)
|
||||
parseSwitchBlock())
|
||||
ensureBlock(), null, switch statement)
|
||||
listener: beginSwitchBlock({)
|
||||
notEofOrValue(}, case)
|
||||
peekPastLabels(case)
|
||||
listener: beginCaseExpression(case)
|
||||
parsePattern(case, PatternContext.matching, precedence: 1)
|
||||
parsePrimaryPattern(case, PatternContext.matching)
|
||||
reportRecoverableErrorWithToken(}, Instance of 'Template<(Token) => Message>')
|
||||
listener: handleRecoverableError(Message[ExpectedIdentifier, Expected an identifier, but got '}'., Try inserting an identifier before '}'., {lexeme: }}], }, })
|
||||
rewriter()
|
||||
listener: beginConstantPattern(null)
|
||||
parsePrecedenceExpression(case, 7, false, ConstantPatternContext.implicit)
|
||||
parseUnaryExpression(case, false, ConstantPatternContext.implicit)
|
||||
parsePrimary(case, expression, ConstantPatternContext.implicit)
|
||||
parseSendOrFunctionLiteral(case, expression, ConstantPatternContext.implicit)
|
||||
parseSend(case, expression, ConstantPatternContext.implicit)
|
||||
isNextIdentifier(case)
|
||||
ensureIdentifier(case, expression)
|
||||
listener: handleIdentifier(A, expression)
|
||||
listener: handleNoTypeArguments(.)
|
||||
parseArgumentsOpt(A)
|
||||
listener: handleNoArguments(.)
|
||||
listener: handleSend(A, .)
|
||||
parsePrimary(., expressionContinuation, ConstantPatternContext.implicit)
|
||||
parseSendOrFunctionLiteral(., expressionContinuation, ConstantPatternContext.implicit)
|
||||
parseSend(., expressionContinuation, ConstantPatternContext.implicit)
|
||||
isNextIdentifier(.)
|
||||
ensureIdentifier(., expressionContinuation)
|
||||
listener: handleIdentifier(, expressionContinuation)
|
||||
listener: handleNoTypeArguments(})
|
||||
parseArgumentsOpt()
|
||||
listener: handleNoArguments(})
|
||||
listener: handleSend(, })
|
||||
listener: handleEndingBinaryExpression(.)
|
||||
listener: endConstantPattern(null)
|
||||
listener: handleSwitchCaseNoWhenClause()
|
||||
ensureColon()
|
||||
rewriteAndRecover(, Message[ExpectedButGot, Expected ':' before this., null, {string: :}], :)
|
||||
reportRecoverableError(}, Message[ExpectedButGot, Expected ':' before this., null, {string: :}])
|
||||
listener: handleRecoverableError(Message[ExpectedButGot, Expected ':' before this., null, {string: :}], }, })
|
||||
rewriter()
|
||||
listener: endCaseExpression(case, null, :)
|
||||
peekPastLabels(})
|
||||
parseStatementsInSwitchCase(:, }, case, 0, 1, null, null)
|
||||
listener: beginSwitchCase(0, 1, case)
|
||||
listener: endSwitchCase(0, 1, null, null, 0, case, })
|
||||
notEofOrValue(}, })
|
||||
listener: endSwitchBlock(1, {, })
|
||||
listener: endSwitchStatement(switch, })
|
||||
notEofOrValue(}, })
|
||||
listener: endBlockFunctionBody(1, {, })
|
||||
listener: endTopLevelMethod(void, null, })
|
||||
listener: endTopLevelDeclaration()
|
||||
reportAllErrorTokens(void)
|
||||
listener: endCompilationUnit(1, )
|
|
@ -0,0 +1,15 @@
|
|||
NOTICE: Stream was rewritten by parser!
|
||||
|
||||
void f(x) {
|
||||
switch (x) {
|
||||
case A.
|
||||
*synthetic*:}
|
||||
}
|
||||
|
||||
|
||||
void[KeywordToken] f[StringToken]([BeginToken]x[StringToken])[SimpleToken] {[BeginToken]
|
||||
switch[KeywordToken] ([BeginToken]x[StringToken])[SimpleToken] {[BeginToken]
|
||||
case[KeywordToken] A[StringToken].[SimpleToken]
|
||||
[SyntheticStringToken]:[SyntheticToken]}[SimpleToken]
|
||||
}[SimpleToken]
|
||||
[SimpleToken]
|
|
@ -0,0 +1,13 @@
|
|||
void f(x) {
|
||||
switch (x) {
|
||||
case A.
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void[KeywordToken] f[StringToken]([BeginToken]x[StringToken])[SimpleToken] {[BeginToken]
|
||||
switch[KeywordToken] ([BeginToken]x[StringToken])[SimpleToken] {[BeginToken]
|
||||
case[KeywordToken] A[StringToken].[SimpleToken]
|
||||
}[SimpleToken]
|
||||
}[SimpleToken]
|
||||
[SimpleToken]
|
Loading…
Reference in a new issue