Open sandboxFocusImprove this doc

LAMA0556: A custom attribute can't be added to a compiler-synthesized member

Severity Error
Reported by Metalama.Framework
Message The aspect '<AspectType>' cannot introduce a custom attribute on '<Member>' because the compiler synthesizes that member and there is no declaration on which to write the attribute.

Cause

When an aspect introduces a type such as a record, the C# compiler synthesizes some of its members from the type declaration: for instance PrintMembers, Equals, GetHashCode, the copy constructor and the primary constructor of a record. These members are present in the code model, but they have no syntax of their own, so Metalama has no declaration on which to write an attribute.

Metalama reports LAMA0556 when an aspect calls IntroduceAttribute on such a member, instead of ignoring the attribute silently. The <Member> placeholder is the targeted member.

In the following example, the GenerateRecord aspect introduces a record and tries to add an attribute to its synthesized PrintMembers method.

1using Metalama.Framework.Aspects;
2using Metalama.Framework.Code;
3using Metalama.Framework.Code.DeclarationBuilders;
4using System.Diagnostics.CodeAnalysis;
5using System.Linq;
6
7namespace Doc.LAMA0556.Error;
8
9public class GenerateRecordAttribute : TypeAspect
10{
11    public override void BuildAspect( IAspectBuilder<INamedType> builder )
12    {
13        var record = builder.IntroduceRecord(
14            "OrderLine",
15            buildRecord: r =>
16            {
17                r.Accessibility = Accessibility.Public;
18                r.AddPositionalParameter( "Quantity", typeof(int) );
19            } );
20
21        var printMembers = record.Declaration.Methods.OfName( "PrintMembers" ).Single();
22
23        // Error: the compiler synthesizes PrintMembers, so there is no declaration on which to write the attribute.
24        builder.With( printMembers ).IntroduceAttribute( AttributeConstruction.Create( typeof(ExcludeFromCodeCoverageAttribute) ) );
25    }
26}
27
1namespace Doc.LAMA0556.Error;
2
3[GenerateRecord]
Error LAMA0556: The aspect 'GenerateRecord' cannot introduce a custom attribute on 'OrderService.OrderLine.PrintMembers(StringBuilder)' because the compiler synthesizes that member and there is no declaration on which to write the attribute.

4public partial class OrderService { }
5

Resolution

Add the attribute to a declaration that exists in the source code:

  • For a property that a positional parameter of a record declares, add the attribute to the property. Metalama writes it on the parameter with the property: target.
  • For the type itself or for a member that you introduce explicitly, add the attribute to that declaration.

In the following example, the attribute is added to the property that the Quantity positional parameter declares.

1using Metalama.Framework.Aspects;
2using Metalama.Framework.Code;
3using Metalama.Framework.Code.DeclarationBuilders;
4using System.Diagnostics.CodeAnalysis;
5using System.Linq;
6
7namespace Doc.LAMA0556.Fixed;
8
9public class GenerateRecordAttribute : TypeAspect
10{
11    public override void BuildAspect( IAspectBuilder<INamedType> builder )
12    {
13        var record = builder.IntroduceRecord(
14            "OrderLine",
15            buildRecord: r =>
16            {
17                r.Accessibility = Accessibility.Public;
18                r.AddPositionalParameter( "Quantity", typeof(int) );
19            } );
20
21        var quantity = record.Declaration.Properties.OfName( "Quantity" ).Single();
22
23        // The attribute is written on the positional parameter, which is a declaration.
24        builder.With( quantity ).IntroduceAttribute( AttributeConstruction.Create( typeof(ExcludeFromCodeCoverageAttribute) ) );
25    }
26}
27
Source Code
1namespace Doc.LAMA0556.Fixed;
2


3[GenerateRecord]
4public partial class OrderService { }
5
Transformed Code
1using System.Diagnostics.CodeAnalysis;
2
3namespace Doc.LAMA0556.Fixed;
4
5[GenerateRecord]
6public partial class OrderService
7{
8    public record OrderLine([property: ExcludeFromCodeCoverage] int Quantity)
9    {
10    }
11}
12

See also