add Builder.report method and Diagnostic class to support macros sending diagnostics

Will follow up with tests once we agree on the API. At the same time I will handle translating any normal runtime errors into error diagnostics.

Bug: https://github.com/dart-lang/language/issues/3234
Change-Id: I2fc22af088f0e50b1877022aa793337850257804
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/317221
Commit-Queue: Jake Macdonald <jakemac@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
This commit is contained in:
Jake Macdonald 2023-08-30 17:43:42 +00:00 committed by Commit Queue
parent 6a2c9cb826
commit abc7a21679
14 changed files with 555 additions and 246 deletions

View file

@ -3,8 +3,10 @@
// BSD-style license that can be found in the LICENSE file.
import 'dart:async';
import 'dart:collection' show UnmodifiableListView;
part 'api/builders.dart';
part 'api/code.dart';
part 'api/diagnostic.dart';
part 'api/introspection.dart';
part 'api/macros.dart';

View file

@ -6,7 +6,15 @@ part of '../api.dart';
/// The base interface used to add declarations to the program as well
/// as augment existing ones.
abstract interface class Builder {}
///
/// Can also be used to emit diagnostic messages back to the parent tool.
abstract interface class Builder {
/// Attaches [diagnostic] to the result of this macro application phase.
///
/// Note that this will not immediately send the result, these will all be
/// collected and reported at once when the macro completes this phase.
void report(Diagnostic diagnostic);
}
/// The interface for all introspection that is allowed during the type phase
/// (and later).

View file

@ -0,0 +1,96 @@
// Copyright (c) 2023, 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 '../api.dart';
/// A diagnostic reported from a [Macro].
class Diagnostic {
/// Additional [DiagnosticMessage]s related to this one, to help with the
/// context.
final Iterable<DiagnosticMessage> contextMessages;
/// An optional message describing to the user how they might fix this
/// diagnostic.
final String? correctionMessage;
/// The primary message for this diagnostic.
final DiagnosticMessage message;
/// The severity of this diagnostic.
final Severity severity;
/// General diagnostics for the current macro application.
///
/// These will be attached to the macro application itself.
Diagnostic(this.message, this.severity,
{List<DiagnosticMessage> contextMessages = const [],
this.correctionMessage})
: contextMessages = new UnmodifiableListView(contextMessages);
}
/// A message and optional target for a [Diagnostic] reported by a [Macro].
class DiagnosticMessage {
/// The primary message for this diagnostic message.
final String message;
/// The optional target for this diagnostic message.
///
/// If provided, the diagnostic should be linked to this target.
///
/// If not provided, it should be implicitly linked to the macro application
/// that generated this diagnostic.
final DiagnosticTarget? target;
DiagnosticMessage(this.message, {this.target});
}
/// A target for a [DiagnosticMessage]. We use a sealed class to represent a
/// union type of the valid target types.
sealed class DiagnosticTarget {}
/// A [DiagnosticMessage] target which is a [Declaration].
final class DeclarationDiagnosticTarget extends DiagnosticTarget {
final Declaration declaration;
DeclarationDiagnosticTarget(this.declaration);
}
/// A simplified way of creating a [DiagnosticTarget] target for a
/// [Declaration].
extension DeclarationAsTarget on Declaration {
DeclarationDiagnosticTarget get asDiagnosticTarget =>
new DeclarationDiagnosticTarget(this);
}
/// A [DiagnosticMessage] target which is a [TypeAnnotation].
final class TypeAnnotationDiagnosticTarget extends DiagnosticTarget {
final TypeAnnotation typeAnnotation;
TypeAnnotationDiagnosticTarget(this.typeAnnotation);
}
/// A simplified way of creating a [DiagnosticTarget] target for a
/// [TypeAnnotation].
extension TypeAnnotationAsTarget on TypeAnnotation {
TypeAnnotationDiagnosticTarget get asDiagnosticTarget =>
new TypeAnnotationDiagnosticTarget(this);
}
/// The severities supported for [Diagnostic]s.
enum Severity {
/// Informational message only, for example a style guideline is not being
/// followed. These may not always be shown to the user depending on how the
/// app is being compiled and with what flags.
info,
/// Not a critical failure, but something is likely wrong and the code should
/// be changed. Always shown to the user by default, but may be silenceable by
/// some tools.
warning,
/// Critical failure, the macro could not proceed. Cannot be silenced and will
/// always prevent the app from compiling successfully. These are always shown
/// to the user and cannot be silenced.
error,
}

View file

