Resolve PatternAssignment and AssignedVariablePattern.

Change-Id: I79a7a3b7f5b5fed349c9d40d61a9e0f0fd3978ae
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/274723
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
This commit is contained in:
Konstantin Shcheglov 2022-12-09 22:46:28 +00:00 committed by Commit Queue
parent de24bf1cce
commit 78fb91e739
17 changed files with 505 additions and 5 deletions

View file

@ -893,6 +893,8 @@ CompileTimeErrorCode.PART_OF_NON_PART:
status: needsEvaluation
CompileTimeErrorCode.PART_OF_UNNAMED_LIBRARY:
status: needsEvaluation
CompileTimeErrorCode.PATTERN_ASSIGNMENT_NOT_LOCAL_VARIABLE:
status: needsEvaluation
CompileTimeErrorCode.PATTERN_TYPE_MISMATCH_IN_IRREFUTABLE_CONTEXT:
status: needsEvaluation
CompileTimeErrorCode.POSITIONAL_SUPER_FORMAL_PARAMETER_WITH_POSITIONAL_ARGUMENT:

View file

@ -690,7 +690,11 @@ class AssignedVariablePatternImpl extends DartPatternImpl
@override
DartType computePatternSchema(ResolverVisitor resolverVisitor) {
throw UnimplementedError('TODO(scheglov)');
final element = this.element;
if (element is PromotableElement) {
return resolverVisitor.analyzeAssignedVariablePatternSchema(element);
}
return resolverVisitor.unknownType;
}
@override
@ -699,7 +703,11 @@ class AssignedVariablePatternImpl extends DartPatternImpl
DartType matchedType,
SharedMatchContext context,
) {
throw UnimplementedError('TODO(scheglov)');
resolverVisitor.resolveAssignedVariablePattern(
node: this,
matchedType: matchedType,
context: context,
);
}
@override
@ -10088,8 +10096,7 @@ class PatternAssignmentImpl extends ExpressionImpl
@override
void resolveExpression(ResolverVisitor resolver, DartType? contextType) {
// TODO(brianwilkerson) implement resolveExpression
throw UnimplementedError();
resolver.visitPatternAssignment(this);
}
@override

View file

