blob: 248fb1382155c14e5144b10ebc08f1c382b6ea2a [file] [edit]
// Copyright (c) 2025, 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
part of '../control.dart';
/// Represents a `catch` block.
///
/// {@category controlFlow}
abstract class CatchBlock
with ControlBlock
implements Built<CatchBlock, CatchBlockBuilder> {
CatchBlock._();
factory CatchBlock([void Function(CatchBlockBuilder) updates]) = _$CatchBlock;
/// The optional type of exception to catch (`on` clause).
///
/// When [type] is set, leave [exception] and [stacktrace]
/// `null` to omit the `catch` statement.
///
/// ``` dart
/// on type
/// on type catch (exception)
/// on type catch (exception, stacktrace)
/// ```
Reference? get type;
/// The optional name of the exception parameter.
///
/// If a [type] is specified, leaving this and [stacktrace] null
/// will omit the `catch` statement entirely.
///
/// If left `null` otherwise, a wildcard (`_`) will be used
/// as the exception name.
///
/// ```dart
/// catch (exception)
/// catch (exception, stacktrace)
/// ```
String? get exception;
/// The optional name of the stacktrace parameter.
///
/// Will be excluded if left `null`.
///
/// ```dart
/// catch (exception)
/// catch (exception, stacktrace)
/// ```
String? get stacktrace;
ControlExpression get _catch =>
ControlExpression.catchStatement(exception ?? '_', stacktrace);
@override
ControlExpression get _expression {
if (type == null) return _catch;
// omit catch clause if exception and stacktrace are unspecified
if (exception == null && stacktrace == null) {
return ControlExpression.onStatement(type!);
}
return ControlExpression.onStatement(type!, _catch);
}
}
/// Represents a `try` or `finally` block.
///
/// **INTERNAL ONLY**.
@internal
class TryBlock with ControlBlock {
@override
final Block body;
final bool isFinally;
const TryBlock._(this.body) : isFinally = false;
const TryBlock._finally(this.body) : isFinally = true;
@override
ControlExpression get _expression =>
isFinally
? ControlExpression.finallyStatement
: ControlExpression.tryStatement;
}
/// Represents a `try`/`catch` block.
///
/// {@category controlFlow}
abstract class TryCatch
with ControlTree
implements Built<TryCatch, TryCatchBuilder> {
TryCatch._();
/// Build a [TryCatch].
factory TryCatch([void Function(TryCatchBuilder) updates]) = _$TryCatch;
/// The body of the `try` clause.
///
/// ```dart
/// try {
/// body
/// }
/// ```
Block get body;
/// The `catch` clauses for this block.
BuiltList<CatchBlock> get handlers;
/// The optional `finally` clause body.
///
/// ```dart
/// finally {
/// handleAll
/// }
/// ```
Block? get handleAll;
TryBlock get _try => TryBlock._(body);
TryBlock? get _finally =>
handleAll == null ? null : TryBlock._finally(handleAll!);
@override
List<ControlBlock?> get _blocks => [_try, ...handlers, _finally];
/// Ensure [handlers] is not empty
@BuiltValueHook(finalizeBuilder: true)
static void _build(TryCatchBuilder builder) =>
builder.handlers.isNotEmpty ||
(throw ArgumentError(
'One or more `catch` clauses must be specified.',
'handlers',
));
}
/// Builds a [TryCatch] block.
///
/// {@category controlFlow}
abstract class TryCatchBuilder implements Builder<TryCatch, TryCatchBuilder> {
TryCatchBuilder._();
factory TryCatchBuilder() = _$TryCatchBuilder;
/// The body of the `try` clause.
///
/// ```dart
/// try {
/// body
/// }
/// ```
BlockBuilder body = BlockBuilder();
/// The optional `finally` clause body.
///
/// ```dart
/// finally {
/// handleAll
/// }
/// ```
BlockBuilder? handleAll;
/// The `catch` clauses for this block.
ListBuilder<CatchBlock> handlers = ListBuilder();
/// Build a `catch` clause and add it to [handlers].
void addCatch(void Function(CatchBlockBuilder block) builder) =>
handlers.add((CatchBlockBuilder()..update(builder)).build());
/// Build a `finally` clause and update [handleAll].
void addFinally(void Function(BlockBuilder body) builder) =>
handleAll = BlockBuilder()..update(builder);
}