@ -155,6 +155,9 @@ abstract class MacroInstanceIdentifier implements Serializable {
/// All modifications are expressed in terms of library augmentation
/// declarations.
abstract class MacroExecutionResult implements Serializable {
/// All [Diagnostic]s reported as a result of executing a macro.
List<Diagnostic> get diagnostics;
/// Any augmentations to enum values that should be applied to an enum as a
/// result of executing a macro, indexed by the identifier of the enum.
Map<Identifier, Iterable<DeclarationCode>> get enumValueAugmentations;

View file

@ -10,7 +10,10 @@ import '../executor.dart';
import '../api.dart';
import 'response_impls.dart';
abstract class TypeBuilderBase implements TypePhaseIntrospector {
abstract class TypeBuilderBase implements TypePhaseIntrospector, Builder {
/// All the collected diagnostics for this builder.
final List<Diagnostic> _diagnostics = [];
/// All the enum values to be added, indexed by the identifier for the
/// augmented enum declaration.
final Map<IdentifierImpl, List<DeclarationCode>> _enumValueAugmentations;
@ -38,6 +41,7 @@ abstract class TypeBuilderBase implements TypePhaseIntrospector {
/// Creates and returns a [MacroExecutionResult] out of the [_augmentations]
/// created by this builder.
MacroExecutionResult get result => new MacroExecutionResultImpl(
diagnostics: _diagnostics,
enumValueAugmentations: _enumValueAugmentations,
interfaceAugmentations: _interfaceAugmentations,
libraryAugmentations: _libraryAugmentations,
@ -58,6 +62,9 @@ abstract class TypeBuilderBase implements TypePhaseIntrospector {
_mixinAugmentations = parentMixinAugmentations ?? {},
_typeAugmentations = parentTypeAugmentations ?? {};
@override
void report(Diagnostic diagnostic) => _diagnostics.add(diagnostic);
@override
Future<Identifier> resolveIdentifier(Uri library, String identifier) =>
// ignore: deprecated_member_use_from_same_package

View file

@ -10,47 +10,55 @@ import 'package:_fe_analyzer_shared/src/macros/api.dart';
/// Runs [macro] in the types phase and returns a [MacroExecutionResult].
Future<MacroExecutionResult> executeTypesMacro(
Macro macro, Object target, TypePhaseIntrospector introspector) async {
// This must be assigned by the end.
late final TypeBuilderImpl builder;
// Must be assigned, used for error reporting.
late final TypeBuilderBase builder;
// Shared code for most branches. If we do create it, assign it to `builder`.
late final TypeBuilderImpl typeBuilder =
builder = new TypeBuilderImpl(introspector);
switch ((target, macro)) {
case (Library target, LibraryTypesMacro macro):
await macro.buildTypesForLibrary(target, typeBuilder);
case (ConstructorDeclaration target, ConstructorTypesMacro macro):
await macro.buildTypesForConstructor(target, typeBuilder);
case (MethodDeclaration target, MethodTypesMacro macro):
await macro.buildTypesForMethod(target, typeBuilder);
case (FunctionDeclaration target, FunctionTypesMacro macro):
await macro.buildTypesForFunction(target, typeBuilder);
case (FieldDeclaration target, FieldTypesMacro macro):
await macro.buildTypesForField(target, typeBuilder);
case (VariableDeclaration target, VariableTypesMacro macro):
await macro.buildTypesForVariable(target, typeBuilder);
case (ClassDeclaration target, ClassTypesMacro macro):
await macro.buildTypesForClass(
target,
builder = new ClassTypeBuilderImpl(
target.identifier as IdentifierImpl, introspector));
case (EnumDeclaration target, EnumTypesMacro macro):
await macro.buildTypesForEnum(
target,
builder = new EnumTypeBuilderImpl(
target.identifier as IdentifierImpl, introspector));
case (ExtensionDeclaration target, ExtensionTypesMacro macro):
await macro.buildTypesForExtension(target, typeBuilder);
case (MixinDeclaration target, MixinTypesMacro macro):
await macro.buildTypesForMixin(
target,
builder = new MixinTypeBuilderImpl(
target.identifier as IdentifierImpl, introspector));
case (EnumValueDeclaration target, EnumValueTypesMacro macro):
await macro.buildTypesForEnumValue(target, typeBuilder);
default:
throw new UnsupportedError('Unsupported macro type or invalid target:\n'
'macro: $macro\ntarget: $target');
// TODO(jakemac): More robust handling for unawaited async errors?
try {
// Shared code for most branches. If we do create it, assign it to
// `builder`.
late final TypeBuilderImpl typeBuilder =
builder = new TypeBuilderImpl(introspector);
switch ((target, macro)) {
case (Library target, LibraryTypesMacro macro):
await macro.buildTypesForLibrary(target, typeBuilder);
case (ConstructorDeclaration target, ConstructorTypesMacro macro):
await macro.buildTypesForConstructor(target, typeBuilder);
case (MethodDeclaration target, MethodTypesMacro macro):
await macro.buildTypesForMethod(target, typeBuilder);
case (FunctionDeclaration target, FunctionTypesMacro macro):
await macro.buildTypesForFunction(target, typeBuilder);
case (FieldDeclaration target, FieldTypesMacro macro):
await macro.buildTypesForField(target, typeBuilder);
case (VariableDeclaration target, VariableTypesMacro macro):
await macro.buildTypesForVariable(target, typeBuilder);
case (ClassDeclaration target, ClassTypesMacro macro):
await macro.buildTypesForClass(
target,
builder = new ClassTypeBuilderImpl(
target.identifier as IdentifierImpl, introspector));
case (EnumDeclaration target, EnumTypesMacro macro):
await macro.buildTypesForEnum(
target,
builder = new EnumTypeBuilderImpl(
target.identifier as IdentifierImpl, introspector));
case (ExtensionDeclaration target, ExtensionTypesMacro macro):
await macro.buildTypesForExtension(target, typeBuilder);
case (MixinDeclaration target, MixinTypesMacro macro):
await macro.buildTypesForMixin(
target,
builder = new MixinTypeBuilderImpl(
target.identifier as IdentifierImpl, introspector));
case (EnumValueDeclaration target, EnumValueTypesMacro macro):
await macro.buildTypesForEnumValue(target, typeBuilder);
default:
throw new UnsupportedError('Unsupported macro type or invalid target:\n'
'macro: $macro\ntarget: $target');
}
} catch (e, s) {
builder.report(new Diagnostic(
new DiagnosticMessage('Unhandled error: $e\n' 'Stack trace:\n$s'),
Severity.error));
}
return builder.result;
}
@ -58,9 +66,12 @@ Future<MacroExecutionResult> executeTypesMacro(
/// Runs [macro] in the declaration phase and returns a [MacroExecutionResult].
Future<MacroExecutionResult> executeDeclarationsMacro(Macro macro,
Object target, DeclarationPhaseIntrospector introspector) async {
// Must be assigned, used for error reporting.
late final DeclarationBuilderBase builder;
// At most one of these will be used below.
late MemberDeclarationBuilderImpl memberBuilder =
new MemberDeclarationBuilderImpl(
builder = new MemberDeclarationBuilderImpl(
switch (target) {
MemberDeclaration() => target.definingType as IdentifierImpl,
TypeDeclarationImpl() => target.identifier,
@ -70,158 +81,156 @@ Future<MacroExecutionResult> executeDeclarationsMacro(Macro macro,
},
introspector);
late DeclarationBuilderImpl topLevelBuilder =
new DeclarationBuilderImpl(introspector);
late EnumDeclarationBuilderImpl enumBuilder = new EnumDeclarationBuilderImpl(
switch (target) {
EnumDeclarationImpl() => target.identifier,
EnumValueDeclarationImpl() => target.definingEnum,
_ => throw new StateError(
'Can only create enum declaration builders for enum or enum '
'value declarations, but got $target'),
},
introspector);
builder = new DeclarationBuilderImpl(introspector);
late EnumDeclarationBuilderImpl enumBuilder =
builder = new EnumDeclarationBuilderImpl(
switch (target) {
EnumDeclarationImpl() => target.identifier,
EnumValueDeclarationImpl() => target.definingEnum,
_ => throw new StateError(
'Can only create enum declaration builders for enum or enum '
'value declarations, but got $target'),
},
introspector);
switch ((target, macro)) {
case (Library target, LibraryDeclarationsMacro macro):
await macro.buildDeclarationsForLibrary(target, topLevelBuilder);
return topLevelBuilder.result;
case (ClassDeclaration target, ClassDeclarationsMacro macro):
if (target is! IntrospectableClassDeclarationImpl) {
throw new ArgumentError(
'Class declarations annotated with a macro should be '
'introspectable in the declarations phase.');
}
await macro.buildDeclarationsForClass(target, memberBuilder);
return memberBuilder.result;
case (EnumDeclaration target, EnumDeclarationsMacro macro):
if (target is! IntrospectableEnumDeclarationImpl) {
throw new ArgumentError(
'Enum declarations annotated with a macro should be introspectable '
'in the declarations phase.');
}
// TODO(jakemac): More robust handling for unawaited async errors?
try {
switch ((target, macro)) {
case (Library target, LibraryDeclarationsMacro macro):
await macro.buildDeclarationsForLibrary(target, topLevelBuilder);
case (ClassDeclaration target, ClassDeclarationsMacro macro):
if (target is! IntrospectableClassDeclarationImpl) {
throw new ArgumentError(
'Class declarations annotated with a macro should be '
'introspectable in the declarations phase.');
}
await macro.buildDeclarationsForClass(target, memberBuilder);
case (EnumDeclaration target, EnumDeclarationsMacro macro):
if (target is! IntrospectableEnumDeclarationImpl) {
throw new ArgumentError(
'Enum declarations annotated with a macro should be '
'introspectable in the declarations phase.');
}
await macro.buildDeclarationsForEnum(target, enumBuilder);
return enumBuilder.result;
case (ExtensionDeclaration target, ExtensionDeclarationsMacro macro):
if (target is! IntrospectableExtensionDeclarationImpl) {
throw new ArgumentError(
'Extension declarations annotated with a macro should be '
'introspectable in the declarations phase.');
}
await macro.buildDeclarationsForExtension(target, memberBuilder);
return memberBuilder.result;
case (MixinDeclaration target, MixinDeclarationsMacro macro):
if (target is! IntrospectableMixinDeclarationImpl) {
throw new ArgumentError(
'Mixin declarations annotated with a macro should be '
'introspectable in the declarations phase.');
}
await macro.buildDeclarationsForMixin(target, memberBuilder);
return memberBuilder.result;
case (EnumValueDeclaration target, EnumValueDeclarationsMacro macro):
await macro.buildDeclarationsForEnumValue(target, enumBuilder);
return enumBuilder.result;
case (ConstructorDeclaration target, ConstructorDeclarationsMacro macro):
await macro.buildDeclarationsForConstructor(target, memberBuilder);
return memberBuilder.result;
case (MethodDeclaration target, MethodDeclarationsMacro macro):
await macro.buildDeclarationsForMethod(target, memberBuilder);
return memberBuilder.result;
case (FieldDeclaration target, FieldDeclarationsMacro macro):
await macro.buildDeclarationsForField(target, memberBuilder);
return memberBuilder.result;
case (FunctionDeclaration target, FunctionDeclarationsMacro macro):
await macro.buildDeclarationsForFunction(target, topLevelBuilder);
return topLevelBuilder.result;
case (VariableDeclaration target, VariableDeclarationsMacro macro):
await macro.buildDeclarationsForVariable(target, topLevelBuilder);
return topLevelBuilder.result;
default:
throw new UnsupportedError('Unsupported macro type or invalid target:\n'
'macro: $macro\ntarget: $target');
await macro.buildDeclarationsForEnum(target, enumBuilder);
case (ExtensionDeclaration target, ExtensionDeclarationsMacro macro):
if (target is! IntrospectableExtensionDeclarationImpl) {
throw new ArgumentError(
'Extension declarations annotated with a macro should be '
'introspectable in the declarations phase.');
}
await macro.buildDeclarationsForExtension(target, memberBuilder);
case (MixinDeclaration target, MixinDeclarationsMacro macro):
if (target is! IntrospectableMixinDeclarationImpl) {
throw new ArgumentError(
'Mixin declarations annotated with a macro should be '
'introspectable in the declarations phase.');
}
await macro.buildDeclarationsForMixin(target, memberBuilder);
case (EnumValueDeclaration target, EnumValueDeclarationsMacro macro):
await macro.buildDeclarationsForEnumValue(target, enumBuilder);
case (ConstructorDeclaration target, ConstructorDeclarationsMacro macro):
await macro.buildDeclarationsForConstructor(target, memberBuilder);
case (MethodDeclaration target, MethodDeclarationsMacro macro):
await macro.buildDeclarationsForMethod(target, memberBuilder);
case (FieldDeclaration target, FieldDeclarationsMacro macro):
await macro.buildDeclarationsForField(target, memberBuilder);
case (FunctionDeclaration target, FunctionDeclarationsMacro macro):
await macro.buildDeclarationsForFunction(target, topLevelBuilder);
case (VariableDeclaration target, VariableDeclarationsMacro macro):
await macro.buildDeclarationsForVariable(target, topLevelBuilder);
default:
throw new UnsupportedError('Unsupported macro type or invalid target:\n'
'macro: $macro\ntarget: $target');
}
} catch (e, s) {
builder.report(new Diagnostic(
new DiagnosticMessage('Unhandled error: $e\n' 'Stack trace:\n$s'),
Severity.error));
}
return builder.result;
}
/// Runs [macro] in the definition phase and returns a [MacroExecutionResult].
Future<MacroExecutionResult> executeDefinitionMacro(Macro macro, Object target,
DefinitionPhaseIntrospector introspector) async {
// Must be assigned, used for error reporting and returning a value.
late final DefinitionBuilderBase builder;
// At most one of these will be used below.
late FunctionDefinitionBuilderImpl functionBuilder =
late FunctionDefinitionBuilderImpl functionBuilder = builder =
new FunctionDefinitionBuilderImpl(
target as FunctionDeclarationImpl, introspector);
late VariableDefinitionBuilderImpl variableBuilder =
late VariableDefinitionBuilderImpl variableBuilder = builder =
new VariableDefinitionBuilderImpl(
target as VariableDeclaration, introspector);
late TypeDefinitionBuilderImpl typeBuilder =
late TypeDefinitionBuilderImpl typeBuilder = builder =
new TypeDefinitionBuilderImpl(target as IntrospectableType, introspector);
switch ((target, macro)) {
case (Library target, LibraryDefinitionMacro macro):
LibraryDefinitionBuilderImpl builder =
new LibraryDefinitionBuilderImpl(target, introspector);
await macro.buildDefinitionForLibrary(target, builder);
return builder.result;
case (ClassDeclaration target, ClassDefinitionMacro macro):
if (target is! IntrospectableClassDeclaration) {
throw new ArgumentError(
'Class declarations annotated with a macro should be '
'introspectable in the definitions phase.');
}
await macro.buildDefinitionForClass(target, typeBuilder);
return typeBuilder.result;
case (EnumDeclaration target, EnumDefinitionMacro macro):
if (target is! IntrospectableEnumDeclaration) {
throw new ArgumentError(
'Enum declarations annotated with a macro should be introspectable '
'in the definitions phase.');
}
EnumDefinitionBuilderImpl builder =
new EnumDefinitionBuilderImpl(target, introspector);
await macro.buildDefinitionForEnum(target, builder);
return builder.result;
case (ExtensionDeclaration target, ExtensionDefinitionMacro macro):
if (target is! IntrospectableExtensionDeclaration) {
throw new ArgumentError(
'Extension declarations annotated with a macro should be '
'introspectable in the definitions phase.');
}
await macro.buildDefinitionForExtension(target, typeBuilder);
return typeBuilder.result;
case (MixinDeclaration target, MixinDefinitionMacro macro):
if (target is! IntrospectableMixinDeclaration) {
throw new ArgumentError(
'Mixin declarations annotated with a macro should be '
'introspectable in the definitions phase.');
}
await macro.buildDefinitionForMixin(target, typeBuilder);
return typeBuilder.result;
case (EnumValueDeclaration target, EnumValueDefinitionMacro macro):
EnumValueDefinitionBuilderImpl builder =
new EnumValueDefinitionBuilderImpl(
target as EnumValueDeclarationImpl, introspector);
await macro.buildDefinitionForEnumValue(target, builder);
return builder.result;
case (ConstructorDeclaration target, ConstructorDefinitionMacro macro):
ConstructorDefinitionBuilderImpl builder =
new ConstructorDefinitionBuilderImpl(
target as ConstructorDeclarationImpl, introspector);
await macro.buildDefinitionForConstructor(target, builder);
return builder.result;
case (MethodDeclaration target, MethodDefinitionMacro macro):
await macro.buildDefinitionForMethod(
target as MethodDeclarationImpl, functionBuilder);
return functionBuilder.result;
case (FieldDeclaration target, FieldDefinitionMacro macro):
await macro.buildDefinitionForField(target, variableBuilder);
return variableBuilder.result;
case (FunctionDeclaration target, FunctionDefinitionMacro macro):
await macro.buildDefinitionForFunction(target, functionBuilder);
return functionBuilder.result;
case (VariableDeclaration target, VariableDefinitionMacro macro):
await macro.buildDefinitionForVariable(target, variableBuilder);
return variableBuilder.result;
default:
throw new UnsupportedError('Unsupported macro type or invalid target:\n'
'macro: $macro\ntarget: $target');
// TODO(jakemac): More robust handling for unawaited async errors?
try {
switch ((target, macro)) {
case (Library target, LibraryDefinitionMacro macro):
LibraryDefinitionBuilderImpl libraryBuilder =
builder = new LibraryDefinitionBuilderImpl(target, introspector);
await macro.buildDefinitionForLibrary(target, libraryBuilder);
case (ClassDeclaration target, ClassDefinitionMacro macro):
if (target is! IntrospectableClassDeclaration) {
throw new ArgumentError(
'Class declarations annotated with a macro should be '
'introspectable in the definitions phase.');
}
await macro.buildDefinitionForClass(target, typeBuilder);
case (EnumDeclaration target, EnumDefinitionMacro macro):
if (target is! IntrospectableEnumDeclaration) {
throw new ArgumentError(
'Enum declarations annotated with a macro should be '
'introspectable in the definitions phase.');
}
EnumDefinitionBuilderImpl enumBuilder =
builder = new EnumDefinitionBuilderImpl(target, introspector);
await macro.buildDefinitionForEnum(target, enumBuilder);
case (ExtensionDeclaration target, ExtensionDefinitionMacro macro):
if (target is! IntrospectableExtensionDeclaration) {
throw new ArgumentError(
'Extension declarations annotated with a macro should be '
'introspectable in the definitions phase.');
}
await macro.buildDefinitionForExtension(target, typeBuilder);
case (MixinDeclaration target, MixinDefinitionMacro macro):
if (target is! IntrospectableMixinDeclaration) {
throw new ArgumentError(
'Mixin declarations annotated with a macro should be '
'introspectable in the definitions phase.');
}
await macro.buildDefinitionForMixin(target, typeBuilder);
case (EnumValueDeclaration target, EnumValueDefinitionMacro macro):
EnumValueDefinitionBuilderImpl enumValueBuilder = builder =
new EnumValueDefinitionBuilderImpl(
target as EnumValueDeclarationImpl, introspector);
await macro.buildDefinitionForEnumValue(target, enumValueBuilder);
case (ConstructorDeclaration target, ConstructorDefinitionMacro macro):
ConstructorDefinitionBuilderImpl constructorBuilder = builder =
new ConstructorDefinitionBuilderImpl(
target as ConstructorDeclarationImpl, introspector);
await macro.buildDefinitionForConstructor(target, constructorBuilder);
case (MethodDeclaration target, MethodDefinitionMacro macro):
await macro.buildDefinitionForMethod(
target as MethodDeclarationImpl, functionBuilder);
case (FieldDeclaration target, FieldDefinitionMacro macro):
await macro.buildDefinitionForField(target, variableBuilder);
case (FunctionDeclaration target, FunctionDefinitionMacro macro):
await macro.buildDefinitionForFunction(target, functionBuilder);
case (VariableDeclaration target, VariableDefinitionMacro macro):
await macro.buildDefinitionForVariable(target, variableBuilder);
default:
throw new UnsupportedError('Unsupported macro type or invalid target:\n'
'macro: $macro\ntarget: $target');
}
} catch (e, s) {
builder.report(new Diagnostic(
new DiagnosticMessage('Unhandled error: $e\n' 'Stack trace:\n$s'),
Severity.error));
}
return builder.result;
}

View file

@ -251,6 +251,9 @@ class MacroInstanceIdentifierImpl implements MacroInstanceIdentifier {
/// Implementation of [MacroExecutionResult].
class MacroExecutionResultImpl implements MacroExecutionResult {
@override
final List<Diagnostic> diagnostics;
@override
final Map<IdentifierImpl, List<DeclarationCode>> enumValueAugmentations;
@ -270,6 +273,7 @@ class MacroExecutionResultImpl implements MacroExecutionResult {
final Map<IdentifierImpl, List<DeclarationCode>> typeAugmentations;
MacroExecutionResultImpl({
required this.diagnostics,
required this.enumValueAugmentations,
required this.interfaceAugmentations,
required this.libraryAugmentations,
@ -279,6 +283,13 @@ class MacroExecutionResultImpl implements MacroExecutionResult {
});
factory MacroExecutionResultImpl.deserialize(Deserializer deserializer) {
deserializer
..moveNext()
..expectList();
List<Diagnostic> diagnostics = [
for (; deserializer.moveNext();) deserializer.expectDiagnostic(),
];
deserializer
..moveNext()
..expectList();
@ -358,6 +369,7 @@ class MacroExecutionResultImpl implements MacroExecutionResult {
};
return new MacroExecutionResultImpl(
diagnostics: diagnostics,
enumValueAugmentations: enumValueAugmentations,
interfaceAugmentations: interfaceAugmentations,
libraryAugmentations: libraryAugmentations,
@ -369,6 +381,12 @@ class MacroExecutionResultImpl implements MacroExecutionResult {
@override
void serialize(Serializer serializer) {
serializer.startList();
for (Diagnostic diagnostic in diagnostics) {
diagnostic.serialize(serializer);
}
serializer.endList();
serializer.startList();
for (IdentifierImpl enuum in enumValueAugmentations.keys) {
enuum.serialize(serializer);

View file

@ -494,6 +494,38 @@ extension DeserializerExtensions on Deserializer {
if (checkNull()) return null;
return expectCode();
}
Diagnostic expectDiagnostic() {
expectList();
List<DiagnosticMessage> context = [
for (; moveNext();) expectDiagnosticMessage(),
];
String? correctionMessage = (this..moveNext()).expectNullableString();
DiagnosticMessage message = (this..moveNext()).expectDiagnosticMessage();
Severity severity = Severity.values[(this..moveNext()).expectInt()];
return new Diagnostic(message, severity,
contextMessages: context, correctionMessage: correctionMessage);
}
DiagnosticMessage expectDiagnosticMessage() {
String message = expectString();
moveNext();
RemoteInstance? target = checkNull() ? null : expectRemoteInstance();
return switch (target) {
null => new DiagnosticMessage(message),
DeclarationImpl() =>
new DiagnosticMessage(message, target: target.asDiagnosticTarget),
TypeAnnotationImpl() =>
new DiagnosticMessage(message, target: target.asDiagnosticTarget),
_ => throw new UnsupportedError(
'Unsupported target type ${target.runtimeType}, only Declarations '
'and TypeAnnotations are allowed.'),
};
}
}
extension SerializeNullable on Serializable? {
@ -626,6 +658,34 @@ extension SerializeCode on Code {
}
}
extension SerializeDiagnostic on Diagnostic {
void serialize(Serializer serializer) {
serializer.startList();
for (DiagnosticMessage message in contextMessages) {
message.serialize(serializer);
}
serializer.endList();
serializer.addNullableString(correctionMessage);
message.serialize(serializer);
serializer.addInt(severity.index);
}
}
extension SerializeDiagnosticMessage on DiagnosticMessage {
void serialize(Serializer serializer) {
serializer.addString(message);
switch (target) {
case null:
serializer.addNull();
case DeclarationDiagnosticTarget target:
(target.declaration as DeclarationImpl).serialize(serializer);
case TypeAnnotationDiagnosticTarget target:
(target.typeAnnotation as TypeAnnotationImpl).serialize(serializer);
}
}
}
extension Helpers on Serializer {
void addUri(Uri uri) => addString('$uri');

View file

@ -69,6 +69,7 @@ void main() {
var results = [
for (var i = 0; i < 2; i++)
MacroExecutionResultImpl(
diagnostics: [],
enumValueAugmentations: {},
interfaceAugmentations: {
for (var j = 0; j < 3; j++)
@ -181,6 +182,7 @@ void main() {
uri: Uri.parse('package:bar/bar.dart'));
var results = [
MacroExecutionResultImpl(
diagnostics: [],
enumValueAugmentations: {},
interfaceAugmentations: {},
mixinAugmentations: {},
@ -236,6 +238,7 @@ void main() {
test('can handle omitted type annotations', () {
var results = [
MacroExecutionResultImpl(
diagnostics: [],
enumValueAugmentations: {},
interfaceAugmentations: {},
mixinAugmentations: {},
@ -303,6 +306,7 @@ void main() {
uri: Uri.parse('package:bar/bar.dart'));
var results = [
MacroExecutionResultImpl(
diagnostics: [],
enumValueAugmentations: {},
interfaceAugmentations: {},
mixinAugmentations: {},
@ -399,7 +403,7 @@ void main() {
typeArguments: []));
var results = [
MacroExecutionResultImpl(enumValueAugmentations: {
MacroExecutionResultImpl(diagnostics: [], enumValueAugmentations: {
myEnum.identifier: [
DeclarationCode.fromParts(['a(1),\n']),
],
@ -461,6 +465,7 @@ void main() {
var results = [
MacroExecutionResultImpl(
diagnostics: [],
enumValueAugmentations: {},
typeAugmentations: {
clazz.identifier: [

View file

@ -5,6 +5,7 @@
import 'dart:io';
import 'dart:isolate';
import 'package:_fe_analyzer_shared/src/macros/api.dart';
import 'package:_fe_analyzer_shared/src/macros/bootstrap.dart';
import 'package:_fe_analyzer_shared/src/macros/executor.dart';
import 'package:_fe_analyzer_shared/src/macros/executor/protocol.dart';
@ -23,8 +24,10 @@ import '../util.dart';
void main() {
late MacroExecutor executor;
late File kernelOutputFile;
final macroName = 'SimpleMacro';
late MacroInstanceIdentifier instanceId;
final diagnosticMacroName = 'DiagnosticMacro';
final simpleMacroName = 'SimpleMacro';
late MacroInstanceIdentifier diagnosticMacroInstanceId;
late MacroInstanceIdentifier simpleMacroInstanceId;
late Uri macroUri;
late File simpleMacroFile;
late Directory tmpDir;
@ -45,7 +48,8 @@ void main() {
var bootstrapContent = bootstrapMacroIsolate({
macroUri.toString(): {
macroName: ['', 'named']
simpleMacroName: ['', 'named'],
diagnosticMacroName: [''],
}
}, mode);
var bootstrapFile =
@ -80,21 +84,21 @@ void main() {
: await processExecutor.start(mode,
CommunicationChannel.stdio, kernelOutputFile.path);
instanceId = await executor.instantiateMacro(
macroUri, macroName, '', Arguments([], {}));
expect(instanceId, isNotNull,
simpleMacroInstanceId = await executor.instantiateMacro(
macroUri, simpleMacroName, '', Arguments([], {}));
expect(simpleMacroInstanceId, isNotNull,
reason: 'Can create an instance with no arguments.');
executor.disposeMacro(instanceId);
executor.disposeMacro(simpleMacroInstanceId);
instanceId = await executor.instantiateMacro(
macroUri, macroName, '', Arguments([IntArgument(1)], {}));
expect(instanceId, isNotNull,
simpleMacroInstanceId = await executor.instantiateMacro(
macroUri, simpleMacroName, '', Arguments([IntArgument(1)], {}));
expect(simpleMacroInstanceId, isNotNull,
reason: 'Can create an instance with positional arguments.');
executor.disposeMacro(instanceId);
executor.disposeMacro(simpleMacroInstanceId);
instanceId = await executor.instantiateMacro(
simpleMacroInstanceId = await executor.instantiateMacro(
macroUri,
macroName,
simpleMacroName,
'named',
Arguments([], {
'myBool': BoolArgument(true),
@ -125,14 +129,19 @@ void main() {
]),
'myString': StringArgument('a'),
}));
expect(instanceId, isNotNull,
expect(simpleMacroInstanceId, isNotNull,
reason: 'Can create an instance with named arguments.');
diagnosticMacroInstanceId = await executor.instantiateMacro(
macroUri, diagnosticMacroName, '', Arguments([], {}));
expect(diagnosticMacroInstanceId, isNotNull);
});
tearDownAll(() async {
executor.disposeMacro(instanceId);
executor.disposeMacro(diagnosticMacroInstanceId);
executor.disposeMacro(simpleMacroInstanceId);
await expectLater(
() => executor.executeTypesPhase(instanceId,
() => executor.executeTypesPhase(simpleMacroInstanceId,
Fixtures.myFunction, TestTypePhaseIntrospector()),
throwsA(isA<RemoteException>().having((e) => e.error, 'error',
contains('Unrecognized macro instance'))),
@ -149,8 +158,10 @@ void main() {
group('run macros', () {
group('in the types phase', () {
test('on functions', () async {
var result = await executor.executeTypesPhase(instanceId,
Fixtures.myFunction, TestTypePhaseIntrospector());
var result = await executor.executeTypesPhase(
simpleMacroInstanceId,
Fixtures.myFunction,
TestTypePhaseIntrospector());
expect(result.enumValueAugmentations, isEmpty);
expect(result.interfaceAugmentations, isEmpty);
expect(result.mixinAugmentations, isEmpty);
@ -162,7 +173,9 @@ void main() {
test('on methods', () async {
var result = await executor.executeTypesPhase(
instanceId, Fixtures.myMethod, TestTypePhaseIntrospector());
simpleMacroInstanceId,
Fixtures.myMethod,
TestTypePhaseIntrospector());
expect(result.enumValueAugmentations, isEmpty);
expect(result.interfaceAugmentations, isEmpty);
expect(result.mixinAugmentations, isEmpty);
@ -173,8 +186,10 @@ void main() {
});
test('on getters', () async {
var result = await executor.executeTypesPhase(instanceId,
Fixtures.myVariableGetter, TestTypePhaseIntrospector());
var result = await executor.executeTypesPhase(
simpleMacroInstanceId,
Fixtures.myVariableGetter,
TestTypePhaseIntrospector());
expect(result.enumValueAugmentations, isEmpty);
expect(result.interfaceAugmentations, isEmpty);
expect(result.mixinAugmentations, isEmpty);
@ -186,8 +201,10 @@ void main() {
});
test('on setters', () async {
var result = await executor.executeTypesPhase(instanceId,
Fixtures.myVariableSetter, TestTypePhaseIntrospector());
var result = await executor.executeTypesPhase(
simpleMacroInstanceId,
Fixtures.myVariableSetter,
TestTypePhaseIntrospector());
expect(result.enumValueAugmentations, isEmpty);
expect(result.interfaceAugmentations, isEmpty);
expect(result.mixinAugmentations, isEmpty);
@ -199,8 +216,10 @@ void main() {
});
test('on variables', () async {
var result = await executor.executeTypesPhase(instanceId,
Fixtures.myVariable, TestTypePhaseIntrospector());
var result = await executor.executeTypesPhase(
simpleMacroInstanceId,
Fixtures.myVariable,
TestTypePhaseIntrospector());
expect(result.enumValueAugmentations, isEmpty);
expect(result.interfaceAugmentations, isEmpty);
expect(result.mixinAugmentations, isEmpty);
@ -212,8 +231,10 @@ void main() {
});
test('on constructors', () async {
var result = await executor.executeTypesPhase(instanceId,
Fixtures.myConstructor, TestTypePhaseIntrospector());
var result = await executor.executeTypesPhase(
simpleMacroInstanceId,
Fixtures.myConstructor,
TestTypePhaseIntrospector());
expect(result.enumValueAugmentations, isEmpty);
expect(result.interfaceAugmentations, isEmpty);
expect(result.mixinAugmentations, isEmpty);
@ -226,7 +247,9 @@ void main() {
test('on fields', () async {
var result = await executor.executeTypesPhase(
instanceId, Fixtures.myField, TestTypePhaseIntrospector());
simpleMacroInstanceId,
Fixtures.myField,
TestTypePhaseIntrospector());
expect(result.enumValueAugmentations, isEmpty);
expect(result.interfaceAugmentations, isEmpty);
expect(result.mixinAugmentations, isEmpty);
@ -238,7 +261,9 @@ void main() {
test('on classes', () async {
var result = await executor.executeTypesPhase(
instanceId, Fixtures.myClass, TestTypePhaseIntrospector());
simpleMacroInstanceId,
Fixtures.myClass,
TestTypePhaseIntrospector());
expect(result.enumValueAugmentations, isEmpty);
expect(
result.interfaceAugmentations.mapValuesToDebugCodeString(),
@ -269,7 +294,9 @@ void main() {
test('on enums', () async {
var result = await executor.executeTypesPhase(
instanceId, Fixtures.myEnum, TestTypePhaseIntrospector());
simpleMacroInstanceId,
Fixtures.myEnum,
TestTypePhaseIntrospector());
expect(result.enumValueAugmentations, isEmpty);
expect(result.interfaceAugmentations, isEmpty);
expect(result.mixinAugmentations, isEmpty);
@ -280,8 +307,10 @@ void main() {
});
test('on enum values', () async {
var result = await executor.executeTypesPhase(instanceId,
Fixtures.myEnumValues.first, TestTypePhaseIntrospector());
var result = await executor.executeTypesPhase(
simpleMacroInstanceId,
Fixtures.myEnumValues.first,
TestTypePhaseIntrospector());
expect(result.enumValueAugmentations, isEmpty);
expect(result.interfaceAugmentations, isEmpty);
expect(result.mixinAugmentations, isEmpty);
@ -292,8 +321,10 @@ void main() {
});
test('on extensions', () async {
var result = await executor.executeTypesPhase(instanceId,
Fixtures.myExtension, TestTypePhaseIntrospector());
var result = await executor.executeTypesPhase(
simpleMacroInstanceId,
Fixtures.myExtension,
TestTypePhaseIntrospector());
expect(result.enumValueAugmentations, isEmpty);
expect(result.typeAugmentations, isEmpty);
expect(
@ -303,7 +334,9 @@ void main() {
test('on mixins', () async {
var result = await executor.executeTypesPhase(
instanceId, Fixtures.myMixin, TestTypePhaseIntrospector());
simpleMacroInstanceId,
Fixtures.myMixin,
TestTypePhaseIntrospector());
expect(result.enumValueAugmentations, isEmpty);
expect(result.interfaceAugmentations, isEmpty);
expect(result.mixinAugmentations, isEmpty);
@ -316,7 +349,7 @@ void main() {
test('on libraries', () async {
var result = await executor.executeTypesPhase(
instanceId,
simpleMacroInstanceId,
Fixtures.library,
TestTypePhaseIntrospector(),
);
@ -341,7 +374,7 @@ class LibraryInfo {
group('in the declaration phase', () {
test('on functions', () async {
var result = await executor.executeDeclarationsPhase(
instanceId,
simpleMacroInstanceId,
Fixtures.myFunction,
Fixtures.testDeclarationPhaseIntrospector);
expect(result.enumValueAugmentations, isEmpty);
@ -356,7 +389,7 @@ class LibraryInfo {
test('on methods', () async {
var result = await executor.executeDeclarationsPhase(
instanceId,
simpleMacroInstanceId,
Fixtures.myMethod,
Fixtures.testDeclarationPhaseIntrospector);
expect(result.enumValueAugmentations, isEmpty);
@ -372,7 +405,7 @@ class LibraryInfo {
test('on constructors', () async {
var result = await executor.executeDeclarationsPhase(
instanceId,
simpleMacroInstanceId,
Fixtures.myConstructor,
Fixtures.testDeclarationPhaseIntrospector);
expect(result.enumValueAugmentations, isEmpty);
@ -393,7 +426,7 @@ class LibraryInfo {
test('on getters', () async {
var result = await executor.executeDeclarationsPhase(
instanceId,
simpleMacroInstanceId,
Fixtures.myVariableGetter,
Fixtures.testDeclarationPhaseIntrospector);
expect(result.enumValueAugmentations, isEmpty);
@ -408,7 +441,7 @@ class LibraryInfo {
test('on setters', () async {
var result = await executor.executeDeclarationsPhase(
instanceId,
simpleMacroInstanceId,
Fixtures.myVariableSetter,
Fixtures.testDeclarationPhaseIntrospector);
expect(result.enumValueAugmentations, isEmpty);
@ -423,7 +456,7 @@ class LibraryInfo {
test('on variables', () async {
var result = await executor.executeDeclarationsPhase(
instanceId,
simpleMacroInstanceId,
Fixtures.myVariable,
Fixtures.testDeclarationPhaseIntrospector);
expect(result.enumValueAugmentations, isEmpty);
@ -438,7 +471,7 @@ class LibraryInfo {
test('on fields', () async {
var result = await executor.executeDeclarationsPhase(
instanceId,
simpleMacroInstanceId,
Fixtures.myField,
Fixtures.testDeclarationPhaseIntrospector);
expect(result.enumValueAugmentations, isEmpty);
@ -458,7 +491,7 @@ class LibraryInfo {
test('on classes', () async {
var result = await executor.executeDeclarationsPhase(
instanceId,
simpleMacroInstanceId,
Fixtures.myClass,
Fixtures.testDeclarationPhaseIntrospector);
expect(result.enumValueAugmentations, isEmpty);
@ -476,8 +509,10 @@ class LibraryInfo {
});
test('on enums', () async {
var result = await executor.executeDeclarationsPhase(instanceId,
Fixtures.myEnum, Fixtures.testDeclarationPhaseIntrospector);
var result = await executor.executeDeclarationsPhase(
simpleMacroInstanceId,
Fixtures.myEnum,
Fixtures.testDeclarationPhaseIntrospector);
expect(result.enumValueAugmentations, isEmpty);
expect(result.interfaceAugmentations, isEmpty);
expect(result.mixinAugmentations, isEmpty);
@ -494,7 +529,7 @@ class LibraryInfo {
test('on enum values', () async {
var result = await executor.executeDeclarationsPhase(
instanceId,
simpleMacroInstanceId,
Fixtures.myEnumValues.first,
Fixtures.testDeclarationPhaseIntrospector);
expect(result.enumValueAugmentations, isEmpty);
@ -513,7 +548,7 @@ class LibraryInfo {
test('on extensions', () async {
var result = await executor.executeDeclarationsPhase(
instanceId,
simpleMacroInstanceId,
Fixtures.myExtension,
Fixtures.testDeclarationPhaseIntrospector);
expect(result.enumValueAugmentations, isEmpty);
@ -531,7 +566,7 @@ class LibraryInfo {
test('on mixins', () async {
var result = await executor.executeDeclarationsPhase(
instanceId,
simpleMacroInstanceId,
Fixtures.myMixin,
Fixtures.testDeclarationPhaseIntrospector);
expect(result.enumValueAugmentations, isEmpty);
@ -551,7 +586,7 @@ class LibraryInfo {
test('on libraries', () async {
var result = await executor.executeDeclarationsPhase(
instanceId,
simpleMacroInstanceId,
Fixtures.library,
Fixtures.testDeclarationPhaseIntrospector);
expect(result.enumValueAugmentations, isEmpty);
@ -568,7 +603,7 @@ class LibraryInfo {
group('in the definition phase', () {
test('on functions', () async {
var result = await executor.executeDefinitionsPhase(
instanceId,
simpleMacroInstanceId,
Fixtures.myFunction,
Fixtures.testDefinitionPhaseIntrospector);
expect(result.enumValueAugmentations, isEmpty);
@ -590,7 +625,7 @@ class LibraryInfo {
test('on methods', () async {
var definitionResult = await executor.executeDefinitionsPhase(
instanceId,
simpleMacroInstanceId,
Fixtures.myMethod,
Fixtures.testDefinitionPhaseIntrospector);
expect(definitionResult.enumValueAugmentations, isEmpty);
@ -607,7 +642,7 @@ class LibraryInfo {
test('on constructors', () async {
var definitionResult = await executor.executeDefinitionsPhase(
instanceId,
simpleMacroInstanceId,
Fixtures.myConstructor,
Fixtures.testDefinitionPhaseIntrospector);
expect(definitionResult.enumValueAugmentations, isEmpty);
@ -626,7 +661,7 @@ class LibraryInfo {
test('on getters', () async {
var result = await executor.executeDefinitionsPhase(
instanceId,
simpleMacroInstanceId,
Fixtures.myVariableGetter,
Fixtures.testDefinitionPhaseIntrospector);
expect(result.enumValueAugmentations, isEmpty);
@ -648,7 +683,7 @@ class LibraryInfo {
test('on setters', () async {
var result = await executor.executeDefinitionsPhase(
instanceId,
simpleMacroInstanceId,
Fixtures.myVariableSetter,
Fixtures.testDefinitionPhaseIntrospector);
expect(result.enumValueAugmentations, isEmpty);
@ -671,7 +706,7 @@ class LibraryInfo {
test('on variables', () async {
var result = await executor.executeDefinitionsPhase(
instanceId,
simpleMacroInstanceId,
Fixtures.myVariable,
Fixtures.testDefinitionPhaseIntrospector);
expect(result.enumValueAugmentations, isEmpty);
@ -701,7 +736,7 @@ class LibraryInfo {
test('on fields', () async {
var definitionResult = await executor.executeDefinitionsPhase(
instanceId,
simpleMacroInstanceId,
Fixtures.myField,
Fixtures.testDefinitionPhaseIntrospector);
expect(definitionResult.enumValueAugmentations, isEmpty);
@ -718,7 +753,7 @@ class LibraryInfo {
test('on classes', () async {
var definitionResult = await executor.executeDefinitionsPhase(
instanceId,
simpleMacroInstanceId,
Fixtures.myClass,
Fixtures.testDefinitionPhaseIntrospector);
expect(definitionResult.enumValueAugmentations, isEmpty);
@ -739,7 +774,7 @@ class LibraryInfo {
test('on enums', () async {
var definitionResult = await executor.executeDefinitionsPhase(
instanceId,
simpleMacroInstanceId,
Fixtures.myEnum,
Fixtures.testDefinitionPhaseIntrospector);
expect(definitionResult.enumValueAugmentations, hasLength(1));
@ -775,7 +810,7 @@ class LibraryInfo {
test('on enum values', () async {
var definitionResult = await executor.executeDefinitionsPhase(
instanceId,
simpleMacroInstanceId,
Fixtures.myEnumValues.first,
Fixtures.testDefinitionPhaseIntrospector);
expect(definitionResult.enumValueAugmentations, hasLength(1));
@ -791,7 +826,7 @@ class LibraryInfo {
test('on extensions', () async {
var definitionResult = await executor.executeDefinitionsPhase(
instanceId,
simpleMacroInstanceId,
Fixtures.myExtension,
Fixtures.testDefinitionPhaseIntrospector);
expect(definitionResult.enumValueAugmentations, isEmpty);
@ -809,7 +844,7 @@ class LibraryInfo {
test('on mixins', () async {
var definitionResult = await executor.executeDefinitionsPhase(
instanceId,
simpleMacroInstanceId,
Fixtures.myMixin,
Fixtures.testDefinitionPhaseIntrospector);
expect(definitionResult.enumValueAugmentations, isEmpty);
@ -827,8 +862,10 @@ class LibraryInfo {
});
test('on libraries', () async {
var result = await executor.executeDefinitionsPhase(instanceId,
Fixtures.library, Fixtures.testDefinitionPhaseIntrospector);
var result = await executor.executeDefinitionsPhase(
simpleMacroInstanceId,
Fixtures.library,
Fixtures.testDefinitionPhaseIntrospector);
expect(result.enumValueAugmentations, isEmpty);
expect(result.interfaceAugmentations, isEmpty);
expect(result.mixinAugmentations, isEmpty);
@ -840,6 +877,32 @@ augment final LibraryInfo library = LibraryInfo(Uri.parse('package:foo/bar.dart'
'''));
});
});
test('and report diagnostics', () async {
final result = await executor.executeTypesPhase(
diagnosticMacroInstanceId,
Fixtures.myClass,
TestTypePhaseIntrospector());
expect(result.diagnostics, [
predicate<Diagnostic>((d) =>
d.severity == Severity.info &&
d.message.message == 'superclass' &&
(d.message.target as TypeAnnotationDiagnosticTarget)
.typeAnnotation ==
Fixtures.mySuperclassType &&
d.contextMessages.single.message == 'interface' &&
(d.contextMessages.single.target
as TypeAnnotationDiagnosticTarget)
.typeAnnotation ==
Fixtures.myInterfaceType &&
d.correctionMessage == 'correct me!'),
predicate<Diagnostic>((d) =>
d.severity == Severity.error &&
d.message.message.contains('I threw an error!') &&
// Quick test that some stack trace also appears
d.message.message.contains('simple_macro.dart')),
]);
});
});
});
}

View file

@ -6,6 +6,28 @@ import 'dart:async';
import 'package:_fe_analyzer_shared/src/macros/api.dart';
/// A macro for testing diagnostics reporting, including error handling.
class DiagnosticMacro implements ClassTypesMacro {
@override
FutureOr<void> buildTypesForClass(
ClassDeclaration clazz, ClassTypeBuilder builder) {
builder.report(Diagnostic(
DiagnosticMessage('superclass',
target: clazz.superclass!.asDiagnosticTarget),
Severity.info,
contextMessages: [
DiagnosticMessage(
'interface',
target: clazz.interfaces.single.asDiagnosticTarget,
),
],
correctionMessage: 'correct me!'));
// Test general error handling also
throw 'I threw an error!';
}
}
/// A very simple macro that augments any declaration it is given, usually
/// adding print statements and inlining values from the declaration object
/// for comparison with expected values in tests.

View file

@ -61,6 +61,7 @@ void f(A_Macro a) {}
''');
}
@FailingTest(reason: 'Fails because exceptions are reported as diagnostics')
test_macroExecutionException_compileTimeError() async {
newFile('$testPackageLibPath/a.dart', r'''
import 'package:_fe_analyzer_shared/src/macros/api.dart';
@ -82,6 +83,7 @@ class A {}
''', [error(CompileTimeErrorCode.MACRO_EXECUTION_EXCEPTION, 18, 10)]);
}
@FailingTest(reason: 'Fails because exceptions are reported as diagnostics')
test_macroExecutionException_throwsException() async {
newFile('$testPackageLibPath/a.dart', r'''
import 'package:_fe_analyzer_shared/src/macros/api.dart';

View file

@ -354,6 +354,9 @@ class _MacroInstanceIdentifier implements MacroInstanceIdentifier {
}
class _MacroExecutionResult implements MacroExecutionResult {
@override
List<Diagnostic> diagnostics = [];
@override
Map<Identifier, Iterable<DeclarationCode>> enumValueAugmentations = const {};

View file

@ -92,6 +92,7 @@ atm
atom
atoms
atop
attaches
attributes
augment
augmentation
@ -325,6 +326,7 @@ cr
creator
creators
criterion
critical
cross
cruft
cryptic
@ -657,6 +659,7 @@ growth
gt
guarantee
guarded
guideline
guides
gypi
gz
@ -705,6 +708,7 @@ i'll
i'm
ic
id
ide
ideal
identifies
identifying
@ -1112,6 +1116,7 @@ pkg
play
player
plugin
plugins
pm
pn
pointed
@ -1402,6 +1407,7 @@ setable
setable
setables
setaf
severities
sh
sha
shadowed
@ -1428,6 +1434,8 @@ sides
sigmund
signaling
significant
silenceable
silenced
simplifier
simplify
singleton
@ -1558,6 +1566,7 @@ superinterfaces
supernode
supers
suppose
surfaced
surprising
surrounded
surrounds
@ -1639,6 +1648,7 @@ toplevel
topological
tops
tput
traceable
traced
tracker
traditional
@ -1674,6 +1684,7 @@ unaffected
unaligned
unaltered
unavailable
unawaited
unbound
uncached
uncategorized