blob: ca7221f200c3e34f3d8b3614436698d19b573d74 [file] [edit]
// Copyright (c) 2026, 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:code_builder/code_builder.dart';
import 'package:test/test.dart';
import '../common.dart';
void main() {
useDartfmt();
group('Logical Patterns', () {
test('logical-or pattern', () {
expect(
SwitchExpression(
(b) => b
..value = refer('color')
..cases.addAll([
.new(
(b) => b
..pattern = .refer(
'Color.red',
).or(.refer('Color.yellow')).or(.refer('Color.blue'))
..body = literalTrue,
),
.new(
(b) => b
..pattern = .wildcard
..body = literalFalse,
),
]),
),
equalsDart(r'''
switch (color) {
Color.red || Color.yellow || Color.blue => true,
_ => false,
}
'''),
);
});
test('logical-and pattern', () {
expect(
SwitchExpression(
(b) => b
..value = refer('x')
..cases.addAll([
.new(
(b) => b
..pattern = .greaterThan(
literal(0),
).and(.lessThan(literal(10)))
..body = literal('single-digit'),
),
.new(
(b) => b
..pattern = .wildcard
..body = literal('other'),
),
]),
),
equalsDart(r'''
switch (x) {
> 0 && < 10 => 'single-digit',
_ => 'other',
}
'''),
);
});
});
group('Relational Patterns', () {
test('relational operators', () {
expect(
SwitchExpression(
(b) => b
..value = refer('val')
..cases.addAll([
.new(
(b) => b
..pattern = .equalTo(literal(0))
..body = literal('zero'),
),
.new(
(b) => b
..pattern = .notEqualTo(literal(1))
..body = literal('not-one'),
),
.new(
(b) => b
..pattern = .greaterThan(literal(10))
..body = literal('gt-10'),
),
.new(
(b) => b
..pattern = .greaterOrEqualTo(literal(5))
..body = literal('gte-5'),
),
.new(
(b) => b
..pattern = .lessThan(literal(0))
..body = literal('negative'),
),
.new(
(b) => b
..pattern = .lessOrEqualTo(literal(-5))
..body = literal('lte-neg-5'),
),
.new(
(b) => b
..pattern = .wildcard
..body = literal('other'),
),
]),
),
equalsDart(r'''
switch (val) {
== 0 => 'zero',
!= 1 => 'not-one',
> 10 => 'gt-10',
>= 5 => 'gte-5',
< 0 => 'negative',
<= -5 => 'lte-neg-5',
_ => 'other',
}
'''),
);
});
});
group('Unary Patterns', () {
test('null-check pattern', () {
expect(
SwitchStatement(
(b) => b
..value = refer('maybeString')
..cases.add(
.new(
(b) => b
..pattern = .declareVar('s').nullChecked
..body = refer('print').call([refer('s')]).statement,
),
),
),
equalsDart(r'''
switch (maybeString) {
case var s?:
print(s);
}
'''),
);
});
test('null-assert pattern', () {
expect(
SwitchStatement(
(b) => b
..value = refer('row')
..cases.add(
.new(
(b) => b
..pattern = .list([
.literal('user'),
.declareVar('name').nullAsserted,
])
..body = refer('print').call([refer('name')]).statement,
),
),
),
equalsDart(r'''
switch (row) {
case ['user', var name!]:
print(name);
}
'''),
);
});
test('cast pattern', () {
expect(
SwitchStatement(
(b) => b
..value = refer('record')
..cases.add(
.new(
(b) => b
..pattern = .record(
positional: [
.declareVar('i').asA(refer('int')),
.declareVar('s').asA(refer('String')),
],
)
..body = refer('print').call([refer('i')]).statement,
),
),
),
equalsDart(r'''
switch (record) {
case (var i as int, var s as String):
print(i);
}
'''),
);
});
test('parenthesized pattern', () {
expect(
SwitchExpression(
(b) => b
..value = refer('x')
..cases.addAll([
.new(
(b) => b
..pattern = .greaterThan(literal(0))
.and(.lessThan(literal(10)))
.parenthesized
.or(.equalTo(literal(100)))
..body = literalTrue,
),
.new(
(b) => b
..pattern = .wildcard
..body = literalFalse,
),
]),
),
equalsDart(r'''
switch (x) {
(> 0 && < 10) || == 100 => true,
_ => false,
}
'''),
);
});
});
group('Constant & Variable Patterns', () {
test('constant pattern with const (...) wrapper', () {
expect(
SwitchStatement(
(b) => b
..value = refer('x')
..cases.add(
.new(
(b) => b
..pattern = .constant(
refer('a').operatorAdd(refer('b')),
isConst: true,
)
..body = refer('print').call([literal('matched')]).statement,
),
),
),
equalsDart(r'''
switch (x) {
case const (a + b):
print('matched');
}
'''),
);
});
test('variable patterns: var, final, typed, final typed', () {
expect(
SwitchStatement(
(b) => b
..value = refer('shape')
..cases.addAll([
.new(
(b) => b
..pattern = .declareVar('a')
..body = refer('print').call([refer('a')]).statement,
),
.new(
(b) => b
..pattern = .declareFinal('b')
..body = refer('print').call([refer('b')]).statement,
),
.new(
(b) => b
..pattern = .typed('c', refer('int'))
..body = refer('print').call([refer('c')]).statement,
),
.new(
(b) => b
..pattern = .declareFinal('d', type: refer('String'))
..body = refer('print').call([refer('d')]).statement,
),
]),
),
equalsDart(r'''
switch (shape) {
case var a:
print(a);
case final b:
print(b);
case int c:
print(c);
case final String d:
print(d);
}
'''),
);
});
test('wildcard pattern', () {
expect(
SwitchStatement(
(b) => b
..value = refer('record')
..cases.add(
.new(
(b) => b
..pattern = .record(positional: [.wildcard, .wildcard])
..body = refer('print').call([literal('matched')]).statement,
),
),
),
equalsDart(r'''
switch (record) {
case (_, _):
print('matched');
}
'''),
);
});
});
group('Destructuring Patterns', () {
test('list pattern with rest element', () {
expect(
SwitchStatement(
(b) => b
..value = refer('list')
..cases.add(
.new(
(b) => b
..pattern = .list(
[.declareVar('a'), .declareVar('b'), .declareVar('c')],
rest: .declareVar('rest'),
restIndex: 2,
)
..body = refer('print').call([refer('rest')]).statement,
),
),
),
equalsDart(r'''
switch (list) {
case [var a, var b, ...var rest, var c]:
print(rest);
}
'''),
);
});
test('list pattern validates restIndex', () {
expect(
() => Pattern.list([.declareVar('a')], rest: .wildcard, restIndex: 2),
throwsRangeError,
);
expect(
() => Pattern.list([.declareVar('a')], rest: .wildcard, restIndex: -1),
throwsRangeError,
);
});
test('typed list pattern with anonymous rest element', () {
expect(
SwitchStatement(
(b) => b
..value = refer('list')
..cases.add(
.new(
(b) => b
..pattern = .list(
[.declareVar('first')],
type: refer('int'),
rest: .wildcard,
restIndex: 1,
)
..body = refer('print').call([refer('first')]).statement,
),
),
),
equalsDart(r'''
switch (list) {
case <int>[var first, ..._]:
print(first);
}
'''),
);
});
test('map pattern', () {
expect(
SwitchStatement(
(b) => b
..value = refer('map')
..cases.add(
.new(
(b) => b
..pattern = .map({
literal('name'): .declareVar('name'),
literal('age'): .typed('age', refer('int')),
})
..body = refer('print').call([refer('name')]).statement,
),
),
),
equalsDart(r'''
switch (map) {
case {'name': var name, 'age': int age}:
print(name);
}
'''),
);
});
test('typed map pattern', () {
expect(
SwitchStatement(
(b) => b
..value = refer('map')
..cases.add(
.new(
(b) => b
..pattern = .map(
{literal('key'): .declareVar('v')},
type: (refer('String'), refer('dynamic')),
)
..body = refer('print').call([refer('v')]).statement,
),
),
),
equalsDart(r'''
switch (map) {
case <String, dynamic>{'key': var v}:
print(v);
}
'''),
);
});
test('record pattern with single positional field', () {
expect(
SwitchStatement(
(b) => b
..value = refer('record')
..cases.add(
.new(
(b) => b
..pattern = .record(positional: [.declareVar('a')])
..body = refer('print').call([refer('a')]).statement,
),
),
),
equalsDart(r'''
switch (record) {
case (var a,):
print(a);
}
'''),
);
});
test('record pattern with positional and named fields', () {
expect(
SwitchStatement(
(b) => b
..value = refer('record')
..cases.add(
.new(
(b) => b
..pattern = .record(
positional: [.declareVar('a')],
named: {
'b': .declareVar('b'),
'c': .typed('c', refer('int')),
},
)
..body = refer('print').call([refer('a')]).statement,
),
),
),
equalsDart(r'''
switch (record) {
case (var a, b: var b, c: int c):
print(a);
}
'''),
);
});
test('type check / empty object pattern', () {
expect(
SwitchExpression(
(b) => b
..value = refer('shape')
..cases.addAll([
.new(
(b) => b
..pattern = .object(refer('Square'))
..body = literal('square'),
),
.new(
(b) => b
..pattern = .object(refer('Circle'))
..body = literal('circle'),
),
.new(
(b) => b
..pattern = .wildcard
..body = literal('unknown'),
),
]),
),
equalsDart(r'''
switch (shape) {
Square() => 'square',
Circle() => 'circle',
_ => 'unknown',
}
'''),
);
});
test('object pattern with fields', () {
expect(
SwitchExpression(
(b) => b
..value = refer('shape')
..cases.addAll([
.new(
(b) => b
..pattern = .object(
refer('Square'),
named: {'length': .declareVar('l')},
)
..body = refer('l').operatorMultiply(refer('l')),
),
.new(
(b) => b
..pattern = .object(
refer('Circle'),
named: {'radius': .declareVar('r')},
)
..body = refer('r').operatorMultiply(refer('r')),
),
]),
),
equalsDart(r'''
switch (shape) {
Square(length: var l) => l * l,
Circle(radius: var r) => r * r,
}
'''),
);
});
});
}