@ -166,6 +166,29 @@ class ResolutionVisitor extends RecursiveAstVisitor<void> {
});
}
@override
void visitAssignedVariablePattern(
covariant AssignedVariablePatternImpl node,
) {
var name = node.name.lexeme;
var element = _nameScope.lookup(name).getter;
node.element = element;
if (element == null) {
_errorReporter.reportErrorForToken(
CompileTimeErrorCode.UNDEFINED_IDENTIFIER,
node.name,
[name],
);
} else if (!(element is LocalVariableElement ||
element is ParameterElement)) {
_errorReporter.reportErrorForToken(
CompileTimeErrorCode.PATTERN_ASSIGNMENT_NOT_LOCAL_VARIABLE,
node.name,
);
}
}
@override
void visitAugmentationImportDirective(AugmentationImportDirective node) {
final element = node.element;

View file

@ -3681,6 +3681,14 @@ class CompileTimeErrorCode extends AnalyzerErrorCode {
hasPublishedDocs: true,
);
static const CompileTimeErrorCode PATTERN_ASSIGNMENT_NOT_LOCAL_VARIABLE =
CompileTimeErrorCode(
'PATTERN_ASSIGNMENT_NOT_LOCAL_VARIABLE',
"Only local variables or formal parameters can be used in pattern "
"assignments.",
correctionMessage: "Try assigning to a local variable.",
);
/// Parameters:
/// 0: the matched type
/// 1: the required type

View file

@ -374,6 +374,7 @@ const List<ErrorCode> errorCodeValues = [
CompileTimeErrorCode.PART_OF_DIFFERENT_LIBRARY,
CompileTimeErrorCode.PART_OF_NON_PART,
CompileTimeErrorCode.PART_OF_UNNAMED_LIBRARY,
CompileTimeErrorCode.PATTERN_ASSIGNMENT_NOT_LOCAL_VARIABLE,
CompileTimeErrorCode.PATTERN_TYPE_MISMATCH_IN_IRREFUTABLE_CONTEXT,
CompileTimeErrorCode
.POSITIONAL_SUPER_FORMAL_PARAMETER_WITH_POSITIONAL_ARGUMENT,

View file

@ -1334,6 +1334,41 @@ class ResolverVisitor extends ThrowingAstVisitor<void>
NodeReplacer.replace(oldNode, newNode, parent: parent);
}
void resolveAssignedVariablePattern({
required AssignedVariablePatternImpl node,
required DartType matchedType,
required SharedMatchContext context,
}) {
final element = node.element;
if (element is! PromotableElement) {
return;
}
if (element.isFinal) {
final flow = this.flow;
if (flow != null) {
if (element.isLate) {
if (flow.isAssigned(element)) {
errorReporter.reportErrorForToken(
CompileTimeErrorCode.LATE_FINAL_LOCAL_ALREADY_ASSIGNED,
node.name,
);
}
} else {
if (!flow.isUnassigned(element)) {
errorReporter.reportErrorForToken(
CompileTimeErrorCode.ASSIGNMENT_TO_FINAL_LOCAL,
node.name,
[node.name.lexeme],
);
}
}
}
}
analyzeAssignedVariablePattern(matchedType, context, node, element);
}
/// Resolve LHS [node] of an assignment, an explicit [AssignmentExpression],
/// or implicit [PrefixExpression] or [PostfixExpression].
PropertyElementResolverResult resolveForWrite({
@ -2890,6 +2925,15 @@ class ResolverVisitor extends ThrowingAstVisitor<void>
elementResolver.visitPartOfDirective(node);
}
@override
void visitPatternAssignment(covariant PatternAssignmentImpl node) {
checkUnreachableNode(node);
node.staticType =
analyzePatternAssignment(node, node.pattern, node.expression)
.resolveShorting();
popRewrite(); // expression
}
@override
void visitPatternVariableDeclarationStatement(
PatternVariableDeclarationStatement node) {

View file

@ -53,6 +53,17 @@ class FindNode {
return nodes.single;
}
/// Returns the [PatternAssignment], there must be only one.
PatternAssignment get singlePatternAssignment {
var nodes = <PatternAssignment>[];
unit.accept(
FunctionAstVisitor(
patternAssignment: nodes.add,
),
);
return nodes.single;
}
/// Returns the [PatternVariableDeclaration], there must be only one.
PatternVariableDeclaration get singlePatternVariableDeclaration {
var nodes = <PatternVariableDeclaration>[];

View file

@ -16,6 +16,7 @@ class FunctionAstVisitor extends RecursiveAstVisitor<void> {
final void Function(IfStatement)? ifStatement;
final void Function(Label)? label;
final void Function(MethodInvocation)? methodInvocation;
final void Function(PatternAssignment)? patternAssignment;
final void Function(PatternVariableDeclaration)? patternVariableDeclaration;
final void Function(PatternVariableDeclarationStatement)?
patternVariableDeclarationStatement;
@ -33,6 +34,7 @@ class FunctionAstVisitor extends RecursiveAstVisitor<void> {
this.ifStatement,
this.label,
this.methodInvocation,
this.patternAssignment,
this.patternVariableDeclaration,
this.patternVariableDeclarationStatement,
this.simpleIdentifier,
@ -101,6 +103,12 @@ class FunctionAstVisitor extends RecursiveAstVisitor<void> {
super.visitMethodInvocation(node);
}
@override
void visitPatternAssignment(PatternAssignment node) {
patternAssignment?.call(node);
super.visitPatternAssignment(node);
}
@override
void visitPatternVariableDeclaration(PatternVariableDeclaration node) {
patternVariableDeclaration?.call(node);

View file

@ -11170,6 +11170,9 @@ CompileTimeErrorCode:
%uri="lib/part_file.dart"
part of 'test.dart';
```
PATTERN_ASSIGNMENT_NOT_LOCAL_VARIABLE:
problemMessage: Only local variables or formal parameters can be used in pattern assignments.
correctionMessage: Try assigning to a local variable.
PATTERN_TYPE_MISMATCH_IN_IRREFUTABLE_CONTEXT:
problemMessage: "The matched value of type '{0}' isn't assignable to the required type '{1}'."
correctionMessage: "Try changing the required type of the pattern, or the matched value type."

View file

@ -0,0 +1,263 @@
// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
// 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.
import 'package:analyzer/src/error/codes.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
import 'context_collection_resolution.dart';
main() {
defineReflectiveSuite(() {
defineReflectiveTests(PatternAssignmentResolutionTest);
});
}
@reflectiveTest
class PatternAssignmentResolutionTest extends PubPackageResolutionTest {
test_assignable_final_definitelyAssigned() async {
await assertErrorsInCode(r'''
void f() {
final int a;
a = 0;
(a) = 1;
a;
}
''', [
error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL_LOCAL, 38, 1),
]);
}
test_assignable_final_definitelyUnassigned() async {
await assertNoErrorsInCode(r'''
void f() {
final int a;
(a) = 0;
a;
}
''');
}
test_assignable_final_notDefinitelyUnassigned() async {
await assertErrorsInCode(r'''
void f(bool flag) {
final int a;
if (flag) {
a = 0;
}
(a) = 1;
a;
}
''', [
error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL_LOCAL, 67, 1),
]);
}
test_assignable_lateFinal_definitelyAssigned() async {
await assertErrorsInCode(r'''
void f() {
late final int a;
a = 0;
(a) = 1;
a;
}
''', [
error(CompileTimeErrorCode.LATE_FINAL_LOCAL_ALREADY_ASSIGNED, 43, 1),
]);
}
test_assignable_lateFinal_definitelyUnassigned() async {
await assertNoErrorsInCode(r'''
void f() {
late final int a;
(a) = 1;
a;
}
''');
}
test_assignable_lateFinal_notDefinitelyAssigned() async {
await assertNoErrorsInCode(r'''
void f(bool flag) {
late final int a;
if (flag) {
a = 0;
}
(a) = 1;
a;
}
''');
}
test_container_listPattern() async {
await assertNoErrorsInCode(r'''
void f(List<int> x, num a) {
[a] = x;
}
''');
final node = findNode.singlePatternAssignment;
assertResolvedNodeText(node, r'''
PatternAssignment
pattern: ListPattern
leftBracket: [
elements
AssignedVariablePattern
name: a
element: self::@function::f::@parameter::a
rightBracket: ]
requiredType: List<int>
equals: =
expression: SimpleIdentifier
token: x
staticElement: self::@function::f::@parameter::x
staticType: List<int>
staticType: List<int>
''');
}
test_container_parenthesizedPattern() async {
await assertNoErrorsInCode(r'''
void f(int x, num a) {
(a) = x;
}
''');
final node = findNode.singlePatternAssignment;
assertResolvedNodeText(node, r'''
PatternAssignment
pattern: ParenthesizedPattern
leftParenthesis: (
pattern: AssignedVariablePattern
name: a
element: self::@function::f::@parameter::a
rightParenthesis: )
equals: =
expression: SimpleIdentifier
token: x
staticElement: self::@function::f::@parameter::x
staticType: int
staticType: int
''');
}
test_container_parenthesizedPattern_schema() async {
await assertNoErrorsInCode(r'''
void f(int a) {
(a) = g();
}
T g<T>() => throw 0;
''');
final node = findNode.singlePatternAssignment;
assertResolvedNodeText(node, r'''
PatternAssignment
pattern: ParenthesizedPattern
leftParenthesis: (
pattern: AssignedVariablePattern
name: a
element: self::@function::f::@parameter::a
rightParenthesis: )
equals: =
expression: MethodInvocation
methodName: SimpleIdentifier
token: g
staticElement: self::@function::g
staticType: T Function<T>()
argumentList: ArgumentList
leftParenthesis: (
rightParenthesis: )
staticInvokeType: int Function()
staticType: int
typeArgumentTypes
int
staticType: int
''');
}
test_container_recordPattern_named() async {
await assertNoErrorsInCode(r'''
void f(({int foo}) x, num a) {
(foo: a,) = x;
}
''');
final node = findNode.singlePatternAssignment;
assertResolvedNodeText(node, r'''
PatternAssignment
pattern: RecordPattern
leftParenthesis: (
fields
RecordPatternField
fieldName: RecordPatternFieldName
name: foo
colon: :
pattern: AssignedVariablePattern
name: a
element: self::@function::f::@parameter::a
fieldElement: <null>
rightParenthesis: )
equals: =
expression: SimpleIdentifier
token: x
staticElement: self::@function::f::@parameter::x
staticType: ({int foo})
staticType: ({int foo})
''');
}
test_container_recordPattern_positional() async {
await assertNoErrorsInCode(r'''
void f((int,) x, num a) {
(a,) = x;
}
''');
final node = findNode.singlePatternAssignment;
assertResolvedNodeText(node, r'''
PatternAssignment
pattern: RecordPattern
leftParenthesis: (
fields
RecordPatternField
pattern: AssignedVariablePattern
name: a
element: self::@function::f::@parameter::a
fieldElement: <null>
rightParenthesis: )
equals: =
expression: SimpleIdentifier
token: x
staticElement: self::@function::f::@parameter::x
staticType: (int)
staticType: (int)
''');
}
test_final_becomesDefinitelyAssigned() async {
await assertErrorsInCode(r'''
void f() {
final int a;
(a) = 0;
a;
a = 1;
}
''', [
error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL_LOCAL, 44, 1),
]);
}
test_promotes() async {
await assertNoErrorsInCode(r'''
void f(num a) {
if (a is! int) {
(a) = 0;
}
a;
}
''');
final node = findNode.simple('a;');
assertResolvedNodeText(node, r'''
SimpleIdentifier
token: a
staticElement: self::@function::f::@parameter::a
staticType: int
''');
}
}

View file

@ -67,6 +67,7 @@ import 'object_pattern_test.dart' as object_pattern;
import 'optional_const_test.dart' as optional_const;
import 'parenthesized_pattern_test.dart' as parenthesized_pattern;
import 'part_test.dart' as part_;
import 'pattern_assignment_test.dart' as pattern_assignment;
import 'pattern_variable_declaration_statement_test.dart'
as pattern_variable_declaration_statement;
import 'postfix_expression_test.dart' as postfix_expression;
@ -153,6 +154,7 @@ main() {
optional_const.main();
parenthesized_pattern.main();
part_.main();
pattern_assignment.main();
pattern_variable_declaration_statement.main();
postfix_expression.main();
postfix_pattern.main();

View file

@ -17,7 +17,17 @@ main() {
@reflectiveTest
class DeadCodeTest extends PubPackageResolutionTest
with DeadCodeTestCases, DeadCodeTestCases_Language212 {}
with DeadCodeTestCases, DeadCodeTestCases_Language212 {
test_ifElement_patternAssignment() async {
await assertErrorsInCode(r'''
void f(int a) {
[if (false) (a) = 0];
}
''', [
error(HintCode.DEAD_CODE, 30, 7),
]);
}
}
@reflectiveTest
class DeadCodeTest_Language218 extends PubPackageResolutionTest

View file

@ -0,0 +1,85 @@
// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
// 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.
import 'package:analyzer/src/error/codes.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
import '../dart/resolution/context_collection_resolution.dart';
main() {
defineReflectiveSuite(() {
defineReflectiveTests(PatternAssignmentNotLocalVariableTest);
});
}
@reflectiveTest
class PatternAssignmentNotLocalVariableTest extends PubPackageResolutionTest {
test_class() async {
await assertErrorsInCode('''
void f() {
(int) = 0;
}
''', [
error(CompileTimeErrorCode.PATTERN_ASSIGNMENT_NOT_LOCAL_VARIABLE, 14, 3),
]);
}
test_class_field() async {
await assertErrorsInCode('''
class A {
var x = 0;
void f() {
(x) = 0;
}
}
''', [
error(CompileTimeErrorCode.PATTERN_ASSIGNMENT_NOT_LOCAL_VARIABLE, 42, 1),
]);
}
test_class_typeParameter() async {
await assertErrorsInCode('''
class A<T> {
void f() {
(T) = 0;
}
}
''', [
error(CompileTimeErrorCode.PATTERN_ASSIGNMENT_NOT_LOCAL_VARIABLE, 31, 1),
]);
}
test_dynamic() async {
await assertErrorsInCode('''
void f() {
(dynamic) = 0;
}
''', [
error(CompileTimeErrorCode.PATTERN_ASSIGNMENT_NOT_LOCAL_VARIABLE, 14, 7),
]);
}
test_function() async {
await assertErrorsInCode('''
void f() {
(f) = 0;
}
''', [
error(CompileTimeErrorCode.PATTERN_ASSIGNMENT_NOT_LOCAL_VARIABLE, 14, 1),
]);
}
test_topLevelVariable() async {
await assertErrorsInCode('''
var x = 0;
void f() {
(x) = 0;
}
''', [
error(CompileTimeErrorCode.PATTERN_ASSIGNMENT_NOT_LOCAL_VARIABLE, 26, 1),
]);
}
}

View file

@ -60,6 +60,17 @@ void f(Object x) {
]);
}
test_patternAssignment_assignedVariablePattern() async {
await assertErrorsInCode(r'''
void f(int a) {
(a) = 1.2;
}
''', [
error(CompileTimeErrorCode.PATTERN_TYPE_MISMATCH_IN_IRREFUTABLE_CONTEXT,
19, 1),
]);
}
test_recordPattern_notRecord() async {
await assertErrorsInCode(r'''
void f(Object x) {

View file

@ -607,6 +607,8 @@ import 'packed_annotation_alignment_test.dart' as packed_annotation_alignment;
import 'packed_annotation_test.dart' as packed_annotation;
import 'part_of_different_library_test.dart' as part_of_different_library;
import 'part_of_non_part_test.dart' as part_of_non_part;
import 'pattern_assignment_not_local_variable_test.dart'
as pattern_assignment_not_local_variable;
import 'pattern_type_mismatch_in_irrefutable_context_test.dart'
as pattern_type_mismatch_in_irrefutable_context;
import 'positional_super_formal_parameter_with_positional_argument_test.dart'
@ -1224,6 +1226,7 @@ main() {
packed_annotation_alignment.main();
part_of_different_library.main();
part_of_non_part.main();
pattern_assignment_not_local_variable.main();
pattern_type_mismatch_in_irrefutable_context.main();
positional_super_formal_parameter_with_positional_argument.main();
prefix_collides_with_top_level_member.main();

View file

@ -18,6 +18,16 @@ main() {
@reflectiveTest
class UndefinedIdentifierTest extends PubPackageResolutionTest
with UndefinedIdentifierTestCases {
test_assignedPatternVariable() async {
await assertErrorsInCode('''
void f() {
(x) = 0;
}
''', [
error(CompileTimeErrorCode.UNDEFINED_IDENTIFIER, 14, 1),
]);
}
test_get_from_external_variable_final_valid() async {
await assertNoErrorsInCode('''
external final int x;

View file

@ -1070,6 +1070,15 @@ class ResolvedAstPrinter extends ThrowingAstVisitor<void> {
});
}
@override
void visitPatternAssignment(PatternAssignment node) {
_writeln('PatternAssignment');
_withIndent(() {
_writeNamedChildEntities(node);
_writeType('staticType', node.staticType);
});
}
@override
void visitPatternVariableDeclaration(PatternVariableDeclaration node) {
_writeln('PatternVariableDeclaration');