Skip to content

Commit

Permalink
Patterns parsing: make it an error to use var before a type in a va…
Browse files Browse the repository at this point in the history
…riable pattern.

Bug: #50035
Change-Id: I46b23ef0d1856401eac4b0a05c6bc6008711d35a
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/291780
Commit-Queue: Paul Berry <[email protected]>
Reviewed-by: Jens Johansen <[email protected]>
  • Loading branch information
stereotype441 authored and Commit Queue committed Mar 30, 2023
1 parent de2abc6 commit 134007a
Show file tree
Hide file tree
Showing 34 changed files with 1,170 additions and 4 deletions.
9 changes: 5 additions & 4 deletions pkg/_fe_analyzer_shared/lib/src/parser/parser_impl.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9832,9 +9832,6 @@ class Parser {
if (optional('var', next) || optional('final', next)) {
token = keyword = next;
bool nextIsParen = optional("(", token.next!);
// TODO(paulberry): this accepts `var <type> name` as a variable
// pattern. We want to accept that for error recovery, but don't forget
// to report the appropriate error.
typeInfo = computeVariablePatternType(token, nextIsParen);
token = typeInfo.parseType(token, this);
} else {
Expand All @@ -9860,7 +9857,11 @@ class Parser {
}
break;
case PatternContext.matching:
// All forms of variable patterns are valid in a matching context.
// All forms of variable patterns are valid in a matching context. But
// we do need to check for redundant `var`.
if (typeInfo != noType && keyword != null && optional('var', keyword)) {
reportRecoverableError(keyword, codes.messageTypeAfterVar);
}
break;
case PatternContext.assignment:
// It is a compile-time error if a variable pattern appears in an
Expand Down
150 changes: 150 additions & 0 deletions pkg/analyzer/test/generated/patterns_parser_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3035,6 +3035,35 @@ PatternAssignment
''');
}

test_declaredVariable_inPatternAssignment_usingVarAndType() {
_parse('''
void f() {
[a, var int d] = y;
}
''', errors: [
error(ParserErrorCode.PATTERN_ASSIGNMENT_DECLARES_VARIABLE, 25, 1),
]);
var node = findNode.patternAssignment('=');
assertParsedNodeText(node, r'''
PatternAssignment
pattern: ListPattern
leftBracket: [
elements
AssignedVariablePattern
name: a
DeclaredVariablePattern
keyword: var
type: NamedType
name: SimpleIdentifier
token: int
name: d
rightBracket: ]
equals: =
expression: SimpleIdentifier
token: y
''');
}

test_errorRecovery_afterQuestionSuffixInExpression() {
// Based on co19 test `Language/Expressions/Conditional/syntax_t06.dart`.
// Even though we now support suffix `?` in patterns, we need to make sure
Expand Down Expand Up @@ -9189,6 +9218,98 @@ NullCheckPattern
''');
}

test_varKeywordInTypedVariablePattern_declarationContext() {
_parse('''
void f(int x) {
var (var int y) = x;
}
''', errors: [
error(ParserErrorCode.VARIABLE_PATTERN_KEYWORD_IN_DECLARATION_CONTEXT, 23,
3),
]);
var node = findNode.patternVariableDeclaration('= x').pattern;
assertParsedNodeText(node, r'''
ParenthesizedPattern
leftParenthesis: (
pattern: DeclaredVariablePattern
keyword: var
type: NamedType
name: SimpleIdentifier
token: int
name: y
rightParenthesis: )
''');
}

test_varKeywordInTypedVariablePattern_declarationContext_wildcard() {
_parse('''
void f(x) {
var (var int _) = x;
}
''', errors: [
error(ParserErrorCode.VARIABLE_PATTERN_KEYWORD_IN_DECLARATION_CONTEXT, 19,
3),
]);
var node = findNode.patternVariableDeclaration('= x').pattern;
assertParsedNodeText(node, r'''
ParenthesizedPattern
leftParenthesis: (
pattern: WildcardPattern
keyword: var
type: NamedType
name: SimpleIdentifier
token: int
name: _
rightParenthesis: )
''');
}

