sdk
sdk copied to clipboard
Macro-augmented constructor loses const-ness and default values
Code:
void m() {
const Foo(); // 🔥 error: The constructor being called isn't a const constructor. (const_with_non_const)
}
@MyMacro()
class Foo {
const Foo({int a = 0});
}
Macro:
macro class MyMacro implements ClassDefinitionMacro {
const MyMacro();
@override
FutureOr<void> buildDefinitionForClass(ClassDeclaration clazz, TypeDefinitionBuilder builder) async {
final List<ConstructorDeclaration> constructors = await builder.constructorsOf(clazz);
final ConstructorDefinitionBuilder constructorBuilder = await builder.buildConstructor(constructors.first.identifier);
constructorBuilder.augment(initializers: [DeclarationCode.fromParts(['assert(1 != 0)'])]);
}
}
Generated code:
augment class Foo {
augment Foo({prefix1.int a, }) // 🔥 error: The parameter 'a' can't have a value of 'null' because of its type, but the implicit default value is 'null'. (missing_default_value_for_parameter)
: assert(1 != 0);
}
There are two issues (see the 🔥 lines above) when augmenting a constructor with ConstructorDefinitionBuilder.augment in a macro:
- The constructor (which was declared
constin the original code) is apparently no longerconst. - The default value of the constructor parameter is lost.
Should the constructor augmentation inherit these traits from the augmented constructor? Or is the macro maybe generating incomplete code and should instead generate:
augment class Foo {
augment const Foo({prefix1.int a = 0, })
: assert(1 != 0);
}
Or am I holding this wrong?
Summary: This issue reports that macro-augmented constructors lose their const status and default parameter values. The macro augments the constructor with an assertion, but the generated code doesn't preserve the original const declaration or the default value for the parameter. This leads to errors when trying to use the constructor with const.
I do see two bugs here in the macro impls:
- The
constkeyword should be repeated (I can work on fixing that, should be easy). It should also be a compile time error if it is not repeated, so we may need to file issues and/or create language tests for this. - The original default value should be used by the cfe/analyzer - it is not intended to be repeated . So issues need to be filed on one or both (plus a language test added if we don't have one already). It is actually a compile time error for an augmentation to specify default values (they cannot even repeat the original ones).
You should now see the const keyword being added (once this commit makes its way to you). The other part is still a WIP.
I can confirm that the const keyword is now added correctly. The issue with the default value still remains.
I am seeing a potentially new issue with super parameters when augmenting constructors (at least I don't remember it being there before):
class Base {
const Base({this.foo});
final int? foo;
}
class Sub extends Base {
const Sub({super.foo});
}
augment class Sub {
augment const Sub({super.foo}); // 🔥 error: No associated named super constructor parameter. (super_formal_parameter_without_associated_named)
}
Edit: Actually, this could be related to https://github.com/dart-lang/sdk/issues/55428 (although there the constructor is not augmented).