[macros] Add DiagnosticException that macro implementations can throw to report a diagnostic.

R=jakemac@google.com

Change-Id: I7546aa84f3e0b8423465dcb49d90a89df9231b84
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/350302
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Auto-Submit: Morgan :) <davidmorgan@google.com>
Reviewed-by: Jake Macdonald <jakemac@google.com>
Commit-Queue: Morgan :) <davidmorgan@google.com>
This commit is contained in:
David Morgan 2024-02-06 17:32:14 +00:00 committed by Commit Queue
parent f4b1d59461
commit f367d1e107
4 changed files with 55 additions and 6 deletions

View file

@ -4,6 +4,14 @@
part of '../api.dart';
/// Exception for use in macro implementations.
///
/// Throw to stop the current macro execution and report a [Diagnostic].
class DiagnosticException implements Exception {
final Diagnostic diagnostic;
DiagnosticException(this.diagnostic);
}
/// Base class for exceptions thrown by the host implementation during macro
/// execution.
///

View file

@ -59,8 +59,10 @@ Future<MacroExecutionResult> executeTypesMacro(
'macro: $macro\ntarget: $target');
}
} catch (e, s) {
// Preserve `MacroException`s thrown by SDK tools.
if (e is MacroExceptionImpl) {
if (e is DiagnosticException) {
builder.report(e.diagnostic);
} else if (e is MacroExceptionImpl) {
// Preserve `MacroException`s thrown by SDK tools.
builder.failWithException(e);
} else {
// Convert exceptions thrown by macro implementations into diagnostics.
@ -137,8 +139,10 @@ Future<MacroExecutionResult> executeDeclarationsMacro(Macro macro,
'macro: $macro\ntarget: $target');
}
} catch (e, s) {
// Preserve `MacroException`s thrown by SDK tools.
if (e is MacroExceptionImpl) {
if (e is DiagnosticException) {
builder.report(e.diagnostic);
} else if (e is MacroExceptionImpl) {
// Preserve `MacroException`s thrown by SDK tools.
builder.failWithException(e);
} else {
// Convert exceptions thrown by macro implementations into diagnostics.
@ -212,8 +216,10 @@ Future<MacroExecutionResult> executeDefinitionMacro(Macro macro, Object target,
'macro: $macro\ntarget: $target');
}
} catch (e, s) {
// Preserve `MacroException`s thrown by SDK tools.
if (e is MacroExceptionImpl) {
if (e is DiagnosticException) {
builder.report(e.diagnostic);
} else if (e is MacroExceptionImpl) {
// Preserve `MacroException`s thrown by SDK tools.
builder.failWithException(e);
} else {
// Convert exceptions thrown by macro implementations into diagnostics.

View file

@ -0,0 +1,14 @@
// 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.
// SharedOptions=--enable-experiment=macros
import 'impl/throw_diagnostic_exception_macro.dart';
@ThrowDiagnosticException(atTypeDeclaration: 'B', withMessage: 'failed here')
class A {}
class B {}
// ^
// [analyzer] COMPILE_TIME_ERROR.MACRO_ERROR
// [cfe] failed here

View file

@ -0,0 +1,21 @@
// 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.
// ignore_for_file: deprecated_member_use
import 'package:_fe_analyzer_shared/src/macros/api.dart';
macro class ThrowDiagnosticException implements ClassDeclarationsMacro {
final String atTypeDeclaration;
final String withMessage;
const ThrowDiagnosticException({
required this.atTypeDeclaration, required this.withMessage});
Future<void> buildDeclarationsForClass(
ClassDeclaration clazz, MemberDeclarationBuilder builder) async {
final identifier = await builder.resolveIdentifier(clazz.library.uri, atTypeDeclaration);
final declaration = await builder.typeDeclarationOf(identifier);
throw DiagnosticException(Diagnostic(DiagnosticMessage(
withMessage, target: declaration.asDiagnosticTarget), Severity.error));
}
}