Macro. Support for mixins.

Change-Id: Icab52054c230ab97c3129b0bb32ed3ad55740fbc
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/333046
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Reviewed-by: Phil Quitslund <pquitslund@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
This commit is contained in:
Konstantin Shcheglov 2023-10-31 23:54:49 +00:00 committed by Commit Queue
parent 718f9076bd
commit 0544ecba9a
7 changed files with 530 additions and 166 deletions

View file

@ -8,7 +8,6 @@ import 'package:_fe_analyzer_shared/src/macros/executor/multi_executor.dart';
import 'package:_fe_analyzer_shared/src/macros/executor/protocol.dart' as macro;
import 'package:analyzer/dart/ast/ast.dart' as ast;
import 'package:analyzer/dart/ast/token.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/nullability_suffix.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/src/dart/ast/ast.dart' as ast;
@ -91,15 +90,27 @@ class LibraryMacroApplier {
for (final declaration in unit.declarations.reversed) {
switch (declaration) {
case ast.ClassDeclaration():
final element = declaration.declaredElement!;
final element = declaration.declaredElement;
element as ClassElementImpl;
await _addClassLike(
container: container,
targetElement: element.declarationElement as MacroTargetElement,
targetElement: element.declarationElement,
classNode: declaration,
classDeclarationKind: macro.DeclarationKind.classType,
classAnnotations: declaration.metadata,
members: declaration.members,
);
case ast.MixinDeclaration():
final element = declaration.declaredElement;
element as MixinElementImpl;
await _addClassLike(
container: container,
targetElement: element.declarationElement,
classNode: declaration,
classDeclarationKind: macro.DeclarationKind.mixinType,
classAnnotations: declaration.metadata,
members: declaration.members,
);
}
}
}
@ -285,6 +296,8 @@ class LibraryMacroApplier {
return fromNode.classDeclaration(targetNode);
case ast.MethodDeclaration():
return fromNode.methodDeclaration(targetNode);
case ast.MixinDeclaration():
return fromNode.mixinDeclaration(targetNode);
default:
// TODO(scheglov) incomplete
throw UnimplementedError('${targetNode.runtimeType}');
@ -395,6 +408,8 @@ class LibraryMacroApplier {
final element = (identifier as IdentifierImpl).element;
if (element is ClassElementImpl) {
return declarationBuilder.fromElement.classElement(element);
} else if (element is MixinElementImpl) {
return declarationBuilder.fromElement.mixinElement(element);
} else {
throw ArgumentError('element: $element');
}
@ -583,13 +598,19 @@ class _DeclarationPhaseIntrospector extends _TypePhaseIntrospector
Future<List<macro.FieldDeclaration>> fieldsOf(
covariant macro.IntrospectableType type,
) async {
if (type is! IntrospectableClassDeclarationImpl) {
throw UnsupportedError('Only introspection on classes is supported');
switch (type) {
case IntrospectableClassDeclarationImpl():
return type.element.fields
.where((e) => !e.isSynthetic)
.map(declarationBuilder.fromElement.fieldElement)
.toList();
case IntrospectableMixinDeclarationImpl():
return type.element.fields
.where((e) => !e.isSynthetic)
.map(declarationBuilder.fromElement.fieldElement)
.toList();
}
return type.element.fields
.where((e) => !e.isSynthetic)
.map(declarationBuilder.fromElement.fieldElement)
.toList();
throw UnsupportedError('Only introspection on classes is supported');
}
@override
@ -716,7 +737,7 @@ extension on macro.MacroExecutionResult {
typeAugmentations.isNotEmpty;
}
extension<T extends InstanceElement> on T {
extension<T extends InstanceElementImpl> on T {
T get declarationElement {
switch (augmented) {
case T(:final T declaration):

View file

@ -117,7 +117,8 @@ class UnknownMacroApplicationError extends MacroApplicationError {
@override
String toStringForTest() {
return 'Unknown(annotation: $annotationIndex, message: $message)';
return 'Unknown(annotation: $annotationIndex, '
'message: $message, stackTrace: $stackTrace)';
}
@override

View file

@ -61,6 +61,9 @@ class DeclarationBuilderFromElement {
final Map<ClassElement, IntrospectableClassDeclarationImpl> _classMap =
Map.identity();
final Map<MixinElement, IntrospectableMixinDeclarationImpl> _mixinMap =
Map.identity();
final Map<FieldElement, FieldDeclarationImpl> _fieldMap = Map.identity();
final Map<ExecutableElement, MethodDeclarationImpl> _methodMap =
@ -108,6 +111,10 @@ class DeclarationBuilderFromElement {
return _methodMap[element] ??= _methodElement(element);
}
macro.IntrospectableMixinDeclarationImpl mixinElement(MixinElement element) {
return _mixinMap[element] ??= _introspectableMixinElement(element);
}
macro.TypeParameterDeclarationImpl typeParameter(
TypeParameterElement element,
) {
@ -146,7 +153,7 @@ class DeclarationBuilderFromElement {
}
FieldDeclarationImpl _fieldElement(FieldElement element) {
final enclosingClass = element.enclosingElement as ClassElement;
final enclosingElement = element.enclosingElement;
return FieldDeclarationImpl(
id: macro.RemoteInstance.uniqueId,
identifier: identifier(element),
@ -156,7 +163,7 @@ class DeclarationBuilderFromElement {
hasFinal: element.isFinal,
hasLate: element.isLate,
type: _dartType(element.type),
definingType: identifier(enclosingClass),
definingType: identifier(enclosingElement),
isStatic: element.isStatic,
);
}
@ -192,6 +199,22 @@ class DeclarationBuilderFromElement {
);
}
IntrospectableMixinDeclarationImpl _introspectableMixinElement(
MixinElement element) {
return IntrospectableMixinDeclarationImpl._(
id: macro.RemoteInstance.uniqueId,
identifier: identifier(element),
library: library(element),
metadata: _buildMetadata(element),
typeParameters: element.typeParameters.map(_typeParameter).toList(),
hasBase: element.isBase,
interfaces: element.interfaces.map(_interfaceType).toList(),
superclassConstraints:
element.superclassConstraints.map(_interfaceType).toList(),
element: element,
);
}
MethodDeclarationImpl _methodElement(MethodElement element) {
final enclosingClass = element.enclosingElement as ClassElement;
return MethodDeclarationImpl._(
@ -278,6 +301,12 @@ class DeclarationBuilderFromNode {
return _methodDeclaration(node);
}
macro.MixinDeclarationImpl mixinDeclaration(
ast.MixinDeclaration node,
) {
return _introspectableMixinDeclaration(node);
}
List<macro.MetadataAnnotationImpl> _buildMetadata(
List<ast.Annotation> elements,
) {
@ -294,6 +323,23 @@ class DeclarationBuilderFromNode {
);
}
macro.IdentifierImpl _definingType(ast.AstNode node) {
final parentNode = node.parent;
switch (parentNode) {
case ast.ClassDeclaration():
final parentElement = parentNode.declaredElement!;
final typeElement = parentElement.augmentationTarget ?? parentElement;
return _declaredIdentifier(parentNode.name, typeElement);
case ast.MixinDeclaration():
final parentElement = parentNode.declaredElement!;
final typeElement = parentElement.augmentationTarget ?? parentElement;
return _declaredIdentifier(parentNode.name, typeElement);
default:
// TODO(scheglov) other parents
throw UnimplementedError('(${parentNode.runtimeType}) $parentNode');
}
}
macro.ParameterDeclarationImpl _formalParameter(ast.FormalParameter node) {
if (node is ast.DefaultFormalParameter) {
node = node.parameter;
@ -385,15 +431,45 @@ class DeclarationBuilderFromNode {
);
}
IntrospectableMixinDeclarationImpl _introspectableMixinDeclaration(
ast.MixinDeclaration node,
) {
final element = node.declaredElement as MixinElementImpl;
final onNodes = <ast.NamedType>[];
final interfaceNodes = <ast.NamedType>[];
for (var current = node;;) {
if (current.onClause case final clause?) {
onNodes.addAll(clause.superclassConstraints);
}
if (current.implementsClause case final clause?) {
interfaceNodes.addAll(clause.interfaces);
}
final nextElement = current.declaredElement?.augmentation;
final nextNode = declarationBuilder.nodeOfElement(nextElement);
if (nextNode is! ast.MixinDeclaration) {
break;
}
current = nextNode;
}
return IntrospectableMixinDeclarationImpl._(
id: macro.RemoteInstance.uniqueId,
identifier: _declaredIdentifier(node.name, element),
library: library(element),
metadata: _buildMetadata(node.metadata),
typeParameters: _typeParameters(node.typeParameters),
hasBase: node.baseKeyword != null,
interfaces: _namedTypes(interfaceNodes),
superclassConstraints: _namedTypes(onNodes),
element: element,
);
}
MethodDeclarationImpl _methodDeclaration(
ast.MethodDeclaration node,
) {
// TODO(scheglov) other parents
final parentNode = node.parent as ast.ClassDeclaration;
final parentElement = parentNode.declaredElement!;
final typeElement = parentElement.augmentationTarget ?? parentElement;
final definingType = _declaredIdentifier(parentNode.name, typeElement);
final definingType = _definingType(node);
final element = node.declaredElement!;
return MethodDeclarationImpl._(
@ -591,6 +667,23 @@ class IntrospectableClassDeclarationImpl
});
}
class IntrospectableMixinDeclarationImpl
extends macro.IntrospectableMixinDeclarationImpl {
final MixinElement element;
IntrospectableMixinDeclarationImpl._({
required super.id,
required super.identifier,
required super.library,
required super.metadata,
required super.typeParameters,
required super.hasBase,
required super.interfaces,
required super.superclassConstraints,
required this.element,
});
}
abstract class LibraryImpl extends macro.LibraryImpl {
LibraryImpl({
required super.id,

View file

@ -0,0 +1,46 @@
// 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.
import 'package:_fe_analyzer_shared/src/macros/api.dart';
Future<NamedTypeAnnotationCode> _codeA(TypePhaseIntrospector builder) async {
return NamedTypeAnnotationCode(
// ignore:deprecated_member_use
name: await builder.resolveIdentifier(
Uri.parse('package:test/append.dart'),
'A',
),
);
}
class A {}
/*macro*/ class AppendInterfaceA implements ClassTypesMacro, MixinTypesMacro {
const AppendInterfaceA();
@override
buildTypesForClass(clazz, builder) async {
builder.appendInterfaces([
await _codeA(builder),
]);
}
@override
buildTypesForMixin(clazz, builder) async {
builder.appendInterfaces([
await _codeA(builder),
]);
}
}
/*macro*/ class AppendMixinA implements ClassTypesMacro {
const AppendMixinA();
@override
buildTypesForClass(clazz, builder) async {
builder.appendMixins([
await _codeA(builder),
]);
}
}

View file

@ -9,7 +9,7 @@ import 'package:_fe_analyzer_shared/src/macros/api.dart';
import 'introspect_shared.dart';
/*macro*/ class DeclarationTextMacro
implements ClassTypesMacro, MethodTypesMacro {
implements ClassTypesMacro, MethodTypesMacro, MixinTypesMacro {
const DeclarationTextMacro();
@override
@ -55,6 +55,28 @@ import 'introspect_shared.dart';
),
);
}
@override
Future<void> buildTypesForMixin(declaration, builder) async {
final buffer = StringBuffer();
final sink = TreeStringSink(
sink: buffer,
indent: '',
);
final printer = _DeclarationPrinter(
sink: sink,
);
await printer.writeMixinDeclaration(declaration);
final text = buffer.toString();
builder.declareType(
'x',
DeclarationCode.fromString(
'const x = r"""$text""";',
),
);
}
}
class _DeclarationPrinter {
@ -106,6 +128,23 @@ class _DeclarationPrinter {
});
}
Future<void> writeMixinDeclaration(MixinDeclaration e) async {
sink.writelnWithIndent('mixin ${e.identifier.name}');
await sink.withIndent(() async {
await sink.writeFlags({
'hasBase': e.hasBase,
});
await _writeTypeParameters(e.typeParameters);
await _writeTypeAnnotations(
'superclassConstraints',
e.superclassConstraints,
);
await _writeTypeAnnotations('interfaces', e.interfaces);
});
}
Future<void> _writeFormalParameter(ParameterDeclaration e) async {
sink.writelnWithIndent(e.identifier.name);
await sink.withIndent(() async {

View file

@ -9,7 +9,7 @@ import 'package:_fe_analyzer_shared/src/macros/api.dart';
import 'introspect_shared.dart';
/*macro*/ class IntrospectDeclarationsPhaseMacro
implements ClassDeclarationsMacro {
implements ClassDeclarationsMacro, MixinDeclarationsMacro {
final Set<Object?> withDetailsFor;
const IntrospectDeclarationsPhaseMacro({
@ -42,6 +42,33 @@ import 'introspect_shared.dart';
),
);
}
@override
Future<void> buildDeclarationsForMixin(
IntrospectableMixinDeclaration declaration,
MemberDeclarationBuilder builder,
) async {
final buffer = StringBuffer();
final sink = TreeStringSink(
sink: buffer,
indent: '',
);
final printer = _DeclarationPrinter(
sink: sink,
withDetailsFor: withDetailsFor.cast(),
declarationPhaseIntrospector: builder,
);
await printer.writeMixinDeclaration(declaration);
final text = buffer.toString();
final resultName = 'introspect_${declaration.identifier.name}';
builder.declareInLibrary(
DeclarationCode.fromString(
'const $resultName = r"""$text""";',
),
);
}
}
class _DeclarationPrinter {
@ -104,6 +131,34 @@ class _DeclarationPrinter {
});
}
Future<void> writeMixinDeclaration(IntrospectableMixinDeclaration e) async {
sink.writelnWithIndent('mixin ${e.identifier.name}');
if (!_shouldWriteDetailsFor(e)) {
return;
}
await sink.withIndent(() async {
await sink.writeFlags({
'hasBase': e.hasBase,
});
await _writeTypeParameters(e.typeParameters);
await _writeTypeAnnotations(
'superclassConstraints',
e.superclassConstraints,
);
await _writeTypeAnnotations('interfaces', e.interfaces);
_enclosingDeclarationIdentifier = e.identifier;
await sink.writeElements<FieldDeclaration>(
'fields',
await declarationPhaseIntrospector.fieldsOf(e),
_writeField,
);
});
}
void _assertEnclosingClass(MemberDeclaration e) {
if (e.definingType != _enclosingDeclarationIdentifier) {
throw StateError('Mismatch: definingClass');

View file

@ -10,6 +10,7 @@ import 'package:_fe_analyzer_shared/src/macros/executor/serialization.dart'
as macro;
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/visitor.dart';
import 'package:analyzer/file_system/file_system.dart';
import 'package:analyzer/file_system/physical_file_system.dart';
import 'package:analyzer/src/dart/element/element.dart';
@ -610,12 +611,11 @@ import 'arguments_text.dart';
class A {}
''');
final A = library.definingCompilationUnit.getClass('A')!;
if (expectedErrors != null) {
expect(A.errorsStrForClassElement, expectedErrors);
expect(library.macroErrorsStr, expectedErrors);
return;
} else {
A.assertNoErrorsForClassElement();
library.assertNoMacroErrors();
}
if (expected != null) {
@ -641,6 +641,13 @@ class MacroDeclarationsIntrospectTest extends MacroElementsBaseTest {
@override
bool get keepLinkingLibraries => true;
String get _appendMacrosCode {
var code = MacrosEnvironment.instance.packageAnalyzerFolder
.getChildAssumingFile('test/src/summary/macro/append.dart')
.readAsStringSync();
return code.replaceAll('/*macro*/', 'macro');
}
/// Return the code for `IntrospectDeclarationsPhaseMacro`.
String get _introspectDeclarationsPhaseCode {
final path = 'test/src/summary/macro/introspect_declarations_phase.dart';
@ -651,36 +658,13 @@ class MacroDeclarationsIntrospectTest extends MacroElementsBaseTest {
}
test_class_appendInterfaces() async {
newFile('$testPackageLibPath/a.dart', r'''
class A {}
''');
newFile('$testPackageLibPath/b.dart', r'''
import 'dart:async';
import 'package:_fe_analyzer_shared/src/macros/api.dart';
import 'a.dart';
macro class AddInterfaceA implements ClassTypesMacro {
const AddInterfaceA();
FutureOr<void> buildTypesForClass(clazz, builder) async {
builder.appendInterfaces([
NamedTypeAnnotationCode(
name: await builder.resolveIdentifier(
Uri.parse('package:test/a.dart'),
'A',
),
),
]);
}
}
''');
_newAppendMacrosFile();
await _assertIntrospectDeclarationsText(r'''
import 'b.dart';
import 'append.dart';
@AddInterfaceA()
@IntrospectDeclarationsPhaseMacro()
@AppendInterfaceA()
class X {}
''', r'''
class X
@ -690,36 +674,13 @@ class X
}
test_class_appendMixins() async {
newFile('$testPackageLibPath/a.dart', r'''
mixin A {}
''');
newFile('$testPackageLibPath/b.dart', r'''
import 'dart:async';
import 'package:_fe_analyzer_shared/src/macros/api.dart';
import 'a.dart';
macro class AddInterfaceA implements ClassTypesMacro {
const AddInterfaceA();
FutureOr<void> buildTypesForClass(clazz, builder) async {
builder.appendMixins([
NamedTypeAnnotationCode(
name: await builder.resolveIdentifier(
Uri.parse('package:test/a.dart'),
'A',
),
),
]);
}
}
''');
_newAppendMacrosFile();
await _assertIntrospectDeclarationsText(r'''
import 'b.dart';
import 'append.dart';
@AddInterfaceA()
@IntrospectDeclarationsPhaseMacro()
@AppendMixinA()
class X {}
''', r'''
class X
@ -728,6 +689,106 @@ class X
''');
}
test_class_fieldDeclaration_isExternal() async {
await _assertIntrospectDeclarationsText(r'''
@IntrospectDeclarationsPhaseMacro(
withDetailsFor: {'X'},
)
class X {
external int a;
int b = 0;
}
''', r'''
class X
fields
a
flags: hasExternal
type: int
b
type: int
''');
}
test_class_fieldDeclaration_isFinal() async {
await _assertIntrospectDeclarationsText(r'''
@IntrospectDeclarationsPhaseMacro(
withDetailsFor: {'X'},
)
class X {
final int a = 0;
int b = 0;
}
''', r'''
class X
fields
a
flags: hasFinal
type: int
b
type: int
''');
}
test_class_fieldDeclaration_isLate() async {
await _assertIntrospectDeclarationsText(r'''
@IntrospectDeclarationsPhaseMacro(
withDetailsFor: {'X'},
)
class X {
late final int a;
final int b = 0;
}
''', r'''
class X
fields
a
flags: hasFinal hasLate
type: int
b
flags: hasFinal
type: int
''');
}
test_class_fieldDeclaration_isStatic() async {
await _assertIntrospectDeclarationsText(r'''
@IntrospectDeclarationsPhaseMacro(
withDetailsFor: {'X'},
)
class X {
static int a = 0;
int b = 0;
}
''', r'''
class X
fields
a
flags: isStatic
type: int
b
type: int
''');
}
test_class_fieldDeclaration_type_explicit() async {
await _assertIntrospectDeclarationsText(r'''
@IntrospectDeclarationsPhaseMacro(
withDetailsFor: {'X'},
)
class X {
int a = 0;
List<String> b = [];
}
''', r'''
class X
fields
a
type: int
b
type: List<String>
''');
}
test_classDeclaration_imported_interfaces() async {
newFile('$testPackageLibPath/a.dart', r'''
class A {}
@ -876,37 +937,33 @@ class X
''');
}
test_fieldDeclaration_isExternal() async {
test_mixin_appendInterfaces() async {
_newAppendMacrosFile();
await _assertIntrospectDeclarationsText(r'''
@IntrospectDeclarationsPhaseMacro(
withDetailsFor: {'X'},
)
class X {
external int a;
int b = 0;
}
import 'append.dart';
@IntrospectDeclarationsPhaseMacro()
@AppendInterfaceA()
mixin X {}
''', r'''
class X
fields
a
flags: hasExternal
type: int
b
type: int
mixin X
interfaces
A
''');
}
test_fieldDeclaration_isFinal() async {
test_mixin_fieldDeclaration_isFinal() async {
await _assertIntrospectDeclarationsText(r'''
@IntrospectDeclarationsPhaseMacro(
withDetailsFor: {'X'},
)
class X {
mixin X {
final int a = 0;
int b = 0;
}
''', r'''
class X
mixin X
fields
a
flags: hasFinal
@ -916,66 +973,6 @@ class X
''');
}
test_fieldDeclaration_isLate() async {
await _assertIntrospectDeclarationsText(r'''
@IntrospectDeclarationsPhaseMacro(
withDetailsFor: {'X'},
)
class X {
late final int a;
final int b = 0;
}
''', r'''
class X
fields
a
flags: hasFinal hasLate
type: int
b
flags: hasFinal
type: int
''');
}
test_fieldDeclaration_isStatic() async {
await _assertIntrospectDeclarationsText(r'''
@IntrospectDeclarationsPhaseMacro(
withDetailsFor: {'X'},
)
class X {
static int a = 0;
int b = 0;
}
''', r'''
class X
fields
a
flags: isStatic
type: int
b
type: int
''');
}
test_fieldDeclaration_type_explicit() async {
await _assertIntrospectDeclarationsText(r'''
@IntrospectDeclarationsPhaseMacro(
withDetailsFor: {'X'},
)
class X {
int a = 0;
List<String> b = [];
}
''', r'''
class X
fields
a
type: int
b
type: List<String>
''');
}
/// Assert that the textual dump of the introspection information for
/// annotated declarations is the same as [expected].
Future<void> _assertIntrospectDeclarationsText(
@ -1008,9 +1005,7 @@ import 'introspect_declarations_phase.dart';
$declarationCode
''');
for (final class_ in library.definingCompilationUnit.classes) {
class_.assertNoErrorsForClassElement();
}
library.assertNoMacroErrors();
return library.topLevelElements
.whereType<ConstTopLevelVariableElementImpl>()
@ -1018,6 +1013,13 @@ $declarationCode
.map((e) => (e.constantInitializer as SimpleStringLiteral).value)
.join('\n');
}
void _newAppendMacrosFile() {
newFile(
'$testPackageLibPath/append.dart',
_appendMacrosCode,
);
}
}
abstract class MacroDeclarationsTest extends MacroElementsBaseTest {
@ -1918,6 +1920,19 @@ foo
''');
}
test_class_mixinDeclaration_method() async {
await _assertTypesPhaseIntrospectionText(r'''
mixin A {
@DeclarationTextMacro()
void foo() {}
}
''', r'''
foo
flags: hasBody
returnType: void
''');
}
test_classDeclaration_interfaces() async {
await _assertTypesPhaseIntrospectionText(r'''
@DeclarationTextMacro()
@ -2076,6 +2091,83 @@ class A
''');
}
test_mixin_methodDeclaration_getter() async {
await _assertTypesPhaseIntrospectionText(r'''
mixin A {
@DeclarationTextMacro()
int get foo => 0;
}
''', r'''
foo
flags: hasBody isGetter
returnType: int
''');
}
test_mixin_methodDeclaration_setter() async {
await _assertTypesPhaseIntrospectionText(r'''
mixin A {
@DeclarationTextMacro()
set foo(int value) {}
}
''', r'''
foo
flags: hasBody isSetter
positionalParameters
value
flags: isRequired
type: int
returnType: OmittedType
''');
}
test_mixinDeclaration_flags_hasBase() async {
await _assertTypesPhaseIntrospectionText(r'''
@DeclarationTextMacro()
base mixin A {}
''', r'''
mixin A
flags: hasBase
''');
}
test_mixinDeclaration_interfaces() async {
await _assertTypesPhaseIntrospectionText(r'''
@DeclarationTextMacro()
mixin A implements B, C {}
''', r'''
mixin A
interfaces
B
C
''');
}
test_mixinDeclaration_superclassConstraints() async {
await _assertTypesPhaseIntrospectionText(r'''
@DeclarationTextMacro()
mixin A on B, C {}
''', r'''
mixin A
superclassConstraints
B
C
''');
}
test_mixinDeclaration_typeParameters() async {
await _assertTypesPhaseIntrospectionText(r'''
@DeclarationTextMacro()
mixin A<T, U extends List<T>> {}
''', r'''
mixin A
typeParameters
T
U
bound: List<T>
''');
}
test_namedTypeAnnotation_prefixed() async {
await _assertTypesPhaseIntrospectionText(r'''
@DeclarationTextMacro()
@ -2123,8 +2215,7 @@ import 'declaration_text.dart';
$declarationCode
''');
final A = library.definingCompilationUnit.getClass('A')!;
A.assertNoErrorsForClassElement();
library.assertNoMacroErrors();
final macroAugmentation = library.augmentations.first;
final macroUnit = macroAugmentation.definingCompilationUnit;
@ -3126,17 +3217,35 @@ class MacroTypesTest_keepLinking extends MacroTypesTest {
bool get keepLinkingLibraries => true;
}
extension on ClassElement {
String get errorsStrForClassElement {
final element = this as ClassElementImpl;
return element.macroApplicationErrors.map((e) {
class _MacroApplicationErrorsCollector
extends GeneralizingElementVisitor<void> {
final List<MacroApplicationError> errors = [];
@override
void visitElement(Element element) {
if (element case final MacroTargetElement element) {
errors.addAll(element.macroApplicationErrors);
}
super.visitElement(element);
}
}
extension on LibraryElement {
List<MacroApplicationError> get macroErrors {
final collector = _MacroApplicationErrorsCollector();
accept(collector);
return collector.errors;
}
String get macroErrorsStr {
return macroErrors.map((e) {
return e.toStringForTest();
}).join('\n');
}
void assertNoErrorsForClassElement() {
final actual = errorsStrForClassElement;
expect(actual, isEmpty);
void assertNoMacroErrors() {
expect(macroErrorsStr, isEmpty);
}
}