blob: 0480d75171f43705d3d80fd8220a62c66bd38688 [file] [log] [blame]
// Copyright (c) 2024, 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 '../api.dart';
import 'remote_instance.dart';
import 'serialization.dart';
/// Base class for exceptions thrown during macro execution.
///
/// Macro implementations can catch these exceptions to provide more
/// information to the user. In case an exception results from user error, they
/// can provide a pointer to the likely fix. If the exception results from an
/// implementation error or unknown error, the macro implementation might give
/// the user information on where and how to file an issue.
///
/// If a `MacroException` is not caught by a macro implementation then it will
/// be reported in a user-oriented way, for example for
/// `MacroImplementationException` the displayed message suggests that there
/// is a bug in the macro implementation.
abstract base class MacroExceptionImpl extends RemoteInstance
implements MacroException {
@override
final String message;
@override
final String? stackTrace;
MacroExceptionImpl._({int? id, required this.message, this.stackTrace})
: super(id ?? RemoteInstance.uniqueId);
factory MacroExceptionImpl(
{required int id,
required RemoteInstanceKind kind,
required String message,
String? stackTrace}) {
switch (kind) {
case RemoteInstanceKind.unexpectedMacroException:
return UnexpectedMacroExceptionImpl(message,
id: id, stackTrace: stackTrace);
case RemoteInstanceKind.macroImplementationException:
return MacroImplementationExceptionImpl(message,
id: id, stackTrace: stackTrace);
case RemoteInstanceKind.macroIntrospectionCycleException:
return MacroIntrospectionCycleExceptionImpl(message,
id: id, stackTrace: stackTrace);
default:
throw ArgumentError.value(kind, 'kind');
}
}
/// Instantiates from a throwable caught during macro execution.
///
/// If [throwable] is already a subclass of `MacroException`, return it.
/// Otherwise it's an unexpected type, return an [UnexpectedMacroException].
factory MacroExceptionImpl.from(Object throwable, StackTrace stackTrace) {
if (throwable is MacroExceptionImpl) return throwable;
return UnexpectedMacroExceptionImpl(throwable.toString(),
stackTrace: stackTrace.toString());
}
@override
String toString() => '$message${stackTrace == null ? '' : '\n\n$stackTrace'}';
@override
void serializeUncached(Serializer serializer) {
super.serializeUncached(serializer);
serializer.addString(message);
serializer.addNullableString(stackTrace);
}
}
/// Something unexpected happened during macro execution.
///
/// For example, a bug in the SDK.
final class UnexpectedMacroExceptionImpl extends MacroExceptionImpl
implements UnexpectedMacroException {
UnexpectedMacroExceptionImpl(String message, {super.id, super.stackTrace})
: super._(message: message);
@override
RemoteInstanceKind get kind => RemoteInstanceKind.unexpectedMacroException;
@override
String toString() => 'UnexpectedMacroException: ${super.toString()}';
}
/// An error due to incorrect implementation was thrown during macro execution.
///
/// For example, an incorrect argument was passed to the macro API.
///
/// The type `Error` is usually used for such throwables, and it's common to
/// allow the program to crash when one is thrown.
///
/// In the case of macros, however, type `Exception` is used because the macro
/// implementation can usefully catch it in order to give the user information
/// about how to notify the macro author about the bug.
final class MacroImplementationExceptionImpl extends MacroExceptionImpl
implements MacroImplementationException {
MacroImplementationExceptionImpl(String message, {super.id, super.stackTrace})
: super._(message: message);
@override
RemoteInstanceKind get kind =>
RemoteInstanceKind.macroImplementationException;
@override
String toString() => 'MacroImplementationException: ${super.toString()}';
}
/// A cycle was detected in macro applications introspecting targets of other
/// macro applications.
///
/// The order the macros should run in is not defined, so allowing
/// introspection in this case would make the macro output non-deterministic.
/// Instead, all the introspection calls in the cycle fail with this exception.
base class MacroIntrospectionCycleExceptionImpl extends MacroExceptionImpl
implements MacroIntrospectionCycleException {
MacroIntrospectionCycleExceptionImpl(String message,
{super.id, super.stackTrace})
: super._(message: message);
@override
RemoteInstanceKind get kind =>
RemoteInstanceKind.macroIntrospectionCycleException;
@override
String toString() => 'MacroIntrospectionCycleException: ${super.toString()}';
}