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