test_varKeywordInTypedVariablePattern_matchingContext() {
_parse('''
void f(x) {
switch (x) {
case var int y:
break;
}
}
''', errors: [
error(ParserErrorCode.VAR_AND_TYPE, 36, 3),
]);
var node = findNode.singleGuardedPattern;
assertParsedNodeText(node, r'''
GuardedPattern
pattern: DeclaredVariablePattern
keyword: var
type: NamedType
name: SimpleIdentifier
token: int
name: y
''');
}

test_varKeywordInTypedVariablePattern_matchingContext_wildcard() {
_parse('''
void f(x) {
switch (x) {
case var int _:
break;
}
}
''', errors: [
error(ParserErrorCode.VAR_AND_TYPE, 36, 3),
]);
var node = findNode.singleGuardedPattern;
assertParsedNodeText(node, r'''
GuardedPattern
pattern: WildcardPattern
keyword: var
type: NamedType
name: SimpleIdentifier
token: int
name: _
''');
}

test_wildcard_bare_beforeWhen() {
_parse('''
void f(x) {
Expand Down Expand Up @@ -9634,6 +9755,35 @@ PatternAssignment
''');
}

test_wildcard_inPatternAssignment_usingVarAndType() {
_parse('''
void f() {
[a, var int _] = y;
}
''', errors: [
error(ParserErrorCode.PATTERN_ASSIGNMENT_DECLARES_VARIABLE, 25, 1),
]);
var node = findNode.patternAssignment('=');
assertParsedNodeText(node, r'''
PatternAssignment
pattern: ListPattern
leftBracket: [
elements
AssignedVariablePattern
name: a
WildcardPattern
keyword: var
type: NamedType
name: SimpleIdentifier
token: int
name: _
rightBracket: ]
equals: =
expression: SimpleIdentifier
token: y
''');
}

