| Severity | Error |
| Reported by | Metalama.Framework |
| Message | The aspect '<AspectType>' cannot introduce the custom attribute '<AttributeType>' into '<TargetDeclaration>' because this attribute is already present on the declaration. To keep or replace the existing attribute, set the 'whenExists' parameter to 'Ignore' or 'Override'. |
Cause
The IntroduceAttribute advice adds a custom attribute to a declaration. Its whenExists parameter, of type OverrideStrategy, determines what happens when the declaration already has an attribute of the same type. The default value, OverrideStrategy.Default, is the same as OverrideStrategy.Fail.
Metalama reports LAMA0521 when the declaration already has an attribute of the type being introduced, whether written in source code or added by another aspect, and whenExists is Fail or Default. Metalama compares only the attribute type, not the constructor arguments.
In the following example, the DefaultDebuggerDisplay aspect adds a [DebuggerDisplay] attribute to each target class, but the Product class already has one.
1using Metalama.Framework.Aspects;
2using Metalama.Framework.Code;
3using Metalama.Framework.Code.DeclarationBuilders;
4using System.Diagnostics;
5
6namespace Doc.LAMA0521.Error;
7
8public class DefaultDebuggerDisplayAttribute : TypeAspect
9{
10 public override void BuildAspect( IAspectBuilder<INamedType> builder )
11 {
12 // Error: the default strategy fails when the type already has a [DebuggerDisplay] attribute.
13 builder.IntroduceAttribute(
14 AttributeConstruction.Create(
15 typeof(DebuggerDisplayAttribute),
16 constructorArguments: new object?[] { "{Name}" } ) );
17 }
18}
19
1using System.Diagnostics;
2
3namespace Doc.LAMA0521.Error;
4
5// The class already has a [DebuggerDisplay] attribute.
6[DefaultDebuggerDisplay]
7[DebuggerDisplay( "Product {Name} ({Price})" )]
Error LAMA0521: The aspect 'DefaultDebuggerDisplay' cannot introduce the custom attribute 'DebuggerDisplayAttribute' into 'Product' because this attribute is already present on the declaration. To keep or replace the existing attribute, set the 'whenExists' parameter to 'Ignore' or 'Override'.
8internal class Product
9{
10 public string Name { get; set; } = "";
11
12 public decimal Price { get; set; }
13}
14
15[DefaultDebuggerDisplay]
16internal class Customer
17{
18 public string Name { get; set; } = "";
19}
20
Resolution
Decide how the aspect handles an existing attribute, and pass the corresponding whenExists argument to IntroduceAttribute:
- OverrideStrategy.Ignore keeps the existing attribute and skips the introduction. Use it when the attribute written by the user should win. The following example shows this fix:
Productkeeps its own [DebuggerDisplay] attribute, andCustomerreceives the default one. - OverrideStrategy.Override removes the existing attributes of this type and adds the new one.
- OverrideStrategy.New adds the attribute even if one already exists. Use it only for attribute types that allow multiple instances (
AllowMultiple = true).
Alternatively, remove the attribute from the declaration, or don't apply the aspect to it.
1using Metalama.Framework.Aspects;
2using Metalama.Framework.Code;
3using Metalama.Framework.Code.DeclarationBuilders;
4using System.Diagnostics;
5
6namespace Doc.LAMA0521.Fixed;
7
8public class DefaultDebuggerDisplayAttribute : TypeAspect
9{
10 public override void BuildAspect( IAspectBuilder<INamedType> builder )
11 {
12 // Fixed: an existing [DebuggerDisplay] attribute is kept.
13 builder.IntroduceAttribute(
14 AttributeConstruction.Create(
15 typeof(DebuggerDisplayAttribute),
16 constructorArguments: new object?[] { "{Name}" } ),
17 whenExists: OverrideStrategy.Ignore );
18 }
19}
20
1using System.Diagnostics;
2
3namespace Doc.LAMA0521.Fixed;
4
5// The aspect keeps the existing [DebuggerDisplay] attribute.
6[DefaultDebuggerDisplay]
7[DebuggerDisplay( "Product {Name} ({Price})" )]
8internal class Product
9{
10 public string Name { get; set; } = "";
11
12 public decimal Price { get; set; }
13}
14
15[DefaultDebuggerDisplay]
16internal class Customer
17{
18 public string Name { get; set; } = "";
19}
20
1using System.Diagnostics;
2
3namespace Doc.LAMA0521.Fixed;
4
5// The aspect keeps the existing [DebuggerDisplay] attribute.
6[DefaultDebuggerDisplay]
7[DebuggerDisplay("Product {Name} ({Price})")]
8internal class Product
9{
10 public string Name { get; set; } = "";
11
12 public decimal Price { get; set; }
13}
14
15[DefaultDebuggerDisplay]
16[DebuggerDisplay("{Name}")]
17internal class Customer
18{
19 public string Name { get; set; } = "";
20}
21