diff --git a/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart b/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart index 73122407f78..54a1efcf8a3 100644 --- a/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart +++ b/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart @@ -7010,6 +7010,34 @@ const MessageCode messageLoadLibraryTakesNoArguments = const MessageCode( analyzerCodes: ["LOAD_LIBRARY_TAKES_NO_ARGUMENTS"], problemMessage: r"""'loadLibrary' takes no arguments."""); +// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE. +const Template< + Message Function( + String + name)> templateMacroClassNotDeclaredMacro = const Template< + Message Function(String name)>( + problemMessageTemplate: + r"""Non-abstract class '#name' implements 'Macro' but isn't declared as a macro class.""", + correctionMessageTemplate: r"""Try adding the 'macro' class modifier.""", + withArguments: _withArgumentsMacroClassNotDeclaredMacro); + +// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE. +const Code codeMacroClassNotDeclaredMacro = + const Code( + "MacroClassNotDeclaredMacro", +); + +// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE. +Message _withArgumentsMacroClassNotDeclaredMacro(String name) { + if (name.isEmpty) throw 'No name provided'; + name = demangleMixinApplicationName(name); + return new Message(codeMacroClassNotDeclaredMacro, + problemMessage: + """Non-abstract class '${name}' implements 'Macro' but isn't declared as a macro class.""", + correctionMessage: """Try adding the 'macro' class modifier.""", + arguments: {'name': name}); +} + // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE. const Code codeMainNotFunctionDeclaration = messageMainNotFunctionDeclaration; diff --git a/pkg/front_end/lib/src/fasta/incremental_compiler.dart b/pkg/front_end/lib/src/fasta/incremental_compiler.dart index 3e10ccd295a..74902b33bdb 100644 --- a/pkg/front_end/lib/src/fasta/incremental_compiler.dart +++ b/pkg/front_end/lib/src/fasta/incremental_compiler.dart @@ -1749,6 +1749,7 @@ class IncrementalCompiler implements IncrementalKernelGenerator { loader: lastGoodKernelTarget.loader, nameOrigin: libraryBuilder, isUnsupported: libraryBuilder.isUnsupported, + isAugmentation: false, ); libraryBuilder.scope.forEachLocalMember((name, member) { debugLibrary.scope.addLocalMember(name, member, setter: false); @@ -1803,6 +1804,7 @@ class IncrementalCompiler implements IncrementalKernelGenerator { scope: debugLibrary.scope.createNestedScope("expression"), nameOrigin: libraryBuilder, isUnsupported: libraryBuilder.isUnsupported, + isAugmentation: false, ); HybridFileSystem hfs = diff --git a/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart b/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart index 755208cb3d1..55f7c9a2197 100644 --- a/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart +++ b/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart @@ -427,12 +427,25 @@ class KernelTarget extends TargetImplementation { Future _buildForPhase1( Iterable augmentationLibraries) async { await loader.buildOutlines(); + // Normally patch libraries are applied in [SourceLoader.resolveParts]. + // For augmentation libraries we instead apply them directly here. + for (SourceLibraryBuilder augmentationLibrary in augmentationLibraries) { + augmentationLibrary.applyPatches(); + } loader.computeLibraryScopes(augmentationLibraries); // TODO(johnniwinther): Support computation of macro applications in // augmentation libraries? loader.resolveTypes(augmentationLibraries); } + /// Builds [augmentationLibrary] to the state expected after applying phase + /// 2 macros. + void _buildForPhase2(SourceLibraryBuilder augmentationLibrary) { + augmentationLibrary.finishTypeVariables(objectClassBuilder, dynamicType); + augmentationLibrary.build(loader.coreLibrary, modifyTarget: false); + augmentationLibrary.resolveConstructors(); + } + Future buildOutlines({CanonicalName? nameRoot}) async { if (loader.first == null) return new BuildResult(); return withCrashReporting(() async { @@ -483,7 +496,8 @@ class KernelTarget extends TargetImplementation { loader.checkSemantics(objectClassBuilder); benchmarker?.enterPhase(BenchmarkPhases.outline_finishTypeVariables); - loader.finishTypeVariables(objectClassBuilder, dynamicType); + loader.finishTypeVariables( + loader.sourceLibraryBuilders, objectClassBuilder, dynamicType); benchmarker ?.enterPhase(BenchmarkPhases.outline_createTypeInferenceEngine); @@ -500,7 +514,7 @@ class KernelTarget extends TargetImplementation { installSyntheticConstructors(sourceClassBuilders); benchmarker?.enterPhase(BenchmarkPhases.outline_resolveConstructors); - loader.resolveConstructors(); + loader.resolveConstructors(loader.sourceLibraryBuilders); benchmarker?.enterPhase(BenchmarkPhases.outline_link); component = @@ -517,8 +531,11 @@ class KernelTarget extends TargetImplementation { if (macroApplications != null) { benchmarker?.enterPhase(BenchmarkPhases.outline_applyDeclarationMacros); - await macroApplications - .applyDeclarationsMacros(loader.hierarchyBuilder); + await macroApplications.applyDeclarationsMacros(loader.hierarchyBuilder, + (SourceLibraryBuilder augmentationLibrary) async { + await _buildForPhase1([augmentationLibrary]); + _buildForPhase2(augmentationLibrary); + }); } benchmarker diff --git a/pkg/front_end/lib/src/fasta/kernel/macro.dart b/pkg/front_end/lib/src/fasta/kernel/macro.dart index c4a34b36798..c81a969b94c 100644 --- a/pkg/front_end/lib/src/fasta/kernel/macro.dart +++ b/pkg/front_end/lib/src/fasta/kernel/macro.dart @@ -116,6 +116,7 @@ class MacroApplications { final macro.MacroExecutor _macroExecutor; final Map libraryData; final MacroApplicationDataForTesting? dataForTesting; + List<_ApplicationData>? _applicationDataCache; MacroApplications( this._macroExecutor, this.libraryData, this.dataForTesting) { @@ -272,60 +273,57 @@ class MacroApplications { } } - Future>> - _applyMacros( - Future> Function( - Builder, macro.Declaration, List) - applyMacros) async { - Map> libraryResults = - {}; - for (MapEntry libraryEntry in libraryData.entries) { - SourceLibraryBuilder libraryBuilder = libraryEntry.key; - List results = []; - LibraryMacroApplicationData libraryMacroApplicationData = - libraryEntry.value; - for (MapEntry> memberEntry - in libraryMacroApplicationData.memberApplications.entries) { - MemberBuilder memberBuilder = memberEntry.key; - macro.Declaration? declaration = _getMemberDeclaration(memberBuilder); - if (declaration != null) { - results.addAll( - await applyMacros(memberBuilder, declaration, memberEntry.value)); - } - } - for (MapEntry classEntry - in libraryMacroApplicationData.classData.entries) { - SourceClassBuilder classBuilder = classEntry.key; - ClassMacroApplicationData classData = classEntry.value; - List? classApplications = classData.classApplications; - if (classApplications != null) { - macro.ClassDeclaration classDeclaration = - _getClassDeclaration(classBuilder); - results.addAll(await applyMacros( - classBuilder, classDeclaration, classApplications)); - } + Iterable<_ApplicationData> get _applicationData { + if (_applicationDataCache == null) { + List<_ApplicationData> data = _applicationDataCache = []; + for (MapEntry libraryEntry in libraryData.entries) { + SourceLibraryBuilder libraryBuilder = libraryEntry.key; + LibraryMacroApplicationData libraryMacroApplicationData = + libraryEntry.value; for (MapEntry> memberEntry - in classData.memberApplications.entries) { + in libraryMacroApplicationData.memberApplications.entries) { MemberBuilder memberBuilder = memberEntry.key; macro.Declaration? declaration = _getMemberDeclaration(memberBuilder); if (declaration != null) { - results.addAll(await applyMacros( - memberBuilder, declaration, memberEntry.value)); + data.add(new _ApplicationData( + libraryBuilder, memberBuilder, declaration, memberEntry.value)); + } + } + for (MapEntry classEntry + in libraryMacroApplicationData.classData.entries) { + SourceClassBuilder classBuilder = classEntry.key; + ClassMacroApplicationData classData = classEntry.value; + List? classApplications = + classData.classApplications; + if (classApplications != null) { + macro.ClassDeclaration classDeclaration = + _getClassDeclaration(classBuilder); + data.add(new _ApplicationData(libraryBuilder, classBuilder, + classDeclaration, classApplications)); + } + for (MapEntry> memberEntry + in classData.memberApplications.entries) { + MemberBuilder memberBuilder = memberEntry.key; + macro.Declaration? declaration = + _getMemberDeclaration(memberBuilder); + if (declaration != null) { + data.add(new _ApplicationData(libraryBuilder, memberBuilder, + declaration, memberEntry.value)); + } } } } - libraryResults[libraryBuilder] = results; } - return libraryResults; + return _applicationDataCache!; } Future> _applyTypeMacros( - Builder builder, - macro.Declaration declaration, - List macroApplications) async { + _ApplicationData applicationData) async { + macro.Declaration declaration = applicationData.declaration; List results = []; - for (MacroApplication macroApplication in macroApplications) { + for (MacroApplication macroApplication + in applicationData.macroApplications) { if (macroApplication.instanceIdentifier .shouldExecute(_declarationKind(declaration), macro.Phase.types)) { macro.MacroExecutionResult result = @@ -336,6 +334,7 @@ class MacroApplications { } if (retainDataForTesting) { + Builder builder = applicationData.builder; if (builder is SourceClassBuilder) { dataForTesting?.classTypesResults[builder] = results; } else { @@ -347,8 +346,13 @@ class MacroApplications { Future> applyTypeMacros() async { List augmentationLibraries = []; - Map> results = - await _applyMacros(_applyTypeMacros); + Map> results = {}; + for (_ApplicationData macroApplication in _applicationData) { + List executionResults = + await _applyTypeMacros(macroApplication); + (results[macroApplication.libraryBuilder] ??= []) + .addAll(executionResults); + } for (MapEntry> entry in results.entries) { SourceLibraryBuilder sourceLibraryBuilder = entry.key; @@ -363,12 +367,12 @@ class MacroApplications { return augmentationLibraries; } - Future> _applyDeclarationsMacros( - Builder builder, - macro.Declaration declaration, - List macroApplications) async { + Future _applyDeclarationsMacros(_ApplicationData applicationData, + Future Function(SourceLibraryBuilder) onAugmentationLibrary) async { List results = []; - for (MacroApplication macroApplication in macroApplications) { + macro.Declaration declaration = applicationData.declaration; + for (MacroApplication macroApplication + in applicationData.macroApplications) { if (macroApplication.instanceIdentifier.shouldExecute( _declarationKind(declaration), macro.Phase.declarations)) { macro.MacroExecutionResult result = @@ -377,10 +381,19 @@ class MacroApplications { declaration, typeResolver, classIntrospector); - results.add(result); + String source = _macroExecutor + .buildAugmentationLibrary([result], _resolveIdentifier); + SourceLibraryBuilder augmentationLibrary = await applicationData + .libraryBuilder + .createAugmentationLibrary(source); + await onAugmentationLibrary(augmentationLibrary); + if (retainDataForTesting) { + results.add(result); + } } } if (retainDataForTesting) { + Builder builder = applicationData.builder; if (builder is SourceClassBuilder) { dataForTesting?.classDeclarationsResults[builder] = results; } else { @@ -388,27 +401,28 @@ class MacroApplications { results; } } - return results; } late Types types; late macro.TypeResolver typeResolver; late macro.ClassIntrospector classIntrospector; - Future applyDeclarationsMacros( - ClassHierarchyBase classHierarchy) async { + Future applyDeclarationsMacros(ClassHierarchyBase classHierarchy, + Future Function(SourceLibraryBuilder) onAugmentationLibrary) async { types = new Types(classHierarchy); typeResolver = new _TypeResolver(this); classIntrospector = new _ClassIntrospector(this); - await _applyMacros(_applyDeclarationsMacros); + for (_ApplicationData macroApplication in _applicationData) { + await _applyDeclarationsMacros(macroApplication, onAugmentationLibrary); + } } Future> _applyDefinitionMacros( - Builder builder, - macro.Declaration declaration, - List macroApplications) async { + _ApplicationData applicationData) async { List results = []; - for (MacroApplication macroApplication in macroApplications) { + macro.Declaration declaration = applicationData.declaration; + for (MacroApplication macroApplication + in applicationData.macroApplications) { if (macroApplication.instanceIdentifier.shouldExecute( _declarationKind(declaration), macro.Phase.definitions)) { macro.MacroExecutionResult result = @@ -422,6 +436,7 @@ class MacroApplications { } } if (retainDataForTesting) { + Builder builder = applicationData.builder; if (builder is SourceClassBuilder) { dataForTesting?.classDefinitionsResults[builder] = results; } else { @@ -436,13 +451,16 @@ class MacroApplications { Future applyDefinitionMacros() async { typeDeclarationResolver = new _TypeDeclarationResolver(); - await _applyMacros(_applyDefinitionMacros); + for (_ApplicationData macroApplication in _applicationData) { + await _applyDefinitionMacros(macroApplication); + } } void close() { _macroExecutor.close(); _staticTypeCache.clear(); _typeAnnotationCache.clear(); + _applicationDataCache?.clear(); } macro.ClassDeclaration _createClassDeclaration(SourceClassBuilder builder) { @@ -951,3 +969,14 @@ macro.DeclarationKind _declarationKind(macro.Declaration declaration) { throw new UnsupportedError( "Unexpected declaration ${declaration} (${declaration.runtimeType})"); } + +/// Data needed to apply a list of macro applications to a class or member. +class _ApplicationData { + final SourceLibraryBuilder libraryBuilder; + final Builder builder; + final macro.Declaration declaration; + final List macroApplications; + + _ApplicationData(this.libraryBuilder, this.builder, this.declaration, + this.macroApplications); +} diff --git a/pkg/front_end/lib/src/fasta/source/source_class_builder.dart b/pkg/front_end/lib/src/fasta/source/source_class_builder.dart index 128d00fd92f..d26b6a20f02 100644 --- a/pkg/front_end/lib/src/fasta/source/source_class_builder.dart +++ b/pkg/front_end/lib/src/fasta/source/source_class_builder.dart @@ -618,8 +618,11 @@ class SourceClassBuilder extends ClassBuilderImpl } } - void checkSupertypes(CoreTypes coreTypes, - ClassHierarchyBuilder hierarchyBuilder, Class enumClass) { + void checkSupertypes( + CoreTypes coreTypes, + ClassHierarchyBuilder hierarchyBuilder, + Class enumClass, + Class? macroClass) { // This method determines whether the class (that's being built) its super // class appears both in 'extends' and 'implements' clauses and whether any // interface appears multiple times in the 'implements' clause. @@ -679,6 +682,27 @@ class SourceClassBuilder extends ClassBuilderImpl } } } + if (macroClass != null && !cls.isMacro && !cls.isAbstract) { + // TODO(johnniwinther): Merge this check with the loop above. + bool isMacroFound = false; + List interfaces = + hierarchyBuilder.getNodeFromClass(cls).superclasses; + for (int i = 0; !isMacroFound && i < interfaces.length; i++) { + if (interfaces[i].classNode == macroClass) { + isMacroFound = true; + } + } + interfaces = hierarchyBuilder.getNodeFromClass(cls).interfaces; + for (int i = 0; !isMacroFound && i < interfaces.length; i++) { + if (interfaces[i].classNode == macroClass) { + isMacroFound = true; + } + } + if (isMacroFound) { + addProblem(templateMacroClassNotDeclaredMacro.withArguments(name), + charOffset, noLength); + } + } void fail(NamedTypeBuilder target, Message message, TypeAliasBuilder? aliasBuilder) { diff --git a/pkg/front_end/lib/src/fasta/source/source_library_builder.dart b/pkg/front_end/lib/src/fasta/source/source_library_builder.dart index 878536f8e17..37de8c94cf5 100644 --- a/pkg/front_end/lib/src/fasta/source/source_library_builder.dart +++ b/pkg/front_end/lib/src/fasta/source/source_library_builder.dart @@ -243,6 +243,9 @@ class SourceLibraryBuilder extends LibraryBuilderImpl { List? _patchLibraries; + /// `true` if this is an augmentation library. + final bool isAugmentation; + SourceLibraryBuilder.internal( SourceLoader loader, Uri fileUri, @@ -253,8 +256,9 @@ class SourceLibraryBuilder extends LibraryBuilderImpl { Library library, LibraryBuilder? nameOrigin, Library? referencesFrom, - bool? referenceIsPartOwner, - bool isUnsupported) + {bool? referenceIsPartOwner, + required bool isUnsupported, + required bool isAugmentation}) : this.fromScopes( loader, fileUri, @@ -266,7 +270,8 @@ class SourceLibraryBuilder extends LibraryBuilderImpl { library, nameOrigin, referencesFrom, - isUnsupported); + isUnsupported: isUnsupported, + isAugmentation: isAugmentation); SourceLibraryBuilder.fromScopes( this.loader, @@ -279,7 +284,8 @@ class SourceLibraryBuilder extends LibraryBuilderImpl { this.library, this._nameOrigin, this.referencesFrom, - this.isUnsupported) + {required this.isUnsupported, + required this.isAugmentation}) : _languageVersion = packageLanguageVersion, currentTypeParameterScopeBuilder = _libraryTypeParameterScopeBuilder, referencesFromIndexed = @@ -469,7 +475,8 @@ class SourceLibraryBuilder extends LibraryBuilderImpl { LibraryBuilder? nameOrigin, Library? referencesFrom, bool? referenceIsPartOwner, - required bool isUnsupported}) + required bool isUnsupported, + required bool isAugmentation}) : this.internal( loader, fileUri, @@ -487,8 +494,9 @@ class SourceLibraryBuilder extends LibraryBuilderImpl { ..setLanguageVersion(packageLanguageVersion.version)), nameOrigin, referencesFrom, - referenceIsPartOwner, - isUnsupported); + referenceIsPartOwner: referenceIsPartOwner, + isUnsupported: isUnsupported, + isAugmentation: isAugmentation); @override bool get isPart => partOfName != null || partOfUri != null; @@ -520,7 +528,8 @@ class SourceLibraryBuilder extends LibraryBuilderImpl { loader: loader, isUnsupported: false, target: library, - origin: this); + origin: this, + isAugmentation: true); addPatchLibrary(augmentationLibrary); loader.registerUnparsedLibrarySource(augmentationLibrary, source); return augmentationLibrary; @@ -3896,9 +3905,11 @@ class SourceLibraryBuilder extends LibraryBuilderImpl { Builder member = patchDeclarations.current; // We need to inject all non-patch members into the origin library. This // should only apply to private members. + // For augmentation libraries, all members are injected into the origin + // library, regardless of privacy. if (member.isPatch) { // Ignore patches. - } else if (name.startsWith("_")) { + } else if (name.startsWith("_") || isAugmentation) { origin.injectMemberFromPatch(name, member); } else { origin.exportMemberFromPatch(name, member); diff --git a/pkg/front_end/lib/src/fasta/source/source_loader.dart b/pkg/front_end/lib/src/fasta/source/source_loader.dart index a429ef014a4..b77b5c9684f 100644 --- a/pkg/front_end/lib/src/fasta/source/source_loader.dart +++ b/pkg/front_end/lib/src/fasta/source/source_loader.dart @@ -328,7 +328,8 @@ class SourceLoader extends Loader { referenceIsPartOwner: referenceIsPartOwner, isUnsupported: origin?.library.isUnsupported ?? importUri.isScheme('dart') && - !target.uriTranslator.isLibrarySupported(importUri.path)); + !target.uriTranslator.isLibrarySupported(importUri.path), + isAugmentation: false); } /// Return `"true"` if the [dottedName] is a 'dart.library.*' qualifier for a @@ -1689,9 +1690,9 @@ severity: $severity ticker.logMs("Finished forwarders for $count procedures"); } - void resolveConstructors() { + void resolveConstructors(List libraryBuilders) { int count = 0; - for (SourceLibraryBuilder library in sourceLibraryBuilders) { + for (SourceLibraryBuilder library in libraryBuilders) { count += library.resolveConstructors(); } ticker.logMs("Resolved $count constructors"); @@ -1705,9 +1706,10 @@ severity: $severity } } - void finishTypeVariables(ClassBuilder object, TypeBuilder dynamicType) { + void finishTypeVariables(Iterable libraryBuilders, + ClassBuilder object, TypeBuilder dynamicType) { int count = 0; - for (SourceLibraryBuilder library in sourceLibraryBuilders) { + for (SourceLibraryBuilder library in libraryBuilders) { count += library.finishTypeVariables(object, dynamicType); } ticker.logMs("Resolved $count type-variable bounds"); @@ -2065,7 +2067,8 @@ severity: $severity List sourceClasses, Class enumClass) { for (SourceClassBuilder builder in sourceClasses) { if (builder.library.loader == this && !builder.isPatch) { - builder.checkSupertypes(coreTypes, hierarchyBuilder, enumClass); + builder.checkSupertypes( + coreTypes, hierarchyBuilder, enumClass, _macroClassBuilder?.cls); } } ticker.logMs("Checked supertypes"); diff --git a/pkg/front_end/messages.status b/pkg/front_end/messages.status index 0e1957f7a4a..b2e67dc1e73 100644 --- a/pkg/front_end/messages.status +++ b/pkg/front_end/messages.status @@ -578,6 +578,8 @@ LibraryDirectiveNotFirst/script2: Fail LibraryDirectiveNotFirst/script3: Fail ListLiteralTooManyTypeArguments/example: Fail LoadLibraryTakesNoArguments/example: Fail +MacroClassNotDeclaredMacro/analyzerCode: Fail +MacroClassNotDeclaredMacro/example: Fail MainNotFunctionDeclaration/analyzerCode: Fail MainNotFunctionDeclarationExported/analyzerCode: Fail MainNotFunctionDeclarationExported/part_wrapped_script: Fail diff --git a/pkg/front_end/messages.yaml b/pkg/front_end/messages.yaml index 043a64d4958..72c975af48c 100644 --- a/pkg/front_end/messages.yaml +++ b/pkg/front_end/messages.yaml @@ -5479,3 +5479,7 @@ EnumContainsValuesDeclaration: EnumImplementerContainsValuesDeclaration: problemMessage: "'#name' has 'Enum' as a superinterface and can't contain non-static member with name 'values'." + +MacroClassNotDeclaredMacro: + problemMessage: "Non-abstract class '#name' implements 'Macro' but isn't declared as a macro class." + correctionMessage: "Try adding the 'macro' class modifier." diff --git a/pkg/front_end/test/fasta/generator_to_string_test.dart b/pkg/front_end/test/fasta/generator_to_string_test.dart index 787ad86e8c0..9db87a411ff 100644 --- a/pkg/front_end/test/fasta/generator_to_string_test.dart +++ b/pkg/front_end/test/fasta/generator_to_string_test.dart @@ -95,7 +95,8 @@ Future main() async { new NoneTarget(new TargetFlags())), uriTranslator) .loader, - isUnsupported: false); + isUnsupported: false, + isAugmentation: false); libraryBuilder.markLanguageVersionFinal(); LoadLibraryBuilder loadLibraryBuilder = new LoadLibraryBuilder(libraryBuilder, dummyLibraryDependency, -1); diff --git a/pkg/front_end/test/macro_application/data/pkgs/macro/lib/macro.dart b/pkg/front_end/test/macro_application/data/pkgs/macro/lib/macro.dart index 5f4db94a541..15d9fb7cb9c 100644 --- a/pkg/front_end/test/macro_application/data/pkgs/macro/lib/macro.dart +++ b/pkg/front_end/test/macro_application/data/pkgs/macro/lib/macro.dart @@ -51,7 +51,8 @@ class FunctionTypesMacro1 implements FunctionTypesMacro { name, new DeclarationCode.fromParts([''' class $name { external ''', function.returnType.code, ''' method(); -}'''])); +}''' + ])); } } @@ -129,8 +130,14 @@ class MethodDeclarationsMacro1 implements MethodDeclarationsMacro { if (method.isSetter) { sb.write('s'); } + String name; + if (method.isOperator) { + name = 'operator'; + } else { + name = method.identifier.name; + } builder.declareInLibrary(new DeclarationCode.fromString(''' -void ${method.definingClass.name}_${method.identifier.name}GeneratedMethod_${sb}() {} +void ${method.definingClass.name}_${name}GeneratedMethod_${sb}() {} ''')); } } @@ -288,3 +295,31 @@ void ${constructor.definingClass.name}_${constructor.identifier ''')); } } + +macro + +class ToStringMacro implements ClassDeclarationsMacro { + const ToStringMacro(); + + FutureOr buildDeclarationsForClass(ClassDeclaration clazz, + ClassMemberDeclarationBuilder builder) async { + Iterable methods = await builder.methodsOf(clazz); + if (!methods.any((m) => m.identifier.name == 'toString')) { + Iterable fields = await builder.fieldsOf(clazz); + List parts = [''' + toString() { + return "${clazz.identifier.name}(''']; + String comma = ''; + for (FieldDeclaration field in fields) { + parts.add(comma); + parts.add('${field.identifier.name}=\${'); + parts.add(field.identifier.name); + parts.add('}'); + comma = ','; + } + parts.add(''')"; + }'''); + builder.declareInClass(new DeclarationCode.fromParts(parts)); + } + } +} \ No newline at end of file diff --git a/pkg/front_end/test/macro_application/data/tests/declarations.dart b/pkg/front_end/test/macro_application/data/tests/declarations.dart index dfed26d4704..7a69a25c066 100644 --- a/pkg/front_end/test/macro_application/data/tests/declarations.dart +++ b/pkg/front_end/test/macro_application/data/tests/declarations.dart @@ -112,7 +112,7 @@ void Class1_instanceSetter1GeneratedMethod_s() {} void set instanceSetter1(int? value) {} /*member: Class1.[]: -void Class1_[]GeneratedMethod_o() {} +void Class1_operatorGeneratedMethod_o() {} */ @MethodDeclarationsMacro1() int operator [](int i) => i; diff --git a/pkg/front_end/test/macro_application/data/tests/declarations.dart.expect b/pkg/front_end/test/macro_application/data/tests/declarations.dart.expect index 786028fbd98..dfe99d0d90e 100644 --- a/pkg/front_end/test/macro_application/data/tests/declarations.dart.expect +++ b/pkg/front_end/test/macro_application/data/tests/declarations.dart.expect @@ -19,6 +19,9 @@ class Class1 extends core::Object { constructor •() → self::Class1 : super core::Object::•() ; + method /* from org-dartlang-augmentation:/a/b/c/main.dart-18 */ Class1_GeneratedMethod_() → void {} + method /* from org-dartlang-augmentation:/a/b/c/main.dart-19 */ Class1_redirectGeneratedMethod_f() → void {} + method /* from org-dartlang-augmentation:/a/b/c/main.dart-20 */ Class1_factGeneratedMethod_f() → void {} @#C5 static factory redirect() → self::Class1 return new self::Class1::•(); @@ -68,6 +71,35 @@ static get topLevelGetter1() → core::int? return null; @#C8 static set topLevelSetter1(core::int? value) → void {} +static method /* from org-dartlang-augmentation:/a/b/c/main.dart-1 */ topLevelFunction1GeneratedMethod_() → void {} +static method /* from org-dartlang-augmentation:/a/b/c/main.dart-2 */ topLevelFunction2GeneratedMethod_e() → void {} +static method /* from org-dartlang-augmentation:/a/b/c/main.dart-3 */ topLevelField1GeneratedMethod_() → void {} +static method /* from org-dartlang-augmentation:/a/b/c/main.dart-4 */ topLevelField2GeneratedMethod_e() → void {} +static method /* from org-dartlang-augmentation:/a/b/c/main.dart-5 */ topLevelField3GeneratedMethod_f() → void {} +static method /* from org-dartlang-augmentation:/a/b/c/main.dart-6 */ topLevelField4GeneratedMethod_l() → void {} +static method /* from org-dartlang-augmentation:/a/b/c/main.dart-7 */ topLevelGetter1GeneratedMethod_g() → void {} +static method /* from org-dartlang-augmentation:/a/b/c/main.dart-8 */ topLevelSetter1GeneratedMethod_s() → void {} +static method /* from org-dartlang-augmentation:/a/b/c/main.dart-9 */ Class1GeneratedMethod_() → void {} +static method /* from org-dartlang-augmentation:/a/b/c/main.dart-10 */ Class1Introspection() → void { + core::print("constructors=''"); + core::print("fields='instanceField1','instanceField2','instanceField3'"); + core::print("methods='instanceMethod1','instanceGetter1','[]','instanceSetter1'"); +} +static method /* from org-dartlang-augmentation:/a/b/c/main.dart-11 */ Class1_instanceMethod1GeneratedMethod_() → void {} +static method /* from org-dartlang-augmentation:/a/b/c/main.dart-12 */ Class1_instanceGetter1GeneratedMethod_g() → void {} +static method /* from org-dartlang-augmentation:/a/b/c/main.dart-13 */ Class1_operatorGeneratedMethod_o() → void {} +static method /* from org-dartlang-augmentation:/a/b/c/main.dart-14 */ Class1_instanceField1GeneratedMethod_() → void {} +static method /* from org-dartlang-augmentation:/a/b/c/main.dart-15 */ Class1_instanceField2GeneratedMethod_f() → void {} +static method /* from org-dartlang-augmentation:/a/b/c/main.dart-16 */ Class1_instanceField3GeneratedMethod_fl() → void {} +static method /* from org-dartlang-augmentation:/a/b/c/main.dart-17 */ Class1_instanceSetter1GeneratedMethod_s() → void {} +static method /* from org-dartlang-augmentation:/a/b/c/main.dart-21 */ Class2GeneratedMethod_a() → void {} +static method /* from org-dartlang-augmentation:/a/b/c/main.dart-22 */ Class2Introspection() → void { + core::print("constructors="); + core::print("fields='instanceField1'"); + core::print("methods='instanceMethod1'"); +} +static method /* from org-dartlang-augmentation:/a/b/c/main.dart-23 */ Class2_instanceMethod1GeneratedMethod_a() → void {} +static method /* from org-dartlang-augmentation:/a/b/c/main.dart-24 */ Class2_instanceField1GeneratedMethod_() → void {} constants { #C1 = mac::ClassDeclarationsMacro1 {} diff --git a/pkg/front_end/test/macro_application/data/tests/subtypes.dart.expect b/pkg/front_end/test/macro_application/data/tests/subtypes.dart.expect index d4281835b79..07374b2a74b 100644 --- a/pkg/front_end/test/macro_application/data/tests/subtypes.dart.expect +++ b/pkg/front_end/test/macro_application/data/tests/subtypes.dart.expect @@ -52,6 +52,10 @@ external static method topLevelFunction3(self::C1 a) → self::C2; @#C1 @#C2 external static method topLevelFunction4(self::D1 a) → self::D2; +static method /* from org-dartlang-augmentation:/a/b/c/main.dart-1 */ topLevelFunction1GeneratedMethod_es() → void {} +static method /* from org-dartlang-augmentation:/a/b/c/main.dart-2 */ topLevelFunction2GeneratedMethod_s() → void {} +static method /* from org-dartlang-augmentation:/a/b/c/main.dart-3 */ topLevelFunction3GeneratedMethod_() → void {} +static method /* from org-dartlang-augmentation:/a/b/c/main.dart-4 */ topLevelFunction4GeneratedMethod_() → void {} constants { #C1 = mac::FunctionDeclarationsMacro2 {} diff --git a/pkg/front_end/test/macro_application/data/tests/to_string.dart b/pkg/front_end/test/macro_application/data/tests/to_string.dart new file mode 100644 index 00000000000..b5327e537a8 --- /dev/null +++ b/pkg/front_end/test/macro_application/data/tests/to_string.dart @@ -0,0 +1,47 @@ +// Copyright (c) 2022, 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. + +/*library: + + +*/ + +import 'package:macro/macro.dart'; + +@ToStringMacro() +/*class: A: +augment class A { + toString() { + return "A(a=${a},b=${b})"; + } +}*/ +class A { + var a; + var b; +} + +@ToStringMacro() +/*class: B: +augment class B { + toString() { + return "B(c=${c},d=${d},e=${e})"; + } +}*/ +class B { + var c, d; + var e; +} + +@ToStringMacro() +class C { + var f; + + @override + String toString() => 'C()'; +} + +class D { + var g; + var h; +} diff --git a/pkg/front_end/test/macro_application/data/tests/to_string.dart.expect b/pkg/front_end/test/macro_application/data/tests/to_string.dart.expect new file mode 100644 index 00000000000..431c8a2589f --- /dev/null +++ b/pkg/front_end/test/macro_application/data/tests/to_string.dart.expect @@ -0,0 +1,52 @@ +library /*isNonNullableByDefault*/; +import self as self; +import "package:macro/macro.dart" as mac; +import "dart:core" as core; + +import "package:macro/macro.dart"; + +@#C1 +class A extends core::Object { + field dynamic a = null; + field dynamic b = null; + synthetic constructor •() → self::A + : super core::Object::•() + ; + method /* from org-dartlang-augmentation:/a/b/c/main.dart-1 */ toString() → dynamic { + return "A(a=${this.{self::A::a}{dynamic}},b=${this.{self::A::b}{dynamic}})"; + } +} +@#C1 +class B extends core::Object { + field dynamic c = null; + field dynamic d = null; + field dynamic e = null; + synthetic constructor •() → self::B + : super core::Object::•() + ; + method /* from org-dartlang-augmentation:/a/b/c/main.dart-2 */ toString() → dynamic { + return "B(c=${this.{self::B::c}{dynamic}},d=${this.{self::B::d}{dynamic}},e=${this.{self::B::e}{dynamic}})"; + } +} +@#C1 +class C extends core::Object { + field dynamic f = null; + synthetic constructor •() → self::C + : super core::Object::•() + ; + @#C2 + method toString() → core::String + return "C()"; +} +class D extends core::Object { + field dynamic g = null; + field dynamic h = null; + synthetic constructor •() → self::D + : super core::Object::•() + ; +} + +constants { + #C1 = mac::ToStringMacro {} + #C2 = core::_Override {} +} diff --git a/pkg/front_end/test/macro_application/macro_application_test.dart b/pkg/front_end/test/macro_application/macro_application_test.dart index fd317c75e29..647c544ea6c 100644 --- a/pkg/front_end/test/macro_application/macro_application_test.dart +++ b/pkg/front_end/test/macro_application/macro_application_test.dart @@ -184,7 +184,9 @@ class MacroDataComputer extends DataComputer { in macroApplicationData.classTypesResults.entries) { if (entry.key.cls == cls) { for (MacroExecutionResult result in entry.value) { - sb.write('\n${codeToString(result.augmentations.first)}'); + if (result.augmentations.isNotEmpty) { + sb.write('\n${codeToString(result.augmentations.first)}'); + } } } } @@ -192,7 +194,9 @@ class MacroDataComputer extends DataComputer { in macroApplicationData.classDeclarationsResults.entries) { if (entry.key.cls == cls) { for (MacroExecutionResult result in entry.value) { - sb.write('\n${codeToString(result.augmentations.first)}'); + if (result.augmentations.isNotEmpty) { + sb.write('\n${codeToString(result.augmentations.first)}'); + } } } } @@ -200,7 +204,9 @@ class MacroDataComputer extends DataComputer { in macroApplicationData.classDefinitionsResults.entries) { if (entry.key.cls == cls) { for (MacroExecutionResult result in entry.value) { - sb.write('\n${codeToString(result.augmentations.first)}'); + if (result.augmentations.isNotEmpty) { + sb.write('\n${codeToString(result.augmentations.first)}'); + } } } } diff --git a/pkg/front_end/test/macros/data/tests/macro_declarations.dart b/pkg/front_end/test/macros/data/tests/macro_declarations.dart index c780d613c87..1ddcfd501df 100644 --- a/pkg/front_end/test/macros/data/tests/macro_declarations.dart +++ b/pkg/front_end/test/macros/data/tests/macro_declarations.dart @@ -38,20 +38,20 @@ macro class ImplementsAlias implements Alias {} macro class MixinAlias with Alias {} -class ExtendsNoKeyword extends Macro {} +class /*error: error=MacroClassNotDeclaredMacro*/ExtendsNoKeyword extends Macro {} -class ImplementsNoKeyword implements Macro {} +class /*error: error=MacroClassNotDeclaredMacro*/ImplementsNoKeyword implements Macro {} -class MixinNoKeyword with Macro {} +class /*error: error=MacroClassNotDeclaredMacro*/MixinNoKeyword with Macro {} -class ExtendsAliasNoKeyword extends Alias {} +class /*error: error=MacroClassNotDeclaredMacro*/ExtendsAliasNoKeyword extends Alias {} -class ImplementsAliasNoKeyword implements Alias {} +class /*error: error=MacroClassNotDeclaredMacro*/ImplementsAliasNoKeyword implements Alias {} -class MixinAliasNoKeyword with Alias {} +class /*error: error=MacroClassNotDeclaredMacro*/MixinAliasNoKeyword with Alias {} -class NamedMixin1NoKeyword = Macro with _Mixin; +class /*error: error=MacroClassNotDeclaredMacro*/NamedMixin1NoKeyword = Macro with _Mixin; -class NamedMixin2NoKeyword = Object with Macro; +class /*error: error=MacroClassNotDeclaredMacro*/NamedMixin2NoKeyword = Object with Macro; void main() {} diff --git a/pkg/front_end/test/macros/macro_test.dart b/pkg/front_end/test/macros/macro_test.dart index e583b6be846..a20eaf24e1c 100644 --- a/pkg/front_end/test/macros/macro_test.dart +++ b/pkg/front_end/test/macros/macro_test.dart @@ -17,6 +17,7 @@ import 'package:front_end/src/fasta/builder/library_builder.dart'; import 'package:front_end/src/fasta/builder/member_builder.dart'; import 'package:front_end/src/fasta/kernel/macro.dart'; import 'package:front_end/src/testing/id_testing_helper.dart'; +import 'package:front_end/src/testing/id_testing_utils.dart'; import 'package:kernel/ast.dart' hide Arguments; Future main(List args) async { @@ -77,6 +78,17 @@ class MacroDataComputer extends DataComputer { .computeForLibrary(library); } + @override + bool get supportsErrors => true; + + @override + Features? computeErrorData( + TestResultData testResultData, Id id, List errors) { + Features features = new Features(); + features[Tags.error] = errorsToText(errors, useCodes: true); + return features; + } + @override DataInterpreter get dataValidator => const FeaturesDataInterpreter(); @@ -91,6 +103,7 @@ class Tags { static const String appliedMacros = 'appliedMacros'; static const String macroClassIds = 'macroClassIds'; static const String macroInstanceIds = 'macroInstanceIds'; + static const String error = 'error'; } String constructorNameToString(String constructorName) { diff --git a/pkg/front_end/test/spell_checking_list_messages.txt b/pkg/front_end/test/spell_checking_list_messages.txt index 3230d251102..e6964cdc50e 100644 --- a/pkg/front_end/test/spell_checking_list_messages.txt +++ b/pkg/front_end/test/spell_checking_list_messages.txt @@ -45,6 +45,7 @@ js_util libraries.json list.filled loadlibrary +macro migrate name.#name name.stack diff --git a/pkg/front_end/tool/dart_doctest_impl.dart b/pkg/front_end/tool/dart_doctest_impl.dart index 6481a4c22f6..09a9e7f6149 100644 --- a/pkg/front_end/tool/dart_doctest_impl.dart +++ b/pkg/front_end/tool/dart_doctest_impl.dart @@ -834,6 +834,7 @@ class DocTestIncrementalCompiler extends IncrementalCompiler { scope: libraryBuilder.scope.createNestedScope("dartdoctest"), nameOrigin: libraryBuilder, isUnsupported: false, + isAugmentation: false, ); if (libraryBuilder is DillLibraryBuilder) {