blob: d9511a17f58dae22807ec4867d36ba456b30661a [file] [log] [blame]
// 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
''');
}
}