mirror of
https://github.com/dart-lang/sdk
synced 2024-10-06 15:09:45 +00:00
Fix error reporting and recovery for declared variable in pattern assignment.
If a declared variable occurs inside a pattern assignment (e.g. `[a, var b] = c`), we now recover using the `DeclaredVariablePattern` AST node. Normally, a `DeclaredVariablePattern` wouldn't appear inside a pattern assignment, but it's better to recover in this way because it avoids dropping the keyword token (`var` or `final`) or type. Fixes #50927. Fixes #51529. Bug: https://github.com/dart-lang/sdk/issues/50927, https://github.com/dart-lang/sdk/issues/51529 Change-Id: Ia65853baeae0e8eb999b7bd82fb2455e10b2b996 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/291044 Reviewed-by: Konstantin Shcheglov <scheglov@google.com> Commit-Queue: Paul Berry <paulberry@google.com>
This commit is contained in:
parent
f4bbc427c3
commit
2c16ff8827
|
@ -10921,8 +10921,8 @@ const Template<
|
||||||
const Code<Message Function(String name)>
|
const Code<Message Function(String name)>
|
||||||
codePatternAssignmentDeclaresVariable =
|
codePatternAssignmentDeclaresVariable =
|
||||||
const Code<Message Function(String name)>(
|
const Code<Message Function(String name)>(
|
||||||
"PatternAssignmentDeclaresVariable",
|
"PatternAssignmentDeclaresVariable",
|
||||||
);
|
index: 145);
|
||||||
|
|
||||||
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
|
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
|
||||||
Message _withArgumentsPatternAssignmentDeclaresVariable(String name) {
|
Message _withArgumentsPatternAssignmentDeclaresVariable(String name) {
|
||||||
|
|
|
@ -2384,6 +2384,8 @@ ParserErrorCode.NULL_AWARE_CASCADE_OUT_OF_ORDER:
|
||||||
status: needsEvaluation
|
status: needsEvaluation
|
||||||
ParserErrorCode.OUT_OF_ORDER_CLAUSES:
|
ParserErrorCode.OUT_OF_ORDER_CLAUSES:
|
||||||
status: needsEvaluation
|
status: needsEvaluation
|
||||||
|
ParserErrorCode.PATTERN_ASSIGNMENT_DECLARES_VARIABLE:
|
||||||
|
status: needsEvaluation
|
||||||
ParserErrorCode.POSITIONAL_AFTER_NAMED_ARGUMENT:
|
ParserErrorCode.POSITIONAL_AFTER_NAMED_ARGUMENT:
|
||||||
status: needsEvaluation
|
status: needsEvaluation
|
||||||
ParserErrorCode.POSITIONAL_PARAMETER_OUTSIDE_GROUP:
|
ParserErrorCode.POSITIONAL_PARAMETER_OUTSIDE_GROUP:
|
||||||
|
|
|
@ -159,6 +159,7 @@ final fastaAnalyzerErrorCodes = <ErrorCode?>[
|
||||||
ParserErrorCode.FINAL_MIXIN_CLASS,
|
ParserErrorCode.FINAL_MIXIN_CLASS,
|
||||||
ParserErrorCode.INTERFACE_MIXIN_CLASS,
|
ParserErrorCode.INTERFACE_MIXIN_CLASS,
|
||||||
ParserErrorCode.SEALED_MIXIN_CLASS,
|
ParserErrorCode.SEALED_MIXIN_CLASS,
|
||||||
|
ParserErrorCode.PATTERN_ASSIGNMENT_DECLARES_VARIABLE,
|
||||||
];
|
];
|
||||||
|
|
||||||
class ParserErrorCode extends ErrorCode {
|
class ParserErrorCode extends ErrorCode {
|
||||||
|
@ -1506,6 +1507,15 @@ class ParserErrorCode extends ErrorCode {
|
||||||
correctionMessage: "Try moving the '{0}' clause before the '{1}' clause.",
|
correctionMessage: "Try moving the '{0}' clause before the '{1}' clause.",
|
||||||
);
|
);
|
||||||
|
|
||||||
|
static const ParserErrorCode PATTERN_ASSIGNMENT_DECLARES_VARIABLE =
|
||||||
|
ParserErrorCode(
|
||||||
|
'PATTERN_ASSIGNMENT_DECLARES_VARIABLE',
|
||||||
|
"Variable '{0}' can't be declared in a pattern assignment.",
|
||||||
|
correctionMessage:
|
||||||
|
"Try using a preexisting variable or changing the assignment to a "
|
||||||
|
"pattern variable declaration.",
|
||||||
|
);
|
||||||
|
|
||||||
static const ParserErrorCode POSITIONAL_AFTER_NAMED_ARGUMENT =
|
static const ParserErrorCode POSITIONAL_AFTER_NAMED_ARGUMENT =
|
||||||
ParserErrorCode(
|
ParserErrorCode(
|
||||||
'POSITIONAL_AFTER_NAMED_ARGUMENT',
|
'POSITIONAL_AFTER_NAMED_ARGUMENT',
|
||||||
|
|
|
@ -1070,6 +1070,17 @@ class ResolutionVisitor extends RecursiveAstVisitor<void> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void visitPatternAssignment(PatternAssignment node) {
|
||||||
|
// We need to call `casePatternStart` and `casePatternFinish` in case there
|
||||||
|
// are any declared variable patterns inside the pattern assignment (this
|
||||||
|
// could happen due to error recovery). But we don't need to keep the
|
||||||
|
// variables map that `casePatternFinish` returns.
|
||||||
|
_patternVariables.casePatternStart();
|
||||||
|
super.visitPatternAssignment(node);
|
||||||
|
_patternVariables.casePatternFinish();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void visitPatternVariableDeclaration(
|
void visitPatternVariableDeclaration(
|
||||||
covariant PatternVariableDeclarationImpl node,
|
covariant PatternVariableDeclarationImpl node,
|
||||||
|
|
|
@ -818,6 +818,7 @@ const List<ErrorCode> errorCodeValues = [
|
||||||
ParserErrorCode.NORMAL_BEFORE_OPTIONAL_PARAMETERS,
|
ParserErrorCode.NORMAL_BEFORE_OPTIONAL_PARAMETERS,
|
||||||
ParserErrorCode.NULL_AWARE_CASCADE_OUT_OF_ORDER,
|
ParserErrorCode.NULL_AWARE_CASCADE_OUT_OF_ORDER,
|
||||||
ParserErrorCode.OUT_OF_ORDER_CLAUSES,
|
ParserErrorCode.OUT_OF_ORDER_CLAUSES,
|
||||||
|
ParserErrorCode.PATTERN_ASSIGNMENT_DECLARES_VARIABLE,
|
||||||
ParserErrorCode.POSITIONAL_AFTER_NAMED_ARGUMENT,
|
ParserErrorCode.POSITIONAL_AFTER_NAMED_ARGUMENT,
|
||||||
ParserErrorCode.POSITIONAL_PARAMETER_OUTSIDE_GROUP,
|
ParserErrorCode.POSITIONAL_PARAMETER_OUTSIDE_GROUP,
|
||||||
ParserErrorCode.PREFIX_AFTER_COMBINATOR,
|
ParserErrorCode.PREFIX_AFTER_COMBINATOR,
|
||||||
|
|
|
@ -32,7 +32,8 @@ import 'package:_fe_analyzer_shared/src/messages/codes.dart'
|
||||||
templateExpectedIdentifier,
|
templateExpectedIdentifier,
|
||||||
templateExperimentNotEnabled,
|
templateExperimentNotEnabled,
|
||||||
templateExtraneousModifier,
|
templateExtraneousModifier,
|
||||||
templateInternalProblemUnhandled;
|
templateInternalProblemUnhandled,
|
||||||
|
templatePatternAssignmentDeclaresVariable;
|
||||||
import 'package:_fe_analyzer_shared/src/parser/parser.dart'
|
import 'package:_fe_analyzer_shared/src/parser/parser.dart'
|
||||||
show
|
show
|
||||||
Assert,
|
Assert,
|
||||||
|
@ -5502,6 +5503,18 @@ class AstBuilder extends StackListener {
|
||||||
throw UnimplementedError('Patterns not enabled');
|
throw UnimplementedError('Patterns not enabled');
|
||||||
}
|
}
|
||||||
var type = pop() as TypeAnnotationImpl?;
|
var type = pop() as TypeAnnotationImpl?;
|
||||||
|
if (inAssignmentPattern && (type != null || keyword != null)) {
|
||||||
|
// TODO(paulberry): Consider generating this error in the parser
|
||||||
|
// This error is also reported in the body builder
|
||||||
|
handleRecoverableError(
|
||||||
|
templatePatternAssignmentDeclaresVariable
|
||||||
|
.withArguments(variable.lexeme),
|
||||||
|
variable,
|
||||||
|
variable);
|
||||||
|
// To ensure that none of the tokens are dropped from the AST, don't build
|
||||||
|
// an `AssignedVariablePatternImpl`.
|
||||||
|
inAssignmentPattern = false;
|
||||||
|
}
|
||||||
if (variable.lexeme == '_') {
|
if (variable.lexeme == '_') {
|
||||||
push(
|
push(
|
||||||
WildcardPatternImpl(
|
WildcardPatternImpl(
|
||||||
|
|
|
@ -2926,6 +2926,86 @@ NullCheckPattern
|
||||||
''');
|
''');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test_declaredVariable_inPatternAssignment_usingFinal() {
|
||||||
|
_parse('''
|
||||||
|
void f() {
|
||||||
|
[a, final d] = y;
|
||||||
|
}
|
||||||
|
''', errors: [
|
||||||
|
error(ParserErrorCode.PATTERN_ASSIGNMENT_DECLARES_VARIABLE, 23, 1),
|
||||||
|
]);
|
||||||
|
var node = findNode.patternAssignment('=');
|
||||||
|
assertParsedNodeText(node, r'''
|
||||||
|
PatternAssignment
|
||||||
|
pattern: ListPattern
|
||||||
|
leftBracket: [
|
||||||
|
elements
|
||||||
|
AssignedVariablePattern
|
||||||
|
name: a
|
||||||
|
DeclaredVariablePattern
|
||||||
|
keyword: final
|
||||||
|
name: d
|
||||||
|
rightBracket: ]
|
||||||
|
equals: =
|
||||||
|
expression: SimpleIdentifier
|
||||||
|
token: y
|
||||||
|
''');
|
||||||
|
}
|
||||||
|
|
||||||
|
test_declaredVariable_inPatternAssignment_usingType() {
|
||||||
|
_parse('''
|
||||||
|
void f() {
|
||||||
|
[a, int d] = y;
|
||||||
|
}
|
||||||
|
''', errors: [
|
||||||
|
error(ParserErrorCode.PATTERN_ASSIGNMENT_DECLARES_VARIABLE, 21, 1),
|
||||||
|
]);
|
||||||
|
var node = findNode.patternAssignment('=');
|
||||||
|
assertParsedNodeText(node, r'''
|
||||||
|
PatternAssignment
|
||||||
|
pattern: ListPattern
|
||||||
|
leftBracket: [
|
||||||
|
elements
|
||||||
|
AssignedVariablePattern
|
||||||
|
name: a
|
||||||
|
DeclaredVariablePattern
|
||||||
|
type: NamedType
|
||||||
|
name: SimpleIdentifier
|
||||||
|
token: int
|
||||||
|
name: d
|
||||||
|
rightBracket: ]
|
||||||
|
equals: =
|
||||||
|
expression: SimpleIdentifier
|
||||||
|
token: y
|
||||||
|
''');
|
||||||
|
}
|
||||||
|
|
||||||
|
test_declaredVariable_inPatternAssignment_usingVar() {
|
||||||
|
_parse('''
|
||||||
|
void f() {
|
||||||
|
[a, var d] = y;
|
||||||
|
}
|
||||||
|
''', errors: [
|
||||||
|
error(ParserErrorCode.PATTERN_ASSIGNMENT_DECLARES_VARIABLE, 21, 1),
|
||||||
|
]);
|
||||||
|
var node = findNode.patternAssignment('=');
|
||||||
|
assertParsedNodeText(node, r'''
|
||||||
|
PatternAssignment
|
||||||
|
pattern: ListPattern
|
||||||
|
leftBracket: [
|
||||||
|
elements
|
||||||
|
AssignedVariablePattern
|
||||||
|
name: a
|
||||||
|
DeclaredVariablePattern
|
||||||
|
keyword: var
|
||||||
|
name: d
|
||||||
|
rightBracket: ]
|
||||||
|
equals: =
|
||||||
|
expression: SimpleIdentifier
|
||||||
|
token: y
|
||||||
|
''');
|
||||||
|
}
|
||||||
|
|
||||||
test_errorRecovery_afterQuestionSuffixInExpression() {
|
test_errorRecovery_afterQuestionSuffixInExpression() {
|
||||||
// Based on co19 test `Language/Expressions/Conditional/syntax_t06.dart`.
|
// Based on co19 test `Language/Expressions/Conditional/syntax_t06.dart`.
|
||||||
// Even though we now support suffix `?` in patterns, we need to make sure
|
// Even though we now support suffix `?` in patterns, we need to make sure
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
// for details. All rights reserved. Use of this source code is governed by a
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
// BSD-style license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
import 'package:analyzer/src/dart/error/syntactic_errors.dart';
|
||||||
import 'package:analyzer/src/error/codes.dart';
|
import 'package:analyzer/src/error/codes.dart';
|
||||||
import 'package:test_reflective_loader/test_reflective_loader.dart';
|
import 'package:test_reflective_loader/test_reflective_loader.dart';
|
||||||
|
|
||||||
|
@ -339,6 +340,38 @@ PatternAssignment
|
||||||
''');
|
''');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test_declaredVariable_inPatternAssignment_referenced() async {
|
||||||
|
// Note: the error is reporting during parsing but we test it here to make
|
||||||
|
// sure that error recovery produces an AST that can be analyzed without
|
||||||
|
// crashing.
|
||||||
|
await assertErrorsInCode(r'''
|
||||||
|
void f(a, y) {
|
||||||
|
[a, var d] = y;
|
||||||
|
d;
|
||||||
|
}
|
||||||
|
''', [
|
||||||
|
// The reference doesn't resolve so the errors include
|
||||||
|
// UNUSED_LOCAL_VARIABLE and UNDEFINED_IDENTIFIER.
|
||||||
|
error(ParserErrorCode.PATTERN_ASSIGNMENT_DECLARES_VARIABLE, 25, 1),
|
||||||
|
error(HintCode.UNUSED_LOCAL_VARIABLE, 25, 1),
|
||||||
|
error(CompileTimeErrorCode.UNDEFINED_IDENTIFIER, 35, 1),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
test_declaredVariable_inPatternAssignment_unreferenced() async {
|
||||||
|
// Note: the error is reporting during parsing but we test it here to make
|
||||||
|
// sure that error recovery produces an AST that can be analyzed without
|
||||||
|
// crashing.
|
||||||
|
await assertErrorsInCode(r'''
|
||||||
|
void f(a, y) {
|
||||||
|
[a, var d] = y;
|
||||||
|
}
|
||||||
|
''', [
|
||||||
|
error(ParserErrorCode.PATTERN_ASSIGNMENT_DECLARES_VARIABLE, 25, 1),
|
||||||
|
error(HintCode.UNUSED_LOCAL_VARIABLE, 25, 1),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
test_final_becomesDefinitelyAssigned() async {
|
test_final_becomesDefinitelyAssigned() async {
|
||||||
await assertErrorsInCode(r'''
|
await assertErrorsInCode(r'''
|
||||||
void f() {
|
void f() {
|
||||||
|
|
|
@ -847,7 +847,6 @@ PatchInjectionFailed/analyzerCode: Fail
|
||||||
PatchInjectionFailed/example: Fail
|
PatchInjectionFailed/example: Fail
|
||||||
PatchNonExternal/analyzerCode: Fail
|
PatchNonExternal/analyzerCode: Fail
|
||||||
PatchNonExternal/example: Fail
|
PatchNonExternal/example: Fail
|
||||||
PatternAssignmentDeclaresVariable/analyzerCode: Fail
|
|
||||||
PatternMatchingError/analyzerCode: Fail
|
PatternMatchingError/analyzerCode: Fail
|
||||||
PatternMatchingError/example: Fail
|
PatternMatchingError/example: Fail
|
||||||
PlatformPrivateLibraryAccess/example: Fail
|
PlatformPrivateLibraryAccess/example: Fail
|
||||||
|
|
|
@ -6662,6 +6662,8 @@ InvalidConstantPatternBinary:
|
||||||
PatternAssignmentDeclaresVariable:
|
PatternAssignmentDeclaresVariable:
|
||||||
problemMessage: "Variable '#name' can't be declared in a pattern assignment."
|
problemMessage: "Variable '#name' can't be declared in a pattern assignment."
|
||||||
correctionMessage: "Try using a preexisting variable or changing the assignment to a pattern variable declaration."
|
correctionMessage: "Try using a preexisting variable or changing the assignment to a pattern variable declaration."
|
||||||
|
analyzerCode: ParserErrorCode.PATTERN_ASSIGNMENT_DECLARES_VARIABLE
|
||||||
|
index: 145
|
||||||
experiments: patterns
|
experiments: patterns
|
||||||
script: |
|
script: |
|
||||||
method(x) {
|
method(x) {
|
||||||
|
|
Loading…
Reference in a new issue