blob: 6dcbd9ec179ce6d9ea4d36684ed96d7f9d91efcb [file] [log] [blame]
// Copyright (c) 2024, 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:analyzer/file_system/file_system.dart';
import 'support/configuration_files.dart';
/// Getters and methods that will return the declarations of macros that are
/// useful for testing.
///
/// The macros do not include imports. They are designed to be passed into
/// [addMacros], which will add the necessary imports automatically.
mixin TestMacros on ConfigurationFilesMixin {
/// Adds support for macros to the `package_config.json` file and creates a
/// `macros.dart` file that defines the given [macros]. The macros should not
/// include imports, the imports for macros will be added automatically.
void addMacros(List<String> macros, {bool isFlutter = false}) {
writeTestPackageConfig(
flutter: isFlutter,
macro: true,
);
newFile(
'$testPackageRootPath/lib/macros.dart',
[
'''
// There is no public API exposed yet, the in-progress API lives here.
import 'package:macros/macros.dart';
''',
...macros
].join('\n'),
);
}
/// Return the declaration of a macro that will add a member to a library.
///
/// The text of the member to be declared is provided as an argument to the
/// macro that is returned. For example, the following can be used to generate
/// a top level function in the library containing the class `C`:
/// ```dart
/// @DeclareInLibrary('void generatedTopLevelFunction() {}')
/// class C {}
/// ```
String declareInLibraryMacro() {
return '''
macro class DeclareInLibrary
implements ClassDeclarationsMacro, FunctionDeclarationsMacro {
final String code;
const DeclareInLibrary(this.code);
@override
buildDeclarationsForClass(clazz, builder) async {
await _declare(builder);
}
@override
buildDeclarationsForFunction(clazz, builder) async {
await _declare(builder);
}
Future<void> _declare(DeclarationBuilder builder) async {
builder.declareInLibrary(
DeclarationCode.fromString(code),
);
}
}
''';
}
/// Return the declaration of a macro that will add a member to a type.
///
/// The text of the member to be declared is provided as an argument to the
/// macro that is returned. For example, the following can be used to generate
/// a method in the class `C`:
/// ```dart
/// @DeclareInType(' void generatedMethod() {}')
/// class C {}
/// ```
/// (Adding the indent makes the content of the generated code look nicer, but
/// isn't required.)
String declareInTypeMacro() {
return '''
macro class DeclareInType
implements
ClassDeclarationsMacro,
ConstructorDeclarationsMacro,
FieldDeclarationsMacro,
MethodDeclarationsMacro {
final String code;
const DeclareInType(this.code);
@override
buildDeclarationsForClass(clazz, builder) async {
_declare(builder);
}
@override
buildDeclarationsForConstructor(constructor, builder) async {
_declare(builder);
}
@override
buildDeclarationsForField(field, builder) async {
_declare(builder);
}
@override
buildDeclarationsForMethod(method, builder) async {
_declare(builder);
}
void _declare(MemberDeclarationBuilder builder) {
builder.declareInType(
DeclarationCode.fromString(code),
);
}
}
''';
}
String declareTypesPhaseMacro() {
return '''
macro class DeclareTypesPhase
implements ClassTypesMacro, FunctionTypesMacro {
final String typeName;
final String code;
const DeclareTypesPhase(this.typeName, this.code);
@override
buildTypesForClass(clazz, builder) async {
await _declare(builder);
}
@override
buildTypesForFunction(clazz, builder) async {
await _declare(builder);
}
Future<void> _declare(TypeBuilder builder) async {
builder.declareType(
typeName,
DeclarationCode.fromString(code),
);
}
}
''';
}
File newFile(String path, String content);
}