Skip to content

When generating code using metadata in macros, the prefix is not included. #3867

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
yiiim opened this issue Jun 3, 2024 · 2 comments
Closed
Labels
request Requests to resolve a particular developer problem

Comments

@yiiim
Copy link

yiiim commented Jun 3, 2024

When using positionalArguments and namedArguments from ConstructorMetadataAnnotation for macro code generation, none of the types include a prefix.

macro code:

import 'dart:async';

import 'package:collection/collection.dart';
import 'package:macros/macros.dart';

final _selfLibrary = Uri.parse('package:test/src/test_macros.dart');

enum MyEnum { a, b, c }

class MyFieldMetaData {
  final String alias;
  final MyEnum myEnum;

  const MyFieldMetaData(this.alias, {required this.myEnum});
}

macro class MyMacro implements ClassDeclarationsMacro, ClassDefinitionMacro {
  const MyMacro();

  @override
  FutureOr<void> buildDefinitionForClass(ClassDeclaration clazz, TypeDefinitionBuilder builder) async {
    final myMetaDataIdentifier = await builder.resolveIdentifier(_selfLibrary, 'MyFieldMetaData');
    final myMetaDataDeclaration = await builder.declarationOf(myMetaDataIdentifier) as TypeDeclaration;
    final myMetaDataConstructor = (await builder.constructorsOf(myMetaDataDeclaration)).first;

    final methods = await builder.methodsOf(clazz);
    final fieldMetaDatasMethod = methods.firstWhereOrNull((c) => c.identifier.name == 'fieldMetaDatas');
    if (fieldMetaDatasMethod == null) return;
    final methodBuilder = await builder.buildMethod(fieldMetaDatasMethod.identifier);
    final fields = await builder.fieldsOf(clazz);
    methodBuilder.augment(
      FunctionBodyCode.fromParts(
        [
          "=> [",
          ...(await fields.map<Future<List<Object>>>((e) async {
            ConstructorMetadataAnnotation? metaDataConstructor;
            for (var element in e.metadata) {
              if (element is ConstructorMetadataAnnotation) {
                  final fieldAnnotationType = await builder.declarationOf(element.constructor);
                  if (fieldAnnotationType.identifier == myMetaDataConstructor.identifier) {
                    metaDataConstructor = element;
                    break;
                  }
              }
            }
            if (metaDataConstructor == null) {
              return <Object>[];
            }
            return <Object>[
              myMetaDataIdentifier,
              "(",
              // use the ConstructorMetadataAnnotation's Code
              ...metaDataConstructor.positionalArguments.map((e) => e.parts).expand((e) => e),
              if(metaDataConstructor.positionalArguments.isNotEmpty) ",",
              ...metaDataConstructor.namedArguments.entries
                  .map<List<Object>>(
                    (e) => [
                      e.key,
                      ":",
                      e.value,
                      ",",
                    ],
                  )
                  .expand((e) => e),
              ")\n",
            ];
          }).wait)
              .expand((e) => e),
          "];",
        ],
      ),
    );
  }

  @override
  FutureOr<void> buildDeclarationsForClass(ClassDeclaration clazz, MemberDeclarationBuilder builder) async {
    final myMetaDataIdentifier = await builder.resolveIdentifier(_selfLibrary, 'MyFieldMetaData');
    builder.declareInType(
      DeclarationCode.fromParts([
        NamedTypeAnnotationCode(
          name: await builder.resolveIdentifier(Uri.parse('dart:core'), 'List'),
          typeArguments: [
            NamedTypeAnnotationCode(name: myMetaDataIdentifier),
          ],
        ),
        ' fieldMetaDatas();',
      ]),
    );
  }
}

user code:

@MyMacro()
class Test {
  const Test(this.myField);

  @MyFieldMetaData('lol', myEnum: MyEnum.a)
  final String myField;
}

void main(List<String> args) {
  final test = Test('test');
  test.fieldMetaDatas();
}

generated code:

augment library 'package:test/src/test.dart';

import 'dart:core' as prefix0;
import 'package:test/src/test_macros.dart' as prefix1;

augment class Test {
prefix0.List<prefix1.MyFieldMetaData> fieldMetaDatas();
  augment prefix0.List<prefix1.MyFieldMetaData> fieldMetaDatas() => [prefix1.MyFieldMetaData('lol',myEnum:MyEnum.a,)
];
}

console:

Undefined name 'MyEnum'.
Try correcting the name to one that is defined, or defining the name.
@yiiim yiiim added the request Requests to resolve a particular developer problem label Jun 3, 2024
@jakemac53
Copy link
Contributor

Yes, metadata in general is not fully implemented. The analyzer has half of an implementation, but it only provides "code" as raw strings not Identifiers, so you don't get prefixes, and only code literals will actually work.

@jakemac53
Copy link
Contributor

closing in favor of #3847, the entire design for metadata is a bit in flux right now due to issues implementing the existing design

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
request Requests to resolve a particular developer problem
Projects
None yet
Development

No branches or pull requests

2 participants