mirror of
https://github.com/dart-lang/sdk
synced 2024-09-16 00:19:48 +00:00
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:
parent
6a2c9cb826
commit
abc7a21679
|
@ -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';
|
||||
|
|
|
@ -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).
|
||||
|
|
96
pkg/_fe_analyzer_shared/lib/src/macros/api/diagnostic.dart
Normal file
96
pkg/_fe_analyzer_shared/lib/src/macros/api/diagnostic.dart
Normal 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,
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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');
|
||||
|
||||
|
|
|
@ -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: [
|
||||
|
|
|
@ -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')),
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -354,6 +354,9 @@ class _MacroInstanceIdentifier implements MacroInstanceIdentifier {
|
|||
}
|
||||
|
||||
class _MacroExecutionResult implements MacroExecutionResult {
|
||||
@override
|
||||
List<Diagnostic> diagnostics = [];
|
||||
|
||||
@override
|
||||
Map<Identifier, Iterable<DeclarationCode>> enumValueAugmentations = const {};
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue