| // 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:test_reflective_loader/test_reflective_loader.dart'; | |
| import 'context_collection_resolution.dart'; | |
| import 'node_text_expectations.dart'; | |
| main() { | |
| defineReflectiveSuite(() { | |
| defineReflectiveTests(PatternAssignmentResolutionTest); | |
| defineReflectiveTests(UpdateNodeTextExpectations); | |
| }); | |
| } | |
| @reflectiveTest | |
| class PatternAssignmentResolutionTest extends PubPackageResolutionTest { | |
| test_assignable_final_definitelyAssigned() async { | |
| await resolveTestCodeWithDiagnostics(r''' | |
| void f() { | |
| final int a; | |
| a = 0; | |
| (a) = 1; | |
| // ^ | |
| // [diag.assignmentToFinalLocal] The final variable 'a' can only be set once. | |
| a; | |
| } | |
| '''); | |
| } | |
| test_assignable_final_definitelyUnassigned() async { | |
| await resolveTestCodeWithDiagnostics(r''' | |
| void f() { | |
| final int a; | |
| (a) = 0; | |
| a; | |
| } | |
| '''); | |
| } | |
| test_assignable_final_notDefinitelyUnassigned() async { | |
| await resolveTestCodeWithDiagnostics(r''' | |
| void f(bool flag) { | |
| final int a; | |
| if (flag) { | |
| a = 0; | |
| } | |
| (a) = 1; | |
| // ^ | |
| // [diag.assignmentToFinalLocal] The final variable 'a' can only be set once. | |
| a; | |
| } | |
| '''); | |
| } | |
| test_assignable_lateFinal_definitelyAssigned() async { | |
| await resolveTestCodeWithDiagnostics(r''' | |
| void f() { | |
| late final int a; | |
| a = 0; | |
| (a) = 1; | |
| // ^ | |
| // [diag.lateFinalLocalAlreadyAssigned] The late final local variable is already assigned. | |
| a; | |
| } | |
| '''); | |
| } | |
| test_assignable_lateFinal_definitelyUnassigned() async { | |
| await resolveTestCodeWithDiagnostics(r''' | |
| void f() { | |
| late final int a; | |
| (a) = 1; | |
| a; | |
| } | |
| '''); | |
| } | |
| test_assignable_lateFinal_notDefinitelyAssigned() async { | |
| await resolveTestCodeWithDiagnostics(r''' | |
| void f(bool flag) { | |
| late final int a; | |
| if (flag) { | |
| a = 0; | |
| } | |
| (a) = 1; | |
| a; | |
| } | |
| '''); | |
| } | |
| test_container_listPattern() async { | |
| var result = await resolveTestCodeWithDiagnostics(r''' | |
| void f(List<int> x, num a) { | |
| [a] = x; | |
| } | |
| '''); | |
| var node = result.findNode.singlePatternAssignment; | |
| assertResolvedNodeText(node, r''' | |
| PatternAssignment | |
| pattern: ListPattern | |
| leftBracket: [ | |
| elements | |
| AssignedVariablePattern | |
| name: a | |
| element: <testLibrary>::@function::f::@formalParameter::a | |
| matchedValueType: int | |
| rightBracket: ] | |
| matchedValueType: List<int> | |
| requiredType: List<int> | |
| equals: = | |
| expression2: UnqualifiedNameExpression | |
| name: x | |
| resolution: VariableReadResolution | |
| element: <testLibrary>::@function::f::@formalParameter::x | |
| type: List<int> | |
| staticType: List<int> | |
| expression(v1): SimpleIdentifier | |
| token: x | |
| element: <testLibrary>::@function::f::@formalParameter::x | |
| staticType: List<int> | |
| patternTypeSchema: List<num> | |
| staticType: List<int> | |
| '''); | |
| } | |
| test_container_objectPattern_implicitGetter() async { | |
| var result = await resolveTestCodeWithDiagnostics(r''' | |
| class A { | |
| int get foo => 0; | |
| } | |
| void f(int foo) { | |
| A(:foo) = A(); | |
| } | |
| '''); | |
| var node = result.findNode.singlePatternAssignment; | |
| assertResolvedNodeText(node, r''' | |
| PatternAssignment | |
| pattern: ObjectPattern | |
| type: NamedType | |
| name: A | |
| element: <testLibrary>::@class::A | |
| type: A | |
| leftParenthesis: ( | |
| fields | |
| PatternField | |
| name: PatternFieldName | |
| colon: : | |
| pattern: AssignedVariablePattern | |
| name: foo | |
| element: <testLibrary>::@function::f::@formalParameter::foo | |
| matchedValueType: int | |
| element: <testLibrary>::@class::A::@getter::foo | |
| rightParenthesis: ) | |
| matchedValueType: A | |
| equals: = | |
| expression2: ConstructorInvocation | |
| constructorReference: ConstructorReference2 | |
| typeReference: ConstructorTypeReference | |
| name: A | |
| element: <testLibrary>::@class::A | |
| type: A | |
| element: <testLibrary>::@class::A::@constructor::new | |
| argumentList: ArgumentList | |
| leftParenthesis: ( | |
| rightParenthesis: ) | |
| staticType: A | |
| expression(v1): InstanceCreationExpression | |
| constructorName: ConstructorName | |
| type: NamedType | |
| name: A | |
| element: <testLibrary>::@class::A | |
| type: A | |
| element: <testLibrary>::@class::A::@constructor::new | |
| argumentList: ArgumentList | |
| leftParenthesis: ( | |
| rightParenthesis: ) | |
| staticType: A | |
| patternTypeSchema: A | |
| staticType: A | |
| '''); | |
| } | |
| test_container_parenthesizedPattern() async { | |
| var result = await resolveTestCodeWithDiagnostics(r''' | |
| void f(int x, num a) { | |
| (a) = x; | |
| } | |
| '''); | |
| var node = result.findNode.singlePatternAssignment; | |
| assertResolvedNodeText(node, r''' | |
| PatternAssignment | |
| pattern: ParenthesizedPattern | |
| leftParenthesis: ( | |
| pattern: AssignedVariablePattern | |
| name: a | |
| element: <testLibrary>::@function::f::@formalParameter::a | |
| matchedValueType: int | |
| rightParenthesis: ) | |
| matchedValueType: int | |
| equals: = | |
| expression2: UnqualifiedNameExpression | |
| name: x | |
| resolution: VariableReadResolution | |
| element: <testLibrary>::@function::f::@formalParameter::x | |
| type: int | |
| staticType: int | |
| expression(v1): SimpleIdentifier | |
| token: x | |
| element: <testLibrary>::@function::f::@formalParameter::x | |
| staticType: int | |
| patternTypeSchema: num | |
| staticType: int | |
| '''); | |
| } | |
| test_container_parenthesizedPattern_schema() async { | |
| var result = await resolveTestCodeWithDiagnostics(r''' | |
| void f(int a) { | |
| (a) = g(); | |
| } | |
| T g<T>() => throw 0; | |
| '''); | |
| var node = result.findNode.singlePatternAssignment; | |
| assertResolvedNodeText(node, r''' | |
| PatternAssignment | |
| pattern: ParenthesizedPattern | |
| leftParenthesis: ( | |
| pattern: AssignedVariablePattern | |
| name: a | |
| element: <testLibrary>::@function::f::@formalParameter::a | |
| matchedValueType: int | |
| rightParenthesis: ) | |
| matchedValueType: int | |
| equals: = | |
| expression2: UnqualifiedFunctionInvocation | |
| name: g | |
| argumentList: ArgumentList | |
| leftParenthesis: ( | |
| rightParenthesis: ) | |
| resolution: ExecutableInvocationResolution | |
| element: <testLibrary>::@function::g | |
| invokeType: int Function() | |
| type: int | |
| staticType: int | |
| typeArgumentTypes | |
| int | |
| expression(v1): MethodInvocation | |
| methodName: SimpleIdentifier | |
| token: g | |
| element: <testLibrary>::@function::g | |
| staticType: T Function<T>() | |
| argumentList: ArgumentList | |
| leftParenthesis: ( | |
| rightParenthesis: ) | |
| staticInvokeType: int Function() | |
| staticType: int | |
| typeArgumentTypes | |
| int | |
| patternTypeSchema: int | |
| staticType: int | |
| '''); | |
| } | |
| test_container_recordPattern_named() async { | |
| var result = await resolveTestCodeWithDiagnostics(r''' | |
| void f(({int foo}) x, num a) { | |
| (foo: a,) = x; | |
| } | |
| '''); | |
| var node = result.findNode.singlePatternAssignment; | |
| assertResolvedNodeText(node, r''' | |
| PatternAssignment | |
| pattern: RecordPattern | |
| leftParenthesis: ( | |
| fields | |
| PatternField | |
| name: PatternFieldName | |
| name: foo | |
| colon: : | |
| pattern: AssignedVariablePattern | |
| name: a | |
| element: <testLibrary>::@function::f::@formalParameter::a | |
| matchedValueType: int | |
| element: <null> | |
| rightParenthesis: ) | |
| matchedValueType: ({int foo}) | |
| equals: = | |
| expression2: UnqualifiedNameExpression | |
| name: x | |
| resolution: VariableReadResolution | |
| element: <testLibrary>::@function::f::@formalParameter::x | |
| type: ({int foo}) | |
| staticType: ({int foo}) | |
| expression(v1): SimpleIdentifier | |
| token: x | |
| element: <testLibrary>::@function::f::@formalParameter::x | |
| staticType: ({int foo}) | |
| patternTypeSchema: ({num foo}) | |
| staticType: ({int foo}) | |
| '''); | |
| } | |
| test_container_recordPattern_named_implicit() async { | |
| var result = await resolveTestCodeWithDiagnostics(r''' | |
| void f(int a) { | |
| (:a) = (a: 0); | |
| } | |
| '''); | |
| var node = result.findNode.singlePatternAssignment; | |
| assertResolvedNodeText(node, r''' | |
| PatternAssignment | |
| pattern: RecordPattern | |
| leftParenthesis: ( | |
| fields | |
| PatternField | |
| name: PatternFieldName | |
| colon: : | |
| pattern: AssignedVariablePattern | |
| name: a | |
| element: <testLibrary>::@function::f::@formalParameter::a | |
| matchedValueType: int | |
| element: <null> | |
| rightParenthesis: ) | |
| matchedValueType: ({int a}) | |
| equals: = | |
| expression2: RecordLiteral | |
| leftParenthesis: ( | |
| fields2 | |
| RecordLiteralNamedField | |
| name: a | |
| colon: : | |
| fieldExpression2: IntegerLiteral | |
| literal: 0 | |
| staticType: int | |
| rightParenthesis: ) | |
| staticType: ({int a}) | |
| patternTypeSchema: ({int a}) | |
| staticType: ({int a}) | |
| '''); | |
| } | |
| test_container_recordPattern_positional() async { | |
| var result = await resolveTestCodeWithDiagnostics(r''' | |
| void f((int,) x, num a) { | |
| (a,) = x; | |
| } | |
| '''); | |
| var node = result.findNode.singlePatternAssignment; | |
| assertResolvedNodeText(node, r''' | |
| PatternAssignment | |
| pattern: RecordPattern | |
| leftParenthesis: ( | |
| fields | |
| PatternField | |
| pattern: AssignedVariablePattern | |
| name: a | |
| element: <testLibrary>::@function::f::@formalParameter::a | |
| matchedValueType: int | |
| element: <null> | |
| rightParenthesis: ) | |
| matchedValueType: (int,) | |
| equals: = | |
| expression2: UnqualifiedNameExpression | |
| name: x | |
| resolution: VariableReadResolution | |
| element: <testLibrary>::@function::f::@formalParameter::x | |
| type: (int,) | |
| staticType: (int,) | |
| expression(v1): SimpleIdentifier | |
| token: x | |
| element: <testLibrary>::@function::f::@formalParameter::x | |
| staticType: (int,) | |
| patternTypeSchema: (num,) | |
| staticType: (int,) | |
| '''); | |
| } | |
| test_context_arrowBody_formalParameter() async { | |
| var result = await resolveTestCodeWithDiagnostics(r''' | |
| int f(int x) => (x) = 0; | |
| '''); | |
| var node = result.findNode.singlePatternAssignment; | |
| assertResolvedNodeText(node, r''' | |
| PatternAssignment | |
| pattern: ParenthesizedPattern | |
| leftParenthesis: ( | |
| pattern: AssignedVariablePattern | |
| name: x | |
| element: <testLibrary>::@function::f::@formalParameter::x | |
| matchedValueType: int | |
| rightParenthesis: ) | |
| matchedValueType: int | |
| equals: = | |
| expression2: IntegerLiteral | |
| literal: 0 | |
| staticType: int | |
| patternTypeSchema: int | |
| staticType: int | |
| '''); | |
| } | |
| test_context_returnExpression_formalParameter() async { | |
| var result = await resolveTestCodeWithDiagnostics(r''' | |
| int f(int x) { | |
| return (x) = 0; | |
| } | |
| '''); | |
| var node = result.findNode.singlePatternAssignment; | |
| assertResolvedNodeText(node, r''' | |
| PatternAssignment | |
| pattern: ParenthesizedPattern | |
| leftParenthesis: ( | |
| pattern: AssignedVariablePattern | |
| name: x | |
| element: <testLibrary>::@function::f::@formalParameter::x | |
| matchedValueType: int | |
| rightParenthesis: ) | |
| matchedValueType: int | |
| equals: = | |
| expression2: IntegerLiteral | |
| literal: 0 | |
| staticType: int | |
| patternTypeSchema: int | |
| staticType: int | |
| '''); | |
| } | |
| test_context_variableInitializer_localVariable() async { | |
| var result = await resolveTestCodeWithDiagnostics(r''' | |
| void f() { | |
| int x = 1; | |
| var y = (x) = 0; | |
| x; | |
| y; | |
| } | |
| '''); | |
| var node = result.findNode.singlePatternAssignment; | |
| assertResolvedNodeText(node, r''' | |
| PatternAssignment | |
| pattern: ParenthesizedPattern | |
| leftParenthesis: ( | |
| pattern: AssignedVariablePattern | |
| name: x | |
| element: x@17 | |
| matchedValueType: int | |
| rightParenthesis: ) | |
| matchedValueType: int | |
| equals: = | |
| expression2: IntegerLiteral | |
| literal: 0 | |
| staticType: int | |
| patternTypeSchema: int | |
| staticType: int | |
| '''); | |
| } | |
| 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. | |
| // The reference doesn't resolve so the errors include UNUSED_LOCAL_VARIABLE | |
| // and UNDEFINED_UNQUALIFIED_NAME_READ. | |
| await resolveTestCodeWithDiagnostics(r''' | |
| void f(a, y) { | |
| [a, var d] = y; | |
| // ^ | |
| // [diag.patternAssignmentDeclaresVariable] Variable 'd' can't be declared in a pattern assignment. | |
| // [diag.unusedLocalVariable] The value of the local variable 'd' isn't used. | |
| d; | |
| //^ | |
| // [diag.undefinedUnqualifiedNameReadNotFound] Undefined name 'd'. | |
| } | |
| '''); | |
| } | |
| 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 resolveTestCodeWithDiagnostics(r''' | |
| void f(a, y) { | |
| [a, var d] = y; | |
| // ^ | |
| // [diag.patternAssignmentDeclaresVariable] Variable 'd' can't be declared in a pattern assignment. | |
| // [diag.unusedLocalVariable] The value of the local variable 'd' isn't used. | |
| } | |
| '''); | |
| } | |
| test_final_becomesDefinitelyAssigned() async { | |
| await resolveTestCodeWithDiagnostics(r''' | |
| void f() { | |
| final int a; | |
| (a) = 0; | |
| a; | |
| a = 1; | |
| //^ | |
| // [diag.assignmentToFinalLocal] The final variable 'a' can only be set once. | |
| } | |
| '''); | |
| } | |
| test_promotes() async { | |
| var result = await resolveTestCodeWithDiagnostics(r''' | |
| void f(num a) { | |
| if (a is! int) { | |
| (a) = 0; | |
| } | |
| a; | |
| } | |
| '''); | |
| var node = result.findNode.unqualifiedNameExpression('a;'); | |
| assertResolvedNodeText(node, r''' | |
| UnqualifiedNameExpression | |
| name: a | |
| resolution: VariableReadResolution | |
| element: <testLibrary>::@function::f::@formalParameter::a | |
| type: int | |
| staticType: int | |
| V1: SimpleIdentifier | |
| token: a | |
| element: <testLibrary>::@function::f::@formalParameter::a | |
| staticType: int | |
| '''); | |
| } | |
| } |