test_wildcard_typed_insideCase() {
_parse('''
void f(x) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
void f() {
[a, var int d] = y;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
Problems reported:

parser/patterns/declaredVariable_inPatternAssignment_usingVarAndType:2:15: Variable 'd' can't be declared in a pattern assignment.
[a, var int d] = y;
^

beginCompilationUnit(void)
beginMetadataStar(void)
endMetadataStar(0)
beginTopLevelMember(void)
beginTopLevelMethod(, null, null)
handleVoidKeyword(void)
handleIdentifier(f, topLevelFunctionDeclaration)
handleNoTypeVariables(()
beginFormalParameters((, MemberKind.TopLevelMethod)
endFormalParameters(0, (, ), MemberKind.TopLevelMethod)
handleAsyncModifier(null, null)
beginBlockFunctionBody({)
handleNoTypeArguments([)
handleAssignedVariablePattern(a)
handleIdentifier(int, typeReference)
handleNoTypeArguments(d)
handleType(int, null)
handleRecoverableError(Message[PatternAssignmentDeclaresVariable, Variable 'd' can't be declared in a pattern assignment., Try using a preexisting variable or changing the assignment to a pattern variable declaration., {name: d}], d, d)
handleDeclaredVariablePattern(var, d, true)
handleListPattern(2, [, ])
handleIdentifier(y, expression)
handleNoTypeArguments(;)
handleNoArguments(;)
handleSend(y, ;)
handlePatternAssignment(=)
handleExpressionStatement(;)
endBlockFunctionBody(1, {, })
endTopLevelMethod(void, null, })
endTopLevelDeclaration()
endCompilationUnit(1, )
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
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)
listener: endFormalParameters(0, (, ), MemberKind.TopLevelMethod)
parseAsyncModifierOpt())
listener: handleAsyncModifier(null, null)
inPlainSync()
parseFunctionBody(), false, false)
listener: beginBlockFunctionBody({)
notEofOrValue(}, [)
parseStatement({)
parseStatementX({)
parseExpressionStatementOrDeclaration({, null)
parseExpressionStatementOrDeclarationAfterModifiers({, {, null, null, null, null)
looksLikeLocalFunction([)
parseExpressionStatement({)
parseExpression({)
looksLikeOuterPatternEquals({)
skipOuterPattern({)
parsePatternAssignment({)
parsePattern({, PatternContext.assignment, precedence: 1)
parsePrimaryPattern({, PatternContext.assignment)
listener: handleNoTypeArguments([)
parseListPatternSuffix({, PatternContext.assignment)
parsePattern([, PatternContext.assignment, precedence: 1)
parsePrimaryPattern([, PatternContext.assignment)
parseVariablePattern([, PatternContext.assignment, typeInfo: Instance of 'NoType')
listener: handleAssignedVariablePattern(a)
parsePattern(,, PatternContext.assignment, precedence: 1)
parsePrimaryPattern(,, PatternContext.assignment)
parseVariablePattern(,, PatternContext.assignment, typeInfo: Instance of 'NoType')
listener: handleIdentifier(int, typeReference)
listener: handleNoTypeArguments(d)
listener: handleType(int, null)
reportRecoverableError(d, Message[PatternAssignmentDeclaresVariable, Variable 'd' can't be declared in a pattern assignment., Try using a preexisting variable or changing the assignment to a pattern variable declaration., {name: d}])
listener: handleRecoverableError(Message[PatternAssignmentDeclaresVariable, Variable 'd' can't be declared in a pattern assignment., Try using a preexisting variable or changing the assignment to a pattern variable declaration., {name: d}], d, d)
listener: handleDeclaredVariablePattern(var, d, true)
listener: handleListPattern(2, [, ])
parseExpression(=)
looksLikeOuterPatternEquals(=)
skipOuterPattern(=)
skipObjectPatternRest(y)
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(y, expression)
listener: handleNoTypeArguments(;)
parseArgumentsOpt(y)
listener: handleNoArguments(;)
listener: handleSend(y, ;)
listener: handlePatternAssignment(=)
ensureSemicolon(y)
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() {
[a, var int d] = y;
}


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


void[KeywordToken] f[StringToken]([BeginToken])[SimpleToken] {[BeginToken]
[[BeginToken]a[StringToken],[SimpleToken] var[KeywordToken] int[StringToken] d[StringToken]][SimpleToken] =[SimpleToken] y[StringToken];[SimpleToken]
}[SimpleToken]
[SimpleToken]
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
void f(int x) {
var (var int y) = x;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
Problems reported:

parser/patterns/varKeywordInTypedVariablePattern_declarationContext:2:8: Variable patterns in declaration context can't specify 'var' or 'final' keyword.
var (var int y) = x;
^^^

beginCompilationUnit(void)
beginMetadataStar(void)
endMetadataStar(0)
beginTopLevelMember(void)
beginTopLevelMethod(, null, null)
handleVoidKeyword(void)
handleIdentifier(f, topLevelFunctionDeclaration)
handleNoTypeVariables(()
beginFormalParameters((, MemberKind.TopLevelMethod)
beginMetadataStar(int)
endMetadataStar(0)
beginFormalParameter(int, MemberKind.TopLevelMethod, null, null, null)
handleIdentifier(int, typeReference)
handleNoTypeArguments(x)
handleType(int, null)
handleIdentifier(x, formalParameterDeclaration)
handleFormalParameterWithoutValue())
endFormalParameter(null, null, null, x, null, null, FormalParameterKind.requiredPositional, MemberKind.TopLevelMethod)
endFormalParameters(1, (, ), MemberKind.TopLevelMethod)
handleAsyncModifier(null, null)
beginBlockFunctionBody({)
beginMetadataStar(var)
endMetadataStar(0)
handleIdentifier(int, typeReference)
handleNoTypeArguments(y)
handleType(int, null)
handleRecoverableError(VariablePatternKeywordInDeclarationContext, var, var)
handleDeclaredVariablePattern(var, y, false)
handleParenthesizedPattern(()
handleIdentifier(x, expression)
handleNoTypeArguments(;)
handleNoArguments(;)
handleSend(x, ;)
handlePatternVariableDeclarationStatement(var, =, ;)
endBlockFunctionBody(1, {, })
endTopLevelMethod(void, null, })
endTopLevelDeclaration()
endCompilationUnit(1, )
Loading

0 comments on commit 134007a

Please sign in to comment.