| // 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, |
| } |
| '''), |
| ); |
| }); |
| }); |
| } |