Skip to content

Commit

Permalink
Additional test cases in support of record/variable pattern disambigu…
Browse files Browse the repository at this point in the history
…ation.

Two of these tests (`recordPattern_nullable_beforeAs` and
`recordPattern_nullable_beforeWhen`) verify that the fix for #52439
(Destructuring with explicit type of nullable record is a parsing
error), which causes `(...)? identifier` to be recognized as a
variable pattern, doesn't get confused by the pseudo-identifiers `as`
and `when`, and so it continues to correctly parse `(...)? as ...` as a
cast pattern and `(...)? when ...` as a guarded pattern.

The other two tests (`recordPattern_nonNullable_beforeAs` and
`recordPattern_nonNullable_beforeWhen`) verify that `(...) as ...` and
`(...) when ...` are parsed correctly. These forms were never broken,
but they were not well tested either.

Bug: #52439
Change-Id: I866e1c7a6a8e47e0cd91a1a77654405f395b50da
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/305844
Reviewed-by: Jens Johansen <[email protected]>
Commit-Queue: Paul Berry <[email protected]>
  • Loading branch information
stereotype441 authored and Commit Queue committed May 30, 2023
1 parent cf07d75 commit 76f803a
Show file tree
Hide file tree
Showing 21 changed files with 911 additions and 0 deletions.
116 changes: 116 additions & 0 deletions pkg/analyzer/test/generated/patterns_parser_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8509,6 +8509,122 @@ NullCheckPattern
''');
}

test_recordPattern_nonNullable_beforeAs() {
_parse('''
void f(x) {
switch (x) {
case (_,) as (Object,):
}
}
''');
var node = findNode.singleGuardedPattern;
assertParsedNodeText(node, r'''
GuardedPattern
pattern: CastPattern
pattern: RecordPattern
leftParenthesis: (
fields
PatternField
pattern: WildcardPattern
name: _
rightParenthesis: )
asToken: as
type: RecordTypeAnnotation
leftParenthesis: (
positionalFields
RecordTypeAnnotationPositionalField
type: NamedType
name: Object
rightParenthesis: )
''');
}

test_recordPattern_nonNullable_beforeWhen() {
_parse('''
void f(x) {
switch (x) {
case (_,) when true:
}
}
''');
var node = findNode.singleGuardedPattern;
assertParsedNodeText(node, r'''
GuardedPattern
pattern: RecordPattern
leftParenthesis: (
fields
PatternField
pattern: WildcardPattern
name: _
rightParenthesis: )
whenClause: WhenClause
whenKeyword: when
expression: BooleanLiteral
literal: true
''');
}

test_recordPattern_nullable_beforeAs() {
_parse('''
void f(x) {
switch (x) {
case (_,)? as (Object,):
}
}
''', errors: [
error(ParserErrorCode.INVALID_INSIDE_UNARY_PATTERN, 36, 5),
]);
var node = findNode.singleGuardedPattern;
assertParsedNodeText(node, r'''
GuardedPattern
pattern: CastPattern
pattern: NullCheckPattern
pattern: RecordPattern
leftParenthesis: (
fields
PatternField
pattern: WildcardPattern
name: _
rightParenthesis: )
operator: ?
asToken: as
type: RecordTypeAnnotation
leftParenthesis: (
positionalFields
RecordTypeAnnotationPositionalField
type: NamedType
name: Object
rightParenthesis: )
''');
}

test_recordPattern_nullable_beforeWhen() {
_parse('''
void f(x) {
switch (x) {
case (_,)? when true:
}
}
''');
var node = findNode.singleGuardedPattern;
assertParsedNodeText(node, r'''
GuardedPattern
pattern: NullCheckPattern
pattern: RecordPattern
leftParenthesis: (
fields
PatternField
pattern: WildcardPattern
name: _
rightParenthesis: )
operator: ?
whenClause: WhenClause
whenKeyword: when
expression: BooleanLiteral
literal: true
''');
}

test_recordTypedVariablePattern_nonNullable_beforeAnd() {
_parse('''
void f(x) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
void f(x) {
switch (x) {
case (_,) as (Object,):
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
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)
beginPattern(case)
beginPattern(()
handleNoType(_)
handleWildcardPattern(null, _)
endPattern(_)
handlePatternField(null)
handleRecordPattern((, 1)
beginAsOperatorType(as)
beginRecordType(()
beginRecordTypeEntry()
beginMetadataStar(Object)
endMetadataStar(0)
handleIdentifier(Object, typeReference)
handleNoTypeArguments(,)
handleType(Object, null)
handleNoName(,)
endRecordTypeEntry()
endRecordType((, null, 1, false)
endAsOperatorType(as)
handleCastPattern(as)
endPattern())
handleSwitchCaseNoWhenClause())
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, )
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
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)
listener: beginPattern(case)
parsePrimaryPattern(case, PatternContext.matching)
parseParenthesizedPatternOrRecordPattern(case, PatternContext.matching)
parsePattern((, PatternContext.matching, precedence: 1)
listener: beginPattern(()
parsePrimaryPattern((, PatternContext.matching)
parseVariablePattern((, PatternContext.matching, typeInfo: Instance of 'NoType')
listener: handleNoType(_)
listener: handleWildcardPattern(null, _)
listener: endPattern(_)
listener: handlePatternField(null)
ensureCloseParen(,, ()
listener: handleRecordPattern((, 1)
listener: beginAsOperatorType(as)
computeTypeAfterIsOrAs(as)
parseRecordType((, as, false)
listener: beginRecordType(()
parseRecordTypeField((, identifierIsOptional: true)
listener: beginRecordTypeEntry()
parseMetadataStar(()
listener: beginMetadataStar(Object)
listener: endMetadataStar(0)
listener: handleIdentifier(Object, typeReference)
listener: handleNoTypeArguments(,)
listener: handleType(Object, null)
listener: handleNoName(,)
listener: endRecordTypeEntry()
listener: endRecordType((, null, 1, false)
listener: endAsOperatorType(as)
listener: handleCastPattern(as)
listener: endPattern())
listener: handleSwitchCaseNoWhenClause())
ensureColon())
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, )
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
void f(x) {
switch (x) {
case (_,) as (Object,):
}
}


void[KeywordToken] f[StringToken]([BeginToken]x[StringToken])[SimpleToken] {[BeginToken]
switch[KeywordToken] ([BeginToken]x[StringToken])[SimpleToken] {[BeginToken]
case[KeywordToken] ([BeginToken]_[StringToken],[SimpleToken])[SimpleToken] as[KeywordToken] ([BeginToken]Object[StringToken],[SimpleToken])[SimpleToken]:[SimpleToken]
}[SimpleToken]
}[SimpleToken]
[SimpleToken]
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
void f(x) {
switch (x) {
case (_,) as (Object,):
}
}


void[KeywordToken] f[StringToken]([BeginToken]x[StringToken])[SimpleToken] {[BeginToken]
switch[KeywordToken] ([BeginToken]x[StringToken])[SimpleToken] {[BeginToken]
case[KeywordToken] ([BeginToken]_[StringToken],[SimpleToken])[SimpleToken] as[KeywordToken] ([BeginToken]Object[StringToken],[SimpleToken])[SimpleToken]:[SimpleToken]
}[SimpleToken]
}[SimpleToken]
[SimpleToken]
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
void f(x) {
switch (x) {
case (_,) when true:
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
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)
beginPattern(case)
beginPattern(()
handleNoType(_)
handleWildcardPattern(null, _)
endPattern(_)
handlePatternField(null)
handleRecordPattern((, 1)
endPattern())
beginSwitchCaseWhenClause(when)
handleLiteralBool(true)
endSwitchCaseWhenClause(true)
endCaseExpression(case, when, :)
beginSwitchCase(0, 1, case)
endSwitchCase(0, 1, null, null, 0, case, })
endSwitchBlock(1, {, })
endSwitchStatement(switch, })
endBlockFunctionBody(1, {, })
endTopLevelMethod(void, null, })
endTopLevelDeclaration()
endCompilationUnit(1, )
Loading

0 comments on commit 76f803a

Please sign in to comment.