| // 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'; |
| import 'node_text_expectations.dart'; |
| |
| main() { |
| defineReflectiveSuite(() { |
| defineReflectiveTests(SwitchExpressionResolutionTest); |
| defineReflectiveTests(UpdateNodeTextExpectations); |
| }); |
| } |
| |
| @reflectiveTest |
| class SwitchExpressionResolutionTest extends PubPackageResolutionTest { |
| test_case_expression_void() async { |
| await assertNoErrorsInCode(r''' |
| void f(Object? x) { |
| (switch(x) { |
| 0 => 0, |
| _ => g(), |
| }); |
| } |
| |
| void g() {} |
| '''); |
| |
| var node = findNode.singleSwitchExpression; |
| assertResolvedNodeText(node, r''' |
| SwitchExpression |
| switchKeyword: switch |
| leftParenthesis: ( |
| expression: SimpleIdentifier |
| token: x |
| element: <testLibraryFragment>::@function::f::@parameter::x#element |
| staticType: Object? |
| rightParenthesis: ) |
| leftBracket: { |
| cases |
| SwitchExpressionCase |
| guardedPattern: GuardedPattern |
| pattern: ConstantPattern |
| expression: IntegerLiteral |
| literal: 0 |
| staticType: int |
| matchedValueType: Object? |
| arrow: => |
| expression: IntegerLiteral |
| literal: 0 |
| staticType: int |
| SwitchExpressionCase |
| guardedPattern: GuardedPattern |
| pattern: WildcardPattern |
| name: _ |
| matchedValueType: Object? |
| arrow: => |
| expression: MethodInvocation |
| methodName: SimpleIdentifier |
| token: g |
| element: <testLibrary>::@function::g |
| staticType: void Function() |
| argumentList: ArgumentList |
| leftParenthesis: ( |
| rightParenthesis: ) |
| staticInvokeType: void Function() |
| staticType: void |
| rightBracket: } |
| staticType: void |
| '''); |
| } |
| |
| test_cases_empty() async { |
| await assertErrorsInCode( |
| r''' |
| final a = switch (0) {}; |
| ''', |
| [error(CompileTimeErrorCode.NON_EXHAUSTIVE_SWITCH_EXPRESSION, 10, 6)], |
| ); |
| |
| var node = findNode.singleSwitchExpression; |
| assertResolvedNodeText(node, r''' |
| SwitchExpression |
| switchKeyword: switch |
| leftParenthesis: ( |
| expression: IntegerLiteral |
| literal: 0 |
| staticType: int |
| rightParenthesis: ) |
| leftBracket: { |
| rightBracket: } |
| staticType: Never |
| '''); |
| } |
| |
| test_contextType_case_expression() async { |
| await assertNoErrorsInCode(r''' |
| class A { |
| T foo<T>() => throw 0; |
| |
| int bar(Object? x) { |
| return switch (x) { |
| _ => foo(), |
| }; |
| } |
| } |
| '''); |
| |
| var node = findNode.switchExpression('switch'); |
| assertResolvedNodeText(node, r''' |
| SwitchExpression |
| switchKeyword: switch |
| leftParenthesis: ( |
| expression: SimpleIdentifier |
| token: x |
| element: <testLibraryFragment>::@class::A::@method::bar::@parameter::x#element |
| staticType: Object? |
| rightParenthesis: ) |
| leftBracket: { |
| cases |
| SwitchExpressionCase |
| guardedPattern: GuardedPattern |
| pattern: WildcardPattern |
| name: _ |
| matchedValueType: Object? |
| arrow: => |
| expression: MethodInvocation |
| methodName: SimpleIdentifier |
| token: foo |
| element: <testLibraryFragment>::@class::A::@method::foo#element |
| staticType: T Function<T>() |
| argumentList: ArgumentList |
| leftParenthesis: ( |
| rightParenthesis: ) |
| staticInvokeType: int Function() |
| staticType: int |
| typeArgumentTypes |
| int |
| rightBracket: } |
| staticType: int |
| '''); |
| } |
| |
| test_expression_void() async { |
| await assertErrorsInCode( |
| ''' |
| void f(void x) { |
| (switch(x) { |
| _ => 0, |
| }); |
| } |
| ''', |
| [error(CompileTimeErrorCode.USE_OF_VOID_RESULT, 27, 1)], |
| ); |
| |
| var node = findNode.singleSwitchExpression; |
| assertResolvedNodeText(node, r''' |
| SwitchExpression |
| switchKeyword: switch |
| leftParenthesis: ( |
| expression: SimpleIdentifier |
| token: x |
| element: <testLibraryFragment>::@function::f::@parameter::x#element |
| staticType: void |
| rightParenthesis: ) |
| leftBracket: { |
| cases |
| SwitchExpressionCase |
| guardedPattern: GuardedPattern |
| pattern: WildcardPattern |
| name: _ |
| matchedValueType: void |
| arrow: => |
| expression: IntegerLiteral |
| literal: 0 |
| staticType: int |
| rightBracket: } |
| staticType: int |
| '''); |
| } |
| |
| test_joinedVariables_inLocalFunction() async { |
| // Note: this is an important case to test because when variables are inside |
| // a local function, their enclosing element is `null`. |
| await assertNoErrorsInCode(''' |
| abstract class C { |
| List<int> get values; |
| } |
| abstract class D { |
| List<int> get values; |
| } |
| test(Object o) => () => |
| switch (o) { |
| C(:var values) || D(:var values) => |
| [for (var value in values) value + 1], |
| _ => [] |
| }; |
| '''); |
| |
| var node = findNode.simple('value + 1'); |
| assertResolvedNodeText(node, r''' |
| SimpleIdentifier |
| token: value |
| element: value@185 |
| staticType: int |
| '''); |
| } |
| |
| test_location_topLevel() async { |
| await assertNoErrorsInCode(r''' |
| num a = 0; |
| |
| final b = switch (a) { |
| int(:var isEven) when isEven => 1, |
| _ => 0, |
| }; |
| '''); |
| |
| var node = findNode.singleSwitchExpression; |
| assertResolvedNodeText(node, r''' |
| SwitchExpression |
| switchKeyword: switch |
| leftParenthesis: ( |
| expression: SimpleIdentifier |
| token: a |
| element: <testLibraryFragment>::@getter::a#element |
| staticType: num |
| rightParenthesis: ) |
| leftBracket: { |
| cases |
| SwitchExpressionCase |
| guardedPattern: GuardedPattern |
| pattern: ObjectPattern |
| type: NamedType |
| name: int |
| element2: dart:core::@class::int |
| type: int |
| leftParenthesis: ( |
| fields |
| PatternField |
| name: PatternFieldName |
| colon: : |
| pattern: DeclaredVariablePattern |
| keyword: var |
| name: isEven |
| declaredElement: hasImplicitType isEven@46 |
| type: bool |
| matchedValueType: bool |
| element2: dart:core::<fragment>::@class::int::@getter::isEven#element |
| rightParenthesis: ) |
| matchedValueType: num |
| whenClause: WhenClause |
| whenKeyword: when |
| expression: SimpleIdentifier |
| token: isEven |
| element: isEven@46 |
| staticType: bool |
| arrow: => |
| expression: IntegerLiteral |
| literal: 1 |
| staticType: int |
| SwitchExpressionCase |
| guardedPattern: GuardedPattern |
| pattern: WildcardPattern |
| name: _ |
| matchedValueType: num |
| arrow: => |
| expression: IntegerLiteral |
| literal: 0 |
| staticType: int |
| rightBracket: } |
| staticType: int |
| '''); |
| } |
| |
| test_rewrite_case_expression() async { |
| await assertNoErrorsInCode(r''' |
| void f(Object? x, int Function() a) { |
| (switch (x) { |
| _ => a(), |
| }); |
| } |
| '''); |
| |
| var node = findNode.switchExpressionCase('_'); |
| assertResolvedNodeText(node, r''' |
| SwitchExpressionCase |
| guardedPattern: GuardedPattern |
| pattern: WildcardPattern |
| name: _ |
| matchedValueType: Object? |
| arrow: => |
| expression: FunctionExpressionInvocation |
| function: SimpleIdentifier |
| token: a |
| element: <testLibraryFragment>::@function::f::@parameter::a#element |
| staticType: int Function() |
| argumentList: ArgumentList |
| leftParenthesis: ( |
| rightParenthesis: ) |
| element: <null> |
| staticInvokeType: int Function() |
| staticType: int |
| '''); |
| } |
| |
| test_rewrite_case_pattern() async { |
| await assertNoErrorsInCode(r''' |
| void f(Object? x) { |
| (switch (x) { |
| const A() => 0, |
| _ => 1, |
| }); |
| } |
| |
| class A { |
| const A(); |
| } |
| '''); |
| |
| var node = findNode.switchExpressionCase('=> 0'); |
| assertResolvedNodeText(node, r''' |
| SwitchExpressionCase |
| guardedPattern: GuardedPattern |
| pattern: ConstantPattern |
| const: const |
| expression: InstanceCreationExpression |
| constructorName: ConstructorName |
| type: NamedType |
| name: A |
| element2: <testLibrary>::@class::A |
| type: A |
| element: <testLibraryFragment>::@class::A::@constructor::new#element |
| argumentList: ArgumentList |
| leftParenthesis: ( |
| rightParenthesis: ) |
| staticType: A |
| matchedValueType: Object? |
| arrow: => |
| expression: IntegerLiteral |
| literal: 0 |
| staticType: int |
| '''); |
| } |
| |
| test_rewrite_case_whenClause() async { |
| await assertNoErrorsInCode(r''' |
| void f(Object? x, bool Function() a) { |
| (switch (x) { |
| 0 when a() => true, |
| _ => false, |
| }); |
| } |
| '''); |
| |
| var node = findNode.switchExpressionCase('=> true'); |
| assertResolvedNodeText(node, r''' |
| SwitchExpressionCase |
| guardedPattern: GuardedPattern |
| pattern: ConstantPattern |
| expression: IntegerLiteral |
| literal: 0 |
| staticType: int |
| matchedValueType: Object? |
| whenClause: WhenClause |
| whenKeyword: when |
| expression: FunctionExpressionInvocation |
| function: SimpleIdentifier |
| token: a |
| element: <testLibraryFragment>::@function::f::@parameter::a#element |
| staticType: bool Function() |
| argumentList: ArgumentList |
| leftParenthesis: ( |
| rightParenthesis: ) |
| element: <null> |
| staticInvokeType: bool Function() |
| staticType: bool |
| arrow: => |
| expression: BooleanLiteral |
| literal: true |
| staticType: bool |
| '''); |
| } |
| |
| test_rewrite_expression() async { |
| await assertNoErrorsInCode(r''' |
| void f(int Function() a) { |
| (switch (a()) { |
| _ => 0, |
| }); |
| } |
| '''); |
| |
| var node = findNode.switchExpression('switch'); |
| assertResolvedNodeText(node, r''' |
| SwitchExpression |
| switchKeyword: switch |
| leftParenthesis: ( |
| expression: FunctionExpressionInvocation |
| function: SimpleIdentifier |
| token: a |
| element: <testLibraryFragment>::@function::f::@parameter::a#element |
| staticType: int Function() |
| argumentList: ArgumentList |
| leftParenthesis: ( |
| rightParenthesis: ) |
| element: <null> |
| staticInvokeType: int Function() |
| staticType: int |
| rightParenthesis: ) |
| leftBracket: { |
| cases |
| SwitchExpressionCase |
| guardedPattern: GuardedPattern |
| pattern: WildcardPattern |
| name: _ |
| matchedValueType: int |
| arrow: => |
| expression: IntegerLiteral |
| literal: 0 |
| staticType: int |
| rightBracket: } |
| staticType: int |
| '''); |
| } |
| |
| test_staticType_cases_leastUpperBound() async { |
| await assertNoErrorsInCode(r''' |
| void f(Object? x) { |
| (switch (x) { |
| true => 0, |
| _ => null, |
| }); |
| } |
| '''); |
| |
| var node = findNode.switchExpression('switch'); |
| assertResolvedNodeText(node, r''' |
| SwitchExpression |
| switchKeyword: switch |
| leftParenthesis: ( |
| expression: SimpleIdentifier |
| token: x |
| element: <testLibraryFragment>::@function::f::@parameter::x#element |
| staticType: Object? |
| rightParenthesis: ) |
| leftBracket: { |
| cases |
| SwitchExpressionCase |
| guardedPattern: GuardedPattern |
| pattern: ConstantPattern |
| expression: BooleanLiteral |
| literal: true |
| staticType: bool |
| matchedValueType: Object? |
| arrow: => |
| expression: IntegerLiteral |
| literal: 0 |
| staticType: int |
| SwitchExpressionCase |
| guardedPattern: GuardedPattern |
| pattern: WildcardPattern |
| name: _ |
| matchedValueType: Object? |
| arrow: => |
| expression: NullLiteral |
| literal: null |
| staticType: Null |
| rightBracket: } |
| staticType: int? |
| '''); |
| } |
| |
| test_staticType_cases_same() async { |
| await assertNoErrorsInCode(r''' |
| void f(Object? x) { |
| (switch (x) { |
| true => 0, |
| _ => 1, |
| }); |
| } |
| '''); |
| |
| var node = findNode.switchExpression('switch'); |
| assertResolvedNodeText(node, r''' |
| SwitchExpression |
| switchKeyword: switch |
| leftParenthesis: ( |
| expression: SimpleIdentifier |
| token: x |
| element: <testLibraryFragment>::@function::f::@parameter::x#element |
| staticType: Object? |
| rightParenthesis: ) |
| leftBracket: { |
| cases |
| SwitchExpressionCase |
| guardedPattern: GuardedPattern |
| pattern: ConstantPattern |
| expression: BooleanLiteral |
| literal: true |
| staticType: bool |
| matchedValueType: Object? |
| arrow: => |
| expression: IntegerLiteral |
| literal: 0 |
| staticType: int |
| SwitchExpressionCase |
| guardedPattern: GuardedPattern |
| pattern: WildcardPattern |
| name: _ |
| matchedValueType: Object? |
| arrow: => |
| expression: IntegerLiteral |
| literal: 1 |
| staticType: int |
| rightBracket: } |
| staticType: int |
| '''); |
| } |
| |
| test_variables_logicalOr() async { |
| await assertErrorsInCode( |
| r''' |
| void f(Object? x) { |
| (switch (x) { |
| <int>[var a || var a] => a, |
| _ => 0, |
| }); |
| } |
| ''', |
| [error(WarningCode.DEAD_CODE, 52, 8)], |
| ); |
| |
| var node = findNode.switchExpression('switch'); |
| assertResolvedNodeText(node, r''' |
| SwitchExpression |
| switchKeyword: switch |
| leftParenthesis: ( |
| expression: SimpleIdentifier |
| token: x |
| element: <testLibraryFragment>::@function::f::@parameter::x#element |
| staticType: Object? |
| rightParenthesis: ) |
| leftBracket: { |
| cases |
| SwitchExpressionCase |
| guardedPattern: GuardedPattern |
| pattern: ListPattern |
| typeArguments: TypeArgumentList |
| leftBracket: < |
| arguments |
| NamedType |
| name: int |
| element2: dart:core::@class::int |
| type: int |
| rightBracket: > |
| leftBracket: [ |
| elements |
| LogicalOrPattern |
| leftOperand: DeclaredVariablePattern |
| keyword: var |
| name: a |
| declaredElement: hasImplicitType a@50 |
| type: int |
| matchedValueType: int |
| operator: || |
| rightOperand: DeclaredVariablePattern |
| keyword: var |
| name: a |
| declaredElement: hasImplicitType a@59 |
| type: int |
| matchedValueType: int |
| matchedValueType: int |
| rightBracket: ] |
| matchedValueType: Object? |
| requiredType: List<int> |
| arrow: => |
| expression: SimpleIdentifier |
| token: a |
| element: a@-1 |
| staticType: int |
| SwitchExpressionCase |
| guardedPattern: GuardedPattern |
| pattern: WildcardPattern |
| name: _ |
| matchedValueType: Object? |
| arrow: => |
| expression: IntegerLiteral |
| literal: 0 |
| staticType: int |
| rightBracket: } |
| staticType: int |
| '''); |
| } |
| |
| test_variables_scope() async { |
| await assertErrorsInCode( |
| r''' |
| const a = 0; |
| void f(Object? x) { |
| (switch (x) { |
| [int a, == a] when a > 0 => a, |
| _ => 0, |
| }); |
| } |
| ''', |
| [ |
| error( |
| CompileTimeErrorCode.NON_CONSTANT_RELATIONAL_PATTERN_EXPRESSION, |
| 64, |
| 1, |
| ), |
| error( |
| CompileTimeErrorCode.REFERENCED_BEFORE_DECLARATION, |
| 64, |
| 1, |
| contextMessages: [message(testFile, 58, 1)], |
| ), |
| ], |
| ); |
| |
| var node = findNode.switchExpression('switch'); |
| assertResolvedNodeText(node, r''' |
| SwitchExpression |
| switchKeyword: switch |
| leftParenthesis: ( |
| expression: SimpleIdentifier |
| token: x |
| element: <testLibraryFragment>::@function::f::@parameter::x#element |
| staticType: Object? |
| rightParenthesis: ) |
| leftBracket: { |
| cases |
| SwitchExpressionCase |
| guardedPattern: GuardedPattern |
| pattern: ListPattern |
| leftBracket: [ |
| elements |
| DeclaredVariablePattern |
| type: NamedType |
| name: int |
| element2: dart:core::@class::int |
| type: int |
| name: a |
| declaredElement: a@58 |
| type: int |
| matchedValueType: Object? |
| RelationalPattern |
| operator: == |
| operand: SimpleIdentifier |
| token: a |
| element: a@58 |
| staticType: int |
| element2: dart:core::<fragment>::@class::Object::@method::==#element |
| matchedValueType: Object? |
| rightBracket: ] |
| matchedValueType: Object? |
| requiredType: List<Object?> |
| whenClause: WhenClause |
| whenKeyword: when |
| expression: BinaryExpression |
| leftOperand: SimpleIdentifier |
| token: a |
| element: a@58 |
| staticType: int |
| operator: > |
| rightOperand: IntegerLiteral |
| literal: 0 |
| correspondingParameter: dart:core::<fragment>::@class::num::@method::>::@parameter::other#element |
| staticType: int |
| element: dart:core::<fragment>::@class::num::@method::>#element |
| staticInvokeType: bool Function(num) |
| staticType: bool |
| arrow: => |
| expression: SimpleIdentifier |
| token: a |
| element: a@58 |
| staticType: int |
| SwitchExpressionCase |
| guardedPattern: GuardedPattern |
| pattern: WildcardPattern |
| name: _ |
| matchedValueType: Object? |
| arrow: => |
| expression: IntegerLiteral |
| literal: 0 |
| staticType: int |
| rightBracket: } |
| staticType: int |
| '''); |
| } |
| |
| test_variables_singleCase() async { |
| await assertErrorsInCode( |
| r''' |
| void f(Object? x) { |
| (switch (x) { |
| int a when a > 0 => a, |
| _ => a, |
| }); |
| } |
| ''', |
| [error(CompileTimeErrorCode.UNDEFINED_IDENTIFIER, 72, 1)], |
| ); |
| |
| var node = findNode.switchExpression('switch'); |
| assertResolvedNodeText(node, r''' |
| SwitchExpression |
| switchKeyword: switch |
| leftParenthesis: ( |
| expression: SimpleIdentifier |
| token: x |
| element: <testLibraryFragment>::@function::f::@parameter::x#element |
| staticType: Object? |
| rightParenthesis: ) |
| leftBracket: { |
| cases |
| SwitchExpressionCase |
| guardedPattern: GuardedPattern |
| pattern: DeclaredVariablePattern |
| type: NamedType |
| name: int |
| element2: dart:core::@class::int |
| type: int |
| name: a |
| declaredElement: a@44 |
| type: int |
| matchedValueType: Object? |
| whenClause: WhenClause |
| whenKeyword: when |
| expression: BinaryExpression |
| leftOperand: SimpleIdentifier |
| token: a |
| element: a@44 |
| staticType: int |
| operator: > |
| rightOperand: IntegerLiteral |
| literal: 0 |
| correspondingParameter: dart:core::<fragment>::@class::num::@method::>::@parameter::other#element |
| staticType: int |
| element: dart:core::<fragment>::@class::num::@method::>#element |
| staticInvokeType: bool Function(num) |
| staticType: bool |
| arrow: => |
| expression: SimpleIdentifier |
| token: a |
| element: a@44 |
| staticType: int |
| SwitchExpressionCase |
| guardedPattern: GuardedPattern |
| pattern: WildcardPattern |
| name: _ |
| matchedValueType: Object? |
| arrow: => |
| expression: SimpleIdentifier |
| token: a |
| element: <null> |
| staticType: InvalidType |
| rightBracket: } |
| staticType: InvalidType |
| '''); |
| } |
| } |