Skip to content

Macro: Macros Add abstract keyword To Sealed Type Augmentations Which Breaks Code #59761

@mcmah309

Description

@mcmah309

For the code

final variantRegex = RegExp(r'^(\w+)\((.*?)\)$');

macro class Enum
    implements ClassTypesMacro, ClassDeclarationsMacro, ClassDefinitionMacro {


      final String? variant1, variant2;

  const Enum([this.variant1, this.variant2]);

  List<String> get variants => [
    if(variant1 != null) variant1!,
    if(variant2 != null) variant2!
    ];

  @override
  FutureOr<void> buildTypesForClass(ClassDeclaration clazz, ClassTypeBuilder builder) {
    final className = clazz.identifier.name;
    for(final variant in variants) {
      final match = variantRegex.firstMatch(variant);
      if (match == null) {
        throw Exception("Variant `$variant` is not valid");
      }
      final newTypeName = match.group(1)!;
      final fieldTypes = match.group(2)!.split(',').map((s) => s.trim()).toList();
      int count = 1;
      final fields = StringBuffer();
      final constructors = StringBuffer();
      for(final fieldType in fieldTypes) {
        fields.write("\t$fieldType v$count;\n");
        constructors.write("this.v$count,");
      }
      builder.declareType(newTypeName, DeclarationCode.fromString('''
final class $className\$$newTypeName implements $className {
$fields

  $className\$$newTypeName._($constructors);
}'''));
    }
  }

  @override
  Future<void> buildDeclarationsForClass(
      ClassDeclaration clazz, MemberDeclarationBuilder builder) async {
        final className = clazz.identifier.name;
      for(final variant in variants) {
        final match = variantRegex.firstMatch(variant);
        if (match == null) {
          throw Exception("Variant `$variant` is not valid");
        }
        final newTypeName = match.group(1)!;
        final fieldTypes = match.group(2)!.split(',').map((s) => s.trim()).toList();
        int count = 1;
        final constructor = StringBuffer();
        for(final fieldType in fieldTypes) {
          constructor.write("$fieldType v$count,");
        }
        builder.declareInType(DeclarationCode.fromString("factory $className.$newTypeName($constructor);"));
      }
  }

  @override
  Future<void> buildDefinitionForClass(
      ClassDeclaration clazz, TypeDefinitionBuilder builder) async {
        final className = clazz.identifier.name;
        final constructors = await builder.constructorsOf(clazz);
        for (final constructor in constructors) {
          final constructorName = constructor.identifier.name;
          final constructorBuilder = await builder.buildConstructor(constructor.identifier);
          constructorBuilder.augment(body: FunctionBodyCode.fromString("= $className\$$constructorName._;"));
        }
  }
}

Used like

@Enum("Variant(int)")
sealed class W {
}

void main() {
  final w = W.Variant(1);
}

Gives the following error

org-dartlang-augmentation:/workspaces/algebraic_types/bin/algebraic_types.dart-1:3:18: Error: A 'sealed' class can't be marked 'abstract' because it's already implicitly abstract.
Try removing the 'abstract' keyword.
augment abstract sealed class W {
                 ^^^^^^
org-dartlang-augmentation:/workspaces/algebraic_types/bin/algebraic_types.dart-1:4:27: Error: Expected a function body or '=>'.
Try adding {}.
factory W.Variant(int v1,);
                          ^
org-dartlang-augmentation:/workspaces/algebraic_types/bin/algebraic_types.dart-2:5:18: Error: A 'sealed' class can't be marked 'abstract' because it's already implicitly abstract.
Try removing the 'abstract' keyword.
augment abstract sealed class W {
                 ^^^^^^
org-dartlang-augmentation:/workspaces/algebraic_types/bin/algebraic_types.dart-2:6:19: Error: Constructor 'W.Variant' conflicts with an existing constructor of the same name in the augmented class.
Try changing the name of the constructor or adding an 'augment' modifier.
  augment factory W.Variant(prefix0.int v1, ) = W$Variant._;
                  ^
org-dartlang-augmentation:/workspaces/algebraic_types/bin/algebraic_types.dart-1:4:9: Context: This is the existing constructor.
factory W.Variant(int v1,);

Dart info

#### General info

- Dart 3.7.0-232.0.dev (dev) (None) on "linux_x64"
- on linux / Linux 6.6.63 #1-NixOS SMP PREEMPT_DYNAMIC Fri Nov 22 14:38:37 UTC 2024
- locale is en_US.UTF-8

#### Project info

- sdk constraint: '3.7.0-232.0.dev'
- dependencies: json
- dev_dependencies: lints, test

Metadata

Metadata

Assignees

No one assigned

    Labels

    area-languageDart language related items (some items might be better tracked at github.com/dart-lang/language).feature-macrosImplementation of the macros featurepkg-macrosThe experimental package:_macros librarytype-bugIncorrect behavior (everything from a crash to more subtle misbehavior)

    Type

    No type

    Projects

    Status

    Done

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions