From f367d1e107d5c202246a6910199ba90c64d6d755 Mon Sep 17 00:00:00 2001 From: David Morgan Date: Tue, 6 Feb 2024 17:32:14 +0000 Subject: [PATCH] [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 Auto-Submit: Morgan :) Reviewed-by: Jake Macdonald Commit-Queue: Morgan :) --- .../lib/src/macros/api/exceptions.dart | 8 +++++++ .../src/macros/executor/execute_macro.dart | 18 ++++++++++------ .../error/diagnostic_exception_test.dart | 14 +++++++++++++ .../throw_diagnostic_exception_macro.dart | 21 +++++++++++++++++++ 4 files changed, 55 insertions(+), 6 deletions(-) create mode 100644 tests/language/macros/error/diagnostic_exception_test.dart create mode 100644 tests/language/macros/error/impl/throw_diagnostic_exception_macro.dart diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/api/exceptions.dart b/pkg/_fe_analyzer_shared/lib/src/macros/api/exceptions.dart index 6d76c850c17..ec65ab20fea 100644 --- a/pkg/_fe_analyzer_shared/lib/src/macros/api/exceptions.dart +++ b/pkg/_fe_analyzer_shared/lib/src/macros/api/exceptions.dart @@ -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. /// diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/executor/execute_macro.dart b/pkg/_fe_analyzer_shared/lib/src/macros/executor/execute_macro.dart index 1ec7a607bd9..4bf70b206ea 100644 --- a/pkg/_fe_analyzer_shared/lib/src/macros/executor/execute_macro.dart +++ b/pkg/_fe_analyzer_shared/lib/src/macros/executor/execute_macro.dart @@ -59,8 +59,10 @@ Future 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 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 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. diff --git a/tests/language/macros/error/diagnostic_exception_test.dart b/tests/language/macros/error/diagnostic_exception_test.dart new file mode 100644 index 00000000000..cec905070be --- /dev/null +++ b/tests/language/macros/error/diagnostic_exception_test.dart @@ -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 diff --git a/tests/language/macros/error/impl/throw_diagnostic_exception_macro.dart b/tests/language/macros/error/impl/throw_diagnostic_exception_macro.dart new file mode 100644 index 00000000000..b0ebe00e339 --- /dev/null +++ b/tests/language/macros/error/impl/throw_diagnostic_exception_macro.dart @@ -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 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)); + } +}