diff --git a/pkg/analyzer/lib/src/summary2/library_builder.dart b/pkg/analyzer/lib/src/summary2/library_builder.dart index 010ab14eefb..4f78ea155f9 100644 --- a/pkg/analyzer/lib/src/summary2/library_builder.dart +++ b/pkg/analyzer/lib/src/summary2/library_builder.dart @@ -350,66 +350,71 @@ class LibraryBuilder { _declaredReferences[name] = reference; } - Future executeMacroDeclarationsPhase({ + /// Completes with `true` if a macro application was run in this library. + /// + /// Completes with `false` if there are no macro applications to run, either + /// because we ran all, or those that we have not run yet have dependencies + /// of interfaces declared in other libraries that, and we have not run yet + /// declarations phase macro applications for them. + Future executeMacroDeclarationsPhase({ required OperationPerformanceImpl performance, }) async { final macroApplier = linker.macroApplier; if (macroApplier == null) { - return; + return false; } - while (true) { - final results = await macroApplier.executeDeclarationsPhase( - typeSystem: element.typeSystem, - ); + final results = await macroApplier.executeDeclarationsPhase( + library: element, + ); - // No more applications to execute. - if (results == null) { - break; - } - - // No results from the application. - if (results.isEmpty) { - continue; - } - - _macroResults.add(results); - - final augmentationCode = macroApplier.buildAugmentationLibraryCode( - results, - ); - if (augmentationCode == null) { - continue; - } - - final importState = kind.addMacroAugmentation( - augmentationCode, - addLibraryAugmentDirective: true, - partialIndex: _macroResults.length, - ); - - final augmentation = _addMacroAugmentation(importState); - - final macroLinkingUnit = units.last; - ElementBuilder( - libraryBuilder: this, - container: macroLinkingUnit.container, - unitReference: macroLinkingUnit.reference, - unitElement: macroLinkingUnit.element, - ).buildDeclarationElements(macroLinkingUnit.node); - - final nodesToBuildType = NodesToBuildType(); - final resolver = - ReferenceResolver(linker, nodesToBuildType, augmentation); - macroLinkingUnit.node.accept(resolver); - TypesBuilder(linker).build(nodesToBuildType); - - // Append applications from the partial augmentation. - await macroApplier.add( - container: augmentation, - unit: macroLinkingUnit.node, - ); + // No more applications to execute. + if (results == null) { + return false; } + + // No results from the application. + if (results.isEmpty) { + return true; + } + + _macroResults.add(results); + + final augmentationCode = macroApplier.buildAugmentationLibraryCode( + results, + ); + if (augmentationCode == null) { + return true; + } + + final importState = kind.addMacroAugmentation( + augmentationCode, + addLibraryAugmentDirective: true, + partialIndex: _macroResults.length, + ); + + final augmentation = _addMacroAugmentation(importState); + + final macroLinkingUnit = units.last; + ElementBuilder( + libraryBuilder: this, + container: macroLinkingUnit.container, + unitReference: macroLinkingUnit.reference, + unitElement: macroLinkingUnit.element, + ).buildDeclarationElements(macroLinkingUnit.node); + + final nodesToBuildType = NodesToBuildType(); + final resolver = ReferenceResolver(linker, nodesToBuildType, augmentation); + macroLinkingUnit.node.accept(resolver); + TypesBuilder(linker).build(nodesToBuildType); + + // Append applications from the partial augmentation. + await macroApplier.add( + libraryElement: element, + container: augmentation, + unit: macroLinkingUnit.node, + ); + return true; } Future executeMacroTypesPhase({ @@ -460,6 +465,7 @@ class LibraryBuilder { // Append applications from the partial augmentation. await macroApplier.add( + libraryElement: element, container: augmentation, unit: macroLinkingUnit.node, ); @@ -470,6 +476,7 @@ class LibraryBuilder { Future fillMacroApplier(LibraryMacroApplier macroApplier) async { for (final linkingUnit in units) { await macroApplier.add( + libraryElement: element, container: element, unit: linkingUnit.node, ); diff --git a/pkg/analyzer/lib/src/summary2/link.dart b/pkg/analyzer/lib/src/summary2/link.dart index 61edb9d36cb..ed5fe4cb70f 100644 --- a/pkg/analyzer/lib/src/summary2/link.dart +++ b/pkg/analyzer/lib/src/summary2/link.dart @@ -305,10 +305,16 @@ class Linker { Future _executeMacroDeclarationsPhase({ required OperationPerformanceImpl performance, }) async { - for (final library in builders.values) { - await library.executeMacroDeclarationsPhase( - performance: performance, - ); + while (true) { + var hasProgress = false; + for (final library in builders.values) { + hasProgress |= await library.executeMacroDeclarationsPhase( + performance: performance, + ); + } + if (!hasProgress) { + break; + } } } diff --git a/pkg/analyzer/lib/src/summary2/macro_application.dart b/pkg/analyzer/lib/src/summary2/macro_application.dart index f3434af3224..c2cd358d046 100644 --- a/pkg/analyzer/lib/src/summary2/macro_application.dart +++ b/pkg/analyzer/lib/src/summary2/macro_application.dart @@ -8,6 +8,7 @@ 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; @@ -18,6 +19,7 @@ import 'package:analyzer/src/summary2/macro.dart'; import 'package:analyzer/src/summary2/macro_application_error.dart'; import 'package:analyzer/src/summary2/macro_declarations.dart'; import 'package:analyzer/src/utilities/extensions/object.dart'; +import 'package:collection/collection.dart'; /// The full list of [macro.ArgumentKind]s for this dart type (includes the type /// itself as well as type arguments, in source order with @@ -71,8 +73,25 @@ class LibraryMacroApplier { final MultiMacroExecutor macroExecutor; final bool Function(Uri) isLibraryBeingLinked; final DeclarationBuilder declarationBuilder; + + /// The reversed queue of macro applications to apply. + /// + /// We add classes before methods, and methods in the reverse order, + /// classes in the reverse order, annotations in the direct order. + /// + /// We iterate from the end looking for the next application to apply. + /// This way we ensure two ordering rules: + /// 1. inner before outer + /// 2. right to left + /// 3. source order final List<_MacroApplication> _applications = []; + /// The map from [InstanceElement] to the applications associated with it. + /// This includes applications on the class itself, and on the methods of + /// the class. + final Map> _interfaceApplications = + {}; + late final macro.TypePhaseIntrospector _typesPhaseIntrospector = _TypePhaseIntrospector(elementFactory, declarationBuilder); @@ -84,6 +103,7 @@ class LibraryMacroApplier { }); Future add({ + required LibraryElementImpl libraryElement, required LibraryOrAugmentationElementImpl container, required ast.CompilationUnit unit, }) async { @@ -92,23 +112,31 @@ class LibraryMacroApplier { case ast.ClassDeclaration(): final element = declaration.declaredElement; element as ClassElementImpl; + final declarationElement = element.augmented?.declaration ?? element; + declarationElement as ClassElementImpl; await _addClassLike( + libraryElement: libraryElement, container: container, - targetElement: element.declarationElement, + targetElement: declarationElement, classNode: declaration, classDeclarationKind: macro.DeclarationKind.classType, classAnnotations: declaration.metadata, + declarationsPhaseInterface: declarationElement, members: declaration.members, ); case ast.MixinDeclaration(): final element = declaration.declaredElement; element as MixinElementImpl; + final declarationElement = element.augmented?.declaration ?? element; + declarationElement as MixinElementImpl; await _addClassLike( + libraryElement: libraryElement, container: container, - targetElement: element.declarationElement, + targetElement: declarationElement, classNode: declaration, classDeclarationKind: macro.DeclarationKind.mixinType, classAnnotations: declaration.metadata, + declarationsPhaseInterface: declarationElement, members: declaration.members, ); } @@ -134,9 +162,11 @@ class LibraryMacroApplier { } Future?> executeDeclarationsPhase({ - required TypeSystemImpl typeSystem, + required LibraryElementImpl library, }) async { - final application = _nextForDeclarationsPhase(); + final application = _nextForDeclarationsPhase( + library: library, + ); if (application == null) { return null; } @@ -150,7 +180,7 @@ class LibraryMacroApplier { final introspector = _DeclarationPhaseIntrospector( elementFactory, declarationBuilder, - typeSystem, + library.typeSystem, ); final result = await macroExecutor.executeDeclarationsPhase( @@ -204,7 +234,9 @@ class LibraryMacroApplier { } Future _addAnnotations({ + required LibraryElementImpl libraryElement, required LibraryOrAugmentationElementImpl container, + required InstanceElement? declarationsPhaseElement, required ast.Declaration targetNode, required macro.DeclarationKind targetDeclarationKind, required List annotations, @@ -251,39 +283,54 @@ class LibraryMacroApplier { return instance.shouldExecute(targetDeclarationKind, phase); }).toSet(); - _applications.add( - _MacroApplication( - targetNode: targetNode, - targetElement: targetElement, - targetDeclarationKind: targetDeclarationKind, - instance: instance, - phasesToExecute: phasesToExecute, - ), + final application = _MacroApplication( + libraryElement: libraryElement, + declarationsPhaseElement: declarationsPhaseElement, + targetNode: targetNode, + targetElement: targetElement, + targetDeclarationKind: targetDeclarationKind, + annotationNode: annotation, + instance: instance, + phasesToExecute: phasesToExecute, ); + + _applications.add(application); + + // Record mapping for declarations phase dependencies. + if (declarationsPhaseElement != null) { + (_interfaceApplications[declarationsPhaseElement] ??= []) + .add(application); + } } } Future _addClassLike({ + required LibraryElementImpl libraryElement, required LibraryOrAugmentationElementImpl container, required MacroTargetElement targetElement, required ast.Declaration classNode, required macro.DeclarationKind classDeclarationKind, required List classAnnotations, + required InterfaceElement? declarationsPhaseInterface, required List members, }) async { await _addAnnotations( + libraryElement: libraryElement, container: container, targetNode: classNode, targetDeclarationKind: classDeclarationKind, + declarationsPhaseElement: declarationsPhaseInterface, annotations: classAnnotations, ); for (final member in members.reversed) { await _addAnnotations( + libraryElement: libraryElement, container: container, targetNode: member, // TODO(scheglov) incomplete targetDeclarationKind: macro.DeclarationKind.method, + declarationsPhaseElement: declarationsPhaseInterface, annotations: member.metadata, ); } @@ -304,6 +351,28 @@ class LibraryMacroApplier { } } + bool _hasInterfaceDependenciesSatisfied(_MacroApplication application) { + final dependencyElements = _interfaceDependencies( + application.declarationsPhaseElement, + ); + if (dependencyElements == null) { + return true; + } + + for (final dependencyElement in dependencyElements) { + final applications = _interfaceApplications[dependencyElement]; + if (applications != null) { + for (final dependencyApplication in applications) { + if (dependencyApplication.hasDeclarationsPhase) { + return false; + } + } + } + } + + return true; + } + /// If [annotation] references a macro, invokes the right callback. _AnnotationMacro? _importedMacro({ required LibraryOrAugmentationElementImpl container, @@ -385,13 +454,58 @@ class LibraryMacroApplier { throw UnimplementedError(); } - /// TODO(scheglov) Should use dependencies. - _MacroApplication? _nextForDeclarationsPhase() { - for (final application in _applications.reversed) { - if (application.phasesToExecute.remove(macro.Phase.declarations)) { - return application; - } + Set? _interfaceDependencies(InstanceElement? element) { + // TODO(scheglov) other elements + switch (element) { + case ExtensionElement(): + // TODO(scheglov) implement + throw UnimplementedError(); + case MixinElement(): + final augmented = element.augmented; + switch (augmented) { + case null: + return const {}; + default: + return [ + ...augmented.superclassConstraints.map((e) => e.element), + ...augmented.interfaces.map((e) => e.element), + ].whereNotNull().toSet(); + } + case InterfaceElement(): + final augmented = element.augmented; + switch (augmented) { + case null: + return const {}; + default: + return [ + element.supertype?.element, + ...augmented.mixins.map((e) => e.element), + ...augmented.interfaces.map((e) => e.element), + ].whereNotNull().toSet(); + } + default: + return null; } + } + + _MacroApplication? _nextForDeclarationsPhase({ + required LibraryElementImpl library, + }) { + for (final application in _applications.reversed) { + if (!application.hasDeclarationsPhase) { + continue; + } + if (application.libraryElement != library) { + continue; + } + if (!_hasInterfaceDependenciesSatisfied(application)) { + continue; + } + // The application has no dependencies to run. + application.removeDeclarationsPhase(); + return application; + } + return null; } @@ -610,14 +724,22 @@ class _DeclarationPhaseIntrospector extends _TypePhaseIntrospector .map(declarationBuilder.fromElement.fieldElement) .toList(); } + // TODO(scheglov) implement throw UnsupportedError('Only introspection on classes is supported'); } @override Future> methodsOf( - covariant macro.IntrospectableType clazz) { - // TODO: implement methodsOf - throw UnimplementedError(); + covariant macro.IntrospectableType type) async { + switch (type) { + case IntrospectableClassDeclarationImpl(): + return type.element.augmented!.methods + .where((e) => !e.isSynthetic) + .map(declarationBuilder.fromElement.methodElement) + .toList(); + } + // TODO(scheglov) implement + throw UnsupportedError('Only introspection on classes is supported'); } @override @@ -675,19 +797,33 @@ class _DeclarationPhaseIntrospector extends _TypePhaseIntrospector } class _MacroApplication { + final LibraryElementImpl libraryElement; + final InstanceElement? declarationsPhaseElement; final ast.AstNode targetNode; final MacroTargetElement targetElement; final macro.DeclarationKind targetDeclarationKind; + final ast.Annotation annotationNode; final macro.MacroInstanceIdentifier instance; final Set phasesToExecute; _MacroApplication({ + required this.libraryElement, + required this.declarationsPhaseElement, required this.targetNode, required this.targetElement, required this.targetDeclarationKind, + required this.annotationNode, required this.instance, required this.phasesToExecute, }); + + bool get hasDeclarationsPhase { + return phasesToExecute.contains(macro.Phase.declarations); + } + + void removeDeclarationsPhase() { + phasesToExecute.remove(macro.Phase.declarations); + } } class _StaticTypeImpl implements macro.StaticType { @@ -736,14 +872,3 @@ extension on macro.MacroExecutionResult { mixinAugmentations.isNotEmpty || typeAugmentations.isNotEmpty; } - -extension on T { - T get declarationElement { - switch (augmented) { - case T(:final T declaration): - return declaration; - default: - return this; - } - } -} diff --git a/pkg/analyzer/test/src/summary/element_text.dart b/pkg/analyzer/test/src/summary/element_text.dart index b453b2dbc6d..6de638cbb18 100644 --- a/pkg/analyzer/test/src/summary/element_text.dart +++ b/pkg/analyzer/test/src/summary/element_text.dart @@ -55,6 +55,7 @@ class ElementTextConfiguration { bool withPropertyLinking = false; bool withRedirectedConstructors = false; bool withReferences = false; + bool withReturnType = true; bool withSyntheticDartCoreImport = false; ElementTextConfiguration({ @@ -539,7 +540,7 @@ class _ElementWriter { _writeCodeRange(e); _writeTypeParameterElements(e.typeParameters); _writeParameterElements(e.parameters); - _writeType('returnType', e.returnType); + _writeReturnType(e.returnType); _writeAugmentationTarget(e); _writeAugmentation(e); }); @@ -710,10 +711,12 @@ class _ElementWriter { _writeElements('exports', e.libraryExports, _writeExportElement); - _sink.writelnWithIndent('definingUnit'); - _sink.withIndent(() { - _writeUnitElement(e.definingCompilationUnit); - }); + if (configuration.filter(e.definingCompilationUnit)) { + _sink.writelnWithIndent('definingUnit'); + _sink.withIndent(() { + _writeUnitElement(e.definingCompilationUnit); + }); + } if (e is LibraryElementImpl) { _writeLibraryAugmentations(e); @@ -760,7 +763,7 @@ class _ElementWriter { _writeTypeParameterElements(e.typeParameters); _writeParameterElements(e.parameters); - _writeType('returnType', e.returnType); + _writeReturnType(e.returnType); _writeNonSyntheticElement(e); if (e.isAugmentation) { @@ -967,7 +970,7 @@ class _ElementWriter { expect(e.typeParameters, isEmpty); _writeParameterElements(e.parameters); - _writeType('returnType', e.returnType); + _writeReturnType(e.returnType); _writeNonSyntheticElement(e); writeLinking(); _writeAugmentationTarget(e); @@ -1058,6 +1061,12 @@ class _ElementWriter { } } + void _writeReturnType(DartType type) { + if (configuration.withReturnType) { + _writeType('returnType', type); + } + } + void _writeShouldUseTypeForInitializerInference( PropertyInducingElementImpl e, ) { @@ -1129,7 +1138,7 @@ class _ElementWriter { _sink.withIndent(() { _writeTypeParameterElements(aliasedElement.typeParameters); _writeParameterElements(aliasedElement.parameters); - _writeType('returnType', aliasedElement.returnType); + _writeReturnType(aliasedElement.returnType); }); } }); diff --git a/pkg/analyzer/test/src/summary/elements_base.dart b/pkg/analyzer/test/src/summary/elements_base.dart index 9162cc04d1d..b7d95fc8f9d 100644 --- a/pkg/analyzer/test/src/summary/elements_base.dart +++ b/pkg/analyzer/test/src/summary/elements_base.dart @@ -32,18 +32,14 @@ abstract class ElementsBaseTest extends PubPackageResolutionTest { final uriStr = 'package:test/test.dart'; final libraryResult = await analysisSession.getLibraryByUri(uriStr); - libraryResult as LibraryElementResult; if (keepLinkingLibraries) { - return libraryResult.element as LibraryElementImpl; + return libraryResult.element; } else { analysisContext.changeFile(file.path); await analysisContext.applyPendingFileChanges(); // Ask again, should be read from bytes. - final analysisSession = analysisContext.currentSession; - final libraryResult = await analysisSession.getLibraryByUri(uriStr); - libraryResult as LibraryElementResult; - return libraryResult.element as LibraryElementImpl; + return testContextLibrary(uriStr); } } @@ -59,4 +55,17 @@ abstract class ElementsBaseTest extends PubPackageResolutionTest { } expect(actual, expected); } + + Future testContextLibrary(String uriStr) async { + final analysisContext = contextFor(testFile); + final analysisSession = analysisContext.currentSession; + final libraryResult = await analysisSession.getLibraryByUri(uriStr); + return libraryResult.element; + } +} + +extension on SomeLibraryElementResult { + LibraryElementImpl get element { + return (this as LibraryElementResult).element as LibraryElementImpl; + } } diff --git a/pkg/analyzer/test/src/summary/macro/order.dart b/pkg/analyzer/test/src/summary/macro/order.dart index 971a59773ce..34ed6f7e1ad 100644 --- a/pkg/analyzer/test/src/summary/macro/order.dart +++ b/pkg/analyzer/test/src/summary/macro/order.dart @@ -4,7 +4,8 @@ import 'package:_fe_analyzer_shared/src/macros/api.dart'; -/*macro*/ class AddClass implements ClassTypesMacro, MethodTypesMacro { +/*macro*/ class AddClass + implements ClassTypesMacro, MethodTypesMacro, MixinTypesMacro { final String name; const AddClass(this.name); @@ -19,6 +20,11 @@ import 'package:_fe_analyzer_shared/src/macros/api.dart'; _add(builder); } + @override + buildTypesForMixin(method, builder) { + _add(builder); + } + void _add(TypeBuilder builder) { final code = 'class $name {}'; builder.declareType(name, DeclarationCode.fromString(code)); @@ -26,7 +32,10 @@ import 'package:_fe_analyzer_shared/src/macros/api.dart'; } /*macro*/ class AddFunction - implements ClassDeclarationsMacro, MethodDeclarationsMacro { + implements + ClassDeclarationsMacro, + MethodDeclarationsMacro, + MixinDeclarationsMacro { final String name; const AddFunction(this.name); @@ -41,9 +50,42 @@ import 'package:_fe_analyzer_shared/src/macros/api.dart'; _add(builder); } + @override + buildDeclarationsForMixin(method, builder) { + _add(builder); + } + void _add(DeclarationBuilder builder) { final code = 'void $name() {}'; final declaration = DeclarationCode.fromString(code); builder.declareInLibrary(declaration); } } + +/*macro*/ class AddHierarchyMethod implements ClassDeclarationsMacro { + final String name; + + const AddHierarchyMethod(this.name); + + @override + buildDeclarationsForClass(clazz, builder) async { + // builder.typeDeclarationOf(identifier); + final methods = (await Future.wait( + clazz.interfaces.map( + (interface) async { + final type = await builder.typeDeclarationOf(interface.identifier); + type as IntrospectableType; + return await builder.methodsOf(type); + }, + ), + )) + .expand((element) => element) + .toList(); + final methodsStr = methods.map((e) => e.identifier.name).join('_'); + + final compoundName = methodsStr.isEmpty ? name : '${methodsStr}_$name'; + final code = ' void $compoundName() {}'; + final declaration = DeclarationCode.fromString(code); + builder.declareInType(declaration); + } +} diff --git a/pkg/analyzer/test/src/summary/macro_test.dart b/pkg/analyzer/test/src/summary/macro_test.dart index 934f9ff5ef3..47b4e064e72 100644 --- a/pkg/analyzer/test/src/summary/macro_test.dart +++ b/pkg/analyzer/test/src/summary/macro_test.dart @@ -23,6 +23,7 @@ import 'package:test/test.dart'; import 'package:test_reflective_loader/test_reflective_loader.dart'; import '../dart/resolution/node_text_expectations.dart'; +import 'element_text.dart'; import 'elements_base.dart'; import 'macros_environment.dart'; @@ -45,12 +46,16 @@ main() { defineReflectiveTests(MacroDeclarationsTest_fromBytes); defineReflectiveTests(MacroElementsTest_keepLinking); defineReflectiveTests(MacroElementsTest_fromBytes); - defineReflectiveTests(MacroApplicationOrderTest_keepLinking); + defineReflectiveTests(MacroApplicationOrderTest); defineReflectiveTests(UpdateNodeTextExpectations); }); } -abstract class MacroApplicationOrderTest extends MacroElementsBaseTest { +@reflectiveTest +class MacroApplicationOrderTest extends MacroElementsBaseTest { + @override + bool get keepLinkingLibraries => true; + String get _orderCode { var code = MacrosEnvironment.instance.packageAnalyzerFolder .getChildAssumingFile('test/src/summary/macro/order.dart') @@ -58,34 +63,799 @@ abstract class MacroApplicationOrderTest extends MacroElementsBaseTest { return code.replaceAll('/*macro*/', 'macro'); } - test_phases_class_types_declarations() async { - newFile('$testPackageLibPath/a.dart', _orderCode); + test_declarations_class_interfaces_backward() async { + _newOrderMacrosFile(); var library = await buildLibrary(r''' -import 'a.dart'; +import 'order.dart'; + +@AddFunction('f31') +@AddFunction('f32') +class X3 {} + +@AddFunction('f21') +@AddFunction('f22') +class X2 {} + +@AddFunction('f11') +@AddFunction('f12') +class X1 implements X2, X3 {} +'''); + + configuration.forOrder(); + checkElementText(library, r''' +library + imports + package:test/order.dart + augmentationImports + package:test/test.macro.dart + macroGeneratedCode +--- +library augment 'test.dart'; + +void f32() {} +void f31() {} +void f22() {} +void f21() {} +void f12() {} +void f11() {} +--- +'''); + } + + test_declarations_class_interfaces_forward() async { + _newOrderMacrosFile(); + + var library = await buildLibrary(r''' +import 'order.dart'; + +@AddFunction('f11') +@AddFunction('f12') +class X1 implements X2, X3 {} + +@AddFunction('f21') +@AddFunction('f22') +class X2 {} + +@AddFunction('f31') +@AddFunction('f32') +class X3 {} +'''); + + configuration.forOrder(); + checkElementText(library, r''' +library + imports + package:test/order.dart + augmentationImports + package:test/test.macro.dart + macroGeneratedCode +--- +library augment 'test.dart'; + +void f22() {} +void f21() {} +void f32() {} +void f31() {} +void f12() {} +void f11() {} +--- +'''); + } + + test_declarations_class_interfaces_forward2() async { + _newOrderMacrosFile(); + + var library = await buildLibrary(r''' +import 'order.dart'; + +@AddFunction('f11') +@AddFunction('f12') +class X1 implements X3, X4 {} + +@AddFunction('f21') +@AddFunction('f22') +class X2 implements X3, X4 {} + +@AddFunction('f31') +@AddFunction('f32') +class X3 {} + +@AddFunction('f41') +@AddFunction('f42') +class X4 {} +'''); + + configuration.forOrder(); + checkElementText(library, r''' +library + imports + package:test/order.dart + augmentationImports + package:test/test.macro.dart + macroGeneratedCode +--- +library augment 'test.dart'; + +void f32() {} +void f31() {} +void f42() {} +void f41() {} +void f12() {} +void f11() {} +void f22() {} +void f21() {} +--- +'''); + } + + test_declarations_class_interfaces_forward3() async { + _newOrderMacrosFile(); + + var library = await buildLibrary(r''' +import 'order.dart'; + +@AddFunction('f11') +@AddFunction('f12') +class X1 implements X2, X4 {} + +@AddFunction('f21') +@AddFunction('f22') +class X2 implements X3 {} + +@AddFunction('f31') +@AddFunction('f32') +class X3 {} + +@AddFunction('f41') +@AddFunction('f42') +class X4 {} +'''); + + configuration.forOrder(); + checkElementText(library, r''' +library + imports + package:test/order.dart + augmentationImports + package:test/test.macro.dart + macroGeneratedCode +--- +library augment 'test.dart'; + +void f32() {} +void f31() {} +void f22() {} +void f21() {} +void f42() {} +void f41() {} +void f12() {} +void f11() {} +--- +'''); + } + + test_declarations_class_mixins_backward() async { + _newOrderMacrosFile(); + + var library = await buildLibrary(r''' +import 'order.dart'; + +@AddFunction('f31') +@AddFunction('f32') +mixin X3 {} + +@AddFunction('f21') +@AddFunction('f22') +mixin X2 {} + +@AddFunction('f11') +@AddFunction('f12') +class X1 with X2, X3 {} +'''); + + configuration.forOrder(); + checkElementText(library, r''' +library + imports + package:test/order.dart + augmentationImports + package:test/test.macro.dart + macroGeneratedCode +--- +library augment 'test.dart'; + +void f32() {} +void f31() {} +void f22() {} +void f21() {} +void f12() {} +void f11() {} +--- +'''); + } + + test_declarations_class_mixins_forward() async { + _newOrderMacrosFile(); + + var library = await buildLibrary(r''' +import 'order.dart'; + +@AddFunction('f11') +@AddFunction('f12') +class X1 with X2, X3 {} + +@AddFunction('f21') +@AddFunction('f22') +mixin X2 {} + +@AddFunction('f31') +@AddFunction('f32') +mixin X3 {} +'''); + + configuration.forOrder(); + checkElementText(library, r''' +library + imports + package:test/order.dart + augmentationImports + package:test/test.macro.dart + macroGeneratedCode +--- +library augment 'test.dart'; + +void f22() {} +void f21() {} +void f32() {} +void f31() {} +void f12() {} +void f11() {} +--- +'''); + } + + test_declarations_class_superclass_backward() async { + _newOrderMacrosFile(); + + var library = await buildLibrary(r''' +import 'order.dart'; + +@AddFunction('f3') +class X3 extends X2 { + @AddFunction('f31') + void foo() {} + + @AddFunction('f32') + void bar() {} +} + +@AddFunction('f2') +class X2 extends X1 { + @AddFunction('f21') + void foo() {} + + @AddFunction('f22') + void bar() {} +} + +@AddFunction('f1') +class X1 { + @AddFunction('f11') + void foo() {} + + @AddFunction('f12') + void bar() {} +} +'''); + + configuration.forOrder(); + checkElementText(library, r''' +library + imports + package:test/order.dart + augmentationImports + package:test/test.macro.dart + macroGeneratedCode +--- +library augment 'test.dart'; + +void f11() {} +void f12() {} +void f1() {} +void f21() {} +void f22() {} +void f2() {} +void f31() {} +void f32() {} +void f3() {} +--- +'''); + } + + test_declarations_class_superclass_forward() async { + _newOrderMacrosFile(); + + var library = await buildLibrary(r''' +import 'order.dart'; + +@AddFunction('f1') +class X1 { + @AddFunction('f11') + void foo() {} + + @AddFunction('f12') + void bar() {} +} + +@AddFunction('f2') +class X2 extends X1 { + @AddFunction('f21') + void foo() {} + + @AddFunction('f22') + void bar() {} +} + +@AddFunction('f3') +class X3 extends X2 { + @AddFunction('f31') + void foo() {} + + @AddFunction('f32') + void bar() {} +} +'''); + + configuration.forOrder(); + checkElementText(library, r''' +library + imports + package:test/order.dart + augmentationImports + package:test/test.macro.dart + macroGeneratedCode +--- +library augment 'test.dart'; + +void f11() {} +void f12() {} +void f1() {} +void f21() {} +void f22() {} +void f2() {} +void f31() {} +void f32() {} +void f3() {} +--- +'''); + } + + test_declarations_class_superClass_mixins_interfaces_backward() async { + _newOrderMacrosFile(); + + var library = await buildLibrary(r''' +import 'order.dart'; + +@AddFunction('f41') +@AddFunction('f42') +class X4 {} + +@AddFunction('f31') +@AddFunction('f32') +mixin X3 {} + +@AddFunction('f21') +@AddFunction('f22') +class X2 {} + +@AddFunction('f11') +@AddFunction('f12') +class X1 extends X2 with X3 implements X4 {} +'''); + + configuration.forOrder(); + checkElementText(library, r''' +library + imports + package:test/order.dart + augmentationImports + package:test/test.macro.dart + macroGeneratedCode +--- +library augment 'test.dart'; + +void f42() {} +void f41() {} +void f32() {} +void f31() {} +void f22() {} +void f21() {} +void f12() {} +void f11() {} +--- +'''); + } + + test_declarations_class_superClass_mixins_interfaces_forward() async { + _newOrderMacrosFile(); + + var library = await buildLibrary(r''' +import 'order.dart'; + +@AddFunction('f11') +@AddFunction('f12') +class X1 extends X2 with X3 implements X4 {} + +@AddFunction('f21') +@AddFunction('f22') +class X2 {} + +@AddFunction('f31') +@AddFunction('f32') +mixin X3 {} + +@AddFunction('f41') +@AddFunction('f42') +class X4 {} +'''); + + configuration.forOrder(); + checkElementText(library, r''' +library + imports + package:test/order.dart + augmentationImports + package:test/test.macro.dart + macroGeneratedCode +--- +library augment 'test.dart'; + +void f22() {} +void f21() {} +void f32() {} +void f31() {} +void f42() {} +void f41() {} +void f12() {} +void f11() {} +--- +'''); + } + + test_declarations_libraryCycle_class_interfaces() async { + useEmptyByteStore(); + _newOrderMacrosFile(); + + newFile('$testPackageLibPath/x2.dart', r''' +import 'test.dart'; +import 'order.dart'; + +@AddHierarchyMethod('f211') +@AddHierarchyMethod('f212') +class X21 {} + +@AddHierarchyMethod('f221') +@AddHierarchyMethod('f222') +class X22 {} +'''); + + final testLibrary = await buildLibrary(r''' +import 'order.dart'; +import 'x2.dart'; + +@AddHierarchyMethod('f11') +@AddHierarchyMethod('f12') +class X1 implements X22 {} +'''); + + // When we process `X1`, we see macro generated methods of `X22`. + // This shows that we processed `X22` before `X1`. + configuration.forOrder(); + checkElementText(testLibrary, r''' +library + imports + package:test/order.dart + package:test/x2.dart + augmentationImports + package:test/test.macro.dart + macroGeneratedCode +--- +library augment 'test.dart'; + +augment class X1 { + void f222_f221_f12() {} + void f222_f221_f11() {} +} +--- +'''); + + // There are no dependencies between `X21` and `X22`, so they are + // processed in the source order. + // We see `f212` before `f211`, this shows that we process annotations + // from right to left. + final x2Library = await testContextLibrary('package:test/x2.dart'); + checkElementText(x2Library, r''' +library + imports + package:test/test.dart + package:test/order.dart + augmentationImports + package:test/x2.macro.dart + macroGeneratedCode +--- +library augment 'x2.dart'; + +augment class X21 { + void f212() {} + void f211() {} +} +augment class X22 { + void f222() {} + void f221() {} +} +--- +'''); + } + + test_declarations_mixin_interfaces_backward() async { + _newOrderMacrosFile(); + + var library = await buildLibrary(r''' +import 'order.dart'; + +@AddFunction('f31') +@AddFunction('f32') +class X3 {} + +@AddFunction('f21') +@AddFunction('f22') +class X2 {} + +@AddFunction('f11') +@AddFunction('f12') +mixin X1 implements X2, X3 {} +'''); + + configuration.forOrder(); + checkElementText(library, r''' +library + imports + package:test/order.dart + augmentationImports + package:test/test.macro.dart + macroGeneratedCode +--- +library augment 'test.dart'; + +void f32() {} +void f31() {} +void f22() {} +void f21() {} +void f12() {} +void f11() {} +--- +'''); + } + + test_declarations_mixin_interfaces_forward() async { + _newOrderMacrosFile(); + + var library = await buildLibrary(r''' +import 'order.dart'; + +@AddFunction('f11') +@AddFunction('f12') +mixin X1 implements X2, X3 {} + +@AddFunction('f21') +@AddFunction('f22') +class X2 {} + +@AddFunction('f31') +@AddFunction('f32') +class X3 {} +'''); + + configuration.forOrder(); + checkElementText(library, r''' +library + imports + package:test/order.dart + augmentationImports + package:test/test.macro.dart + macroGeneratedCode +--- +library augment 'test.dart'; + +void f22() {} +void f21() {} +void f32() {} +void f31() {} +void f12() {} +void f11() {} +--- +'''); + } + + test_declarations_mixin_superclassConstraints_backward() async { + _newOrderMacrosFile(); + + var library = await buildLibrary(r''' +import 'order.dart'; + +@AddFunction('f31') +@AddFunction('f32') +class X3 {} + +@AddFunction('f21') +@AddFunction('f22') +class X2 {} + +@AddFunction('f11') +@AddFunction('f12') +mixin X1 on X2, X3 {} +'''); + + configuration.forOrder(); + checkElementText(library, r''' +library + imports + package:test/order.dart + augmentationImports + package:test/test.macro.dart + macroGeneratedCode +--- +library augment 'test.dart'; + +void f32() {} +void f31() {} +void f22() {} +void f21() {} +void f12() {} +void f11() {} +--- +'''); + } + + test_declarations_mixin_superclassConstraints_forward() async { + _newOrderMacrosFile(); + + var library = await buildLibrary(r''' +import 'order.dart'; + +@AddFunction('f11') +@AddFunction('f12') +mixin X1 on X2, X3 {} + +@AddFunction('f21') +@AddFunction('f22') +class X2 {} + +@AddFunction('f31') +@AddFunction('f32') +class X3 {} +'''); + + configuration.forOrder(); + checkElementText(library, r''' +library + imports + package:test/order.dart + augmentationImports + package:test/test.macro.dart + macroGeneratedCode +--- +library augment 'test.dart'; + +void f22() {} +void f21() {} +void f32() {} +void f31() {} +void f12() {} +void f11() {} +--- +'''); + } + + test_declarations_mixin_superclassConstraints_interfaces_backward() async { + _newOrderMacrosFile(); + + var library = await buildLibrary(r''' +import 'order.dart'; + +@AddFunction('f31') +@AddFunction('f32') +class X3 {} + +@AddFunction('f21') +@AddFunction('f22') +class X2 {} + +@AddFunction('f11') +@AddFunction('f12') +mixin X1 on X2 implements X3 {} +'''); + + configuration.forOrder(); + checkElementText(library, r''' +library + imports + package:test/order.dart + augmentationImports + package:test/test.macro.dart + macroGeneratedCode +--- +library augment 'test.dart'; + +void f32() {} +void f31() {} +void f22() {} +void f21() {} +void f12() {} +void f11() {} +--- +'''); + } + + test_declarations_mixin_superclassConstraints_interfaces_forward() async { + _newOrderMacrosFile(); + + var library = await buildLibrary(r''' +import 'order.dart'; + +@AddFunction('f11') +@AddFunction('f12') +mixin X1 on X2 implements X3 {} + +@AddFunction('f21') +@AddFunction('f22') +class X2 {} + +@AddFunction('f31') +@AddFunction('f32') +class X3 {} +'''); + + configuration.forOrder(); + checkElementText(library, r''' +library + imports + package:test/order.dart + augmentationImports + package:test/test.macro.dart + macroGeneratedCode +--- +library augment 'test.dart'; + +void f22() {} +void f21() {} +void f32() {} +void f31() {} +void f12() {} +void f11() {} +--- +'''); + } + + test_phases_class_types_declarations() async { + _newOrderMacrosFile(); + + var library = await buildLibrary(r''' +import 'order.dart'; @AddClass('A1') @AddFunction('f1') class X {} '''); - configuration - ..withConstructors = false - ..withMetadata = false - ..withReferences = true; + configuration.forOrder(); checkElementText(library, r''' library - reference: self imports - package:test/a.dart - definingUnit - reference: self - classes - class X @59 - reference: self::@class::X + package:test/order.dart augmentationImports package:test/test.macro.dart - reference: self::@augmentation::package:test/test.macro.dart macroGeneratedCode --- library augment 'test.dart'; @@ -93,23 +863,14 @@ library augment 'test.dart'; class A1 {} void f1() {} --- - definingUnit - reference: self::@augmentation::package:test/test.macro.dart - classes - class A1 @36 - reference: self::@augmentation::package:test/test.macro.dart::@class::A1 - functions - f1 @47 - reference: self::@augmentation::package:test/test.macro.dart::@function::f1 - returnType: void '''); } test_types_class_method_rightToLeft() async { - newFile('$testPackageLibPath/a.dart', _orderCode); + _newOrderMacrosFile(); var library = await buildLibrary(r''' -import 'a.dart'; +import 'order.dart'; class X { @AddClass('A1') @@ -118,27 +879,13 @@ class X { } '''); - configuration - ..withConstructors = false - ..withMetadata = false - ..withReferences = true; + configuration.forOrder(); checkElementText(library, r''' library - reference: self imports - package:test/a.dart - definingUnit - reference: self - classes - class X @24 - reference: self::@class::X - methods - foo @71 - reference: self::@class::X::@method::foo - returnType: void + package:test/order.dart augmentationImports package:test/test.macro.dart - reference: self::@augmentation::package:test/test.macro.dart macroGeneratedCode --- library augment 'test.dart'; @@ -146,21 +893,14 @@ library augment 'test.dart'; class A2 {} class A1 {} --- - definingUnit - reference: self::@augmentation::package:test/test.macro.dart - classes - class A2 @36 - reference: self::@augmentation::package:test/test.macro.dart::@class::A2 - class A1 @48 - reference: self::@augmentation::package:test/test.macro.dart::@class::A1 '''); } test_types_class_method_sourceOrder() async { - newFile('$testPackageLibPath/a.dart', _orderCode); + _newOrderMacrosFile(); var library = await buildLibrary(r''' -import 'a.dart'; +import 'order.dart'; class X { @AddClass('A1') @@ -171,30 +911,13 @@ class X { } '''); - configuration - ..withConstructors = false - ..withMetadata = false - ..withReferences = true; + configuration.forOrder(); checkElementText(library, r''' library - reference: self imports - package:test/a.dart - definingUnit - reference: self - classes - class X @24 - reference: self::@class::X - methods - foo @53 - reference: self::@class::X::@method::foo - returnType: void - bar @88 - reference: self::@class::X::@method::bar - returnType: void + package:test/order.dart augmentationImports package:test/test.macro.dart - reference: self::@augmentation::package:test/test.macro.dart macroGeneratedCode --- library augment 'test.dart'; @@ -202,44 +925,27 @@ library augment 'test.dart'; class A1 {} class A2 {} --- - definingUnit - reference: self::@augmentation::package:test/test.macro.dart - classes - class A1 @36 - reference: self::@augmentation::package:test/test.macro.dart::@class::A1 - class A2 @48 - reference: self::@augmentation::package:test/test.macro.dart::@class::A2 '''); } test_types_class_rightToLeft() async { - newFile('$testPackageLibPath/a.dart', _orderCode); + _newOrderMacrosFile(); var library = await buildLibrary(r''' -import 'a.dart'; +import 'order.dart'; @AddClass('A1') @AddClass('A2') class X {} '''); - configuration - ..withConstructors = false - ..withMetadata = false - ..withReferences = true; + configuration.forOrder(); checkElementText(library, r''' library - reference: self imports - package:test/a.dart - definingUnit - reference: self - classes - class X @56 - reference: self::@class::X + package:test/order.dart augmentationImports package:test/test.macro.dart - reference: self::@augmentation::package:test/test.macro.dart macroGeneratedCode --- library augment 'test.dart'; @@ -247,48 +953,29 @@ library augment 'test.dart'; class A2 {} class A1 {} --- - definingUnit - reference: self::@augmentation::package:test/test.macro.dart - classes - class A2 @36 - reference: self::@augmentation::package:test/test.macro.dart::@class::A2 - class A1 @48 - reference: self::@augmentation::package:test/test.macro.dart::@class::A1 '''); } test_types_class_sourceOrder() async { - newFile('$testPackageLibPath/a.dart', _orderCode); + _newOrderMacrosFile(); var library = await buildLibrary(r''' -import 'a.dart'; +import 'order.dart'; @AddClass('A1') -class X {} +class X1 {} @AddClass('A2') -class Y {} +class X2 {} '''); - configuration - ..withConstructors = false - ..withMetadata = false - ..withReferences = true; + configuration.forOrder(); checkElementText(library, r''' library - reference: self imports - package:test/a.dart - definingUnit - reference: self - classes - class X @40 - reference: self::@class::X - class Y @68 - reference: self::@class::Y + package:test/order.dart augmentationImports package:test/test.macro.dart - reference: self::@augmentation::package:test/test.macro.dart macroGeneratedCode --- library augment 'test.dart'; @@ -296,21 +983,14 @@ library augment 'test.dart'; class A1 {} class A2 {} --- - definingUnit - reference: self::@augmentation::package:test/test.macro.dart - classes - class A1 @36 - reference: self::@augmentation::package:test/test.macro.dart::@class::A1 - class A2 @48 - reference: self::@augmentation::package:test/test.macro.dart::@class::A2 '''); } test_types_innerBeforeOuter_class_method() async { - newFile('$testPackageLibPath/a.dart', _orderCode); + _newOrderMacrosFile(); var library = await buildLibrary(r''' -import 'a.dart'; +import 'order.dart'; @AddClass('A1') class X { @@ -319,27 +999,13 @@ class X { } '''); - configuration - ..withConstructors = false - ..withMetadata = false - ..withReferences = true; + configuration.forOrder(); checkElementText(library, r''' library - reference: self imports - package:test/a.dart - definingUnit - reference: self - classes - class X @40 - reference: self::@class::X - methods - foo @69 - reference: self::@class::X::@method::foo - returnType: void + package:test/order.dart augmentationImports package:test/test.macro.dart - reference: self::@augmentation::package:test/test.macro.dart macroGeneratedCode --- library augment 'test.dart'; @@ -347,21 +1013,162 @@ library augment 'test.dart'; class A2 {} class A1 {} --- - definingUnit - reference: self::@augmentation::package:test/test.macro.dart - classes - class A2 @36 - reference: self::@augmentation::package:test/test.macro.dart::@class::A2 - class A1 @48 - reference: self::@augmentation::package:test/test.macro.dart::@class::A1 '''); } -} -@reflectiveTest -class MacroApplicationOrderTest_keepLinking extends MacroApplicationOrderTest { - @override - bool get keepLinkingLibraries => true; + test_types_innerBeforeOuter_mixin_method() async { + _newOrderMacrosFile(); + + var library = await buildLibrary(r''' +import 'order.dart'; + +@AddClass('A1') +mixin X { + @AddClass('A2') + void foo() {} +} +'''); + + configuration.forOrder(); + checkElementText(library, r''' +library + imports + package:test/order.dart + augmentationImports + package:test/test.macro.dart + macroGeneratedCode +--- +library augment 'test.dart'; + +class A2 {} +class A1 {} +--- +'''); + } + + test_types_mixin_method_rightToLeft() async { + _newOrderMacrosFile(); + + var library = await buildLibrary(r''' +import 'order.dart'; + +mixin X { + @AddClass('A1') + @AddClass('A2') + void foo() {} +} +'''); + + configuration.forOrder(); + checkElementText(library, r''' +library + imports + package:test/order.dart + augmentationImports + package:test/test.macro.dart + macroGeneratedCode +--- +library augment 'test.dart'; + +class A2 {} +class A1 {} +--- +'''); + } + + test_types_mixin_method_sourceOrder() async { + _newOrderMacrosFile(); + + var library = await buildLibrary(r''' +import 'order.dart'; + +mixin X { + @AddClass('A1') + void foo() {} + + @AddClass('A2') + void bar() {} +} +'''); + + configuration.forOrder(); + checkElementText(library, r''' +library + imports + package:test/order.dart + augmentationImports + package:test/test.macro.dart + macroGeneratedCode +--- +library augment 'test.dart'; + +class A1 {} +class A2 {} +--- +'''); + } + + test_types_mixin_rightToLeft() async { + _newOrderMacrosFile(); + + var library = await buildLibrary(r''' +import 'order.dart'; + +@AddClass('A1') +@AddClass('A2') +mixin X {} +'''); + + configuration.forOrder(); + checkElementText(library, r''' +library + imports + package:test/order.dart + augmentationImports + package:test/test.macro.dart + macroGeneratedCode +--- +library augment 'test.dart'; + +class A2 {} +class A1 {} +--- +'''); + } + + test_types_mixin_sourceOrder() async { + _newOrderMacrosFile(); + + var library = await buildLibrary(r''' +import 'order.dart'; + +@AddClass('A1') +mixin X1 {} + +@AddClass('A2') +mixin X2 {} +'''); + + configuration.forOrder(); + checkElementText(library, r''' +library + imports + package:test/order.dart + augmentationImports + package:test/test.macro.dart + macroGeneratedCode +--- +library augment 'test.dart'; + +class A1 {} +class A2 {} +--- +'''); + } + + void _newOrderMacrosFile() { + newFile('$testPackageLibPath/order.dart', _orderCode); + } } @reflectiveTest @@ -3256,3 +4063,18 @@ extension on Folder { return file; } } + +extension on ElementTextConfiguration { + void forOrder() { + filter = (element) { + if (element is CompilationUnitElement) { + return false; + // return element.source.uri != Uri.parse('package:test/test.dart'); + } + return true; + }; + withConstructors = false; + withMetadata = false; + withReturnType = false; + } +}