| Severity | Error |
| Reported by | Metalama.Framework |
| Message | Aspect '<AspectType>' cannot be applied to <DeclarationKind> '<Declaration>', because this aspect does not implement the '<InterfaceType>' interface. |
Cause
An aspect declares the kinds of declarations it can be applied to by implementing the generic IAspect interface, where T is the code model type of the target, for instance IAspect<IMethod> for methods or IAspect<INamedType> for types. An aspect can implement this interface several times to support several kinds of declarations.
When Metalama executes an aspect, it checks that the aspect class implements IAspect<T> for the kind of the target declaration. If it doesn't, Metalama reports LAMA0003 and skips the aspect. In the message, <DeclarationKind> is the kind of the target declaration and <InterfaceType> is the interface that the aspect would need to implement.
The C# compiler normally prevents this situation: the base aspect classes, such as TypeAspect or OverrideMethodAspect, carry an [AttributeUsage] attribute that matches the interfaces they implement. However, when you implement IAspect<T> directly in a class derived from Attribute and don't add [AttributeUsage], or when [AttributeUsage] allows more targets than the implemented interfaces, C# accepts the attribute on declarations that the aspect can't handle.
In the following example, the Describe aspect implements only IAspect<INamedType>, but it has no [AttributeUsage] and is applied to a method.
1using Metalama.Framework.Advising;
2using Metalama.Framework.Aspects;
3using Metalama.Framework.Code;
4using Metalama.Framework.Eligibility;
5using System;
6
7namespace Doc.LAMA0003.Error;
8
9// This aspect targets types, but the class has no [AttributeUsage], so C# accepts it on any declaration.
10public class DescribeAttribute : Attribute, IAspect<INamedType>
11{
12 public void BuildAspect( IAspectBuilder<INamedType> builder )
13 {
14 builder.IntroduceMethod( nameof(this.Describe) );
15 }
16
17 public void BuildEligibility( IEligibilityBuilder<INamedType> builder ) { }
18
19 [Template]
20 public string Describe() => meta.Target.Type.Name;
21}
22
1namespace Doc.LAMA0003.Error;
2
3internal partial class Invoice
4{
5 public decimal Amount { get; set; }
6
7 // Error: [Describe] is a type aspect, but it's applied to a method.
8 [Describe]
Error LAMA0003: Aspect 'Describe' cannot be applied to method 'Invoice.GetTotal(decimal)', because this aspect does not implement the 'IAspect<IMethod>' interface.
9 public decimal GetTotal( decimal taxRate ) => this.Amount * (1 + taxRate);
10}
11
Resolution
Depending on your intent, do one of the following:
- Apply the aspect to a declaration of a kind that it supports.
- Add an [AttributeUsage] attribute to the aspect class that allows only the kinds of declarations for which the aspect implements IAspect, so that the C# compiler reports wrong usages. Alternatively, derive the aspect from a base class such as TypeAspect, which already has the right
[AttributeUsage]. - If the aspect should support this kind of declaration, implement
IAspect<T>for this kind too.
In the following example, the type aspect is applied to the type, and [AttributeUsage] prevents applying it to a method again.
1using Metalama.Framework.Advising;
2using Metalama.Framework.Aspects;
3using Metalama.Framework.Code;
4using Metalama.Framework.Eligibility;
5using System;
6
7namespace Doc.LAMA0003.Fixed;
8
9// Fixed: [AttributeUsage] restricts the aspect to the kinds of declarations it supports.
10[AttributeUsage( AttributeTargets.Class | AttributeTargets.Struct )]
11public class DescribeAttribute : Attribute, IAspect<INamedType>
12{
13 public void BuildAspect( IAspectBuilder<INamedType> builder )
14 {
15 builder.IntroduceMethod( nameof(this.Describe) );
16 }
17
18 public void BuildEligibility( IEligibilityBuilder<INamedType> builder ) { }
19
20 [Template]
21 public string Describe() => meta.Target.Type.Name;
22}
23
1namespace Doc.LAMA0003.Fixed;
2
3// Fixed: the type aspect is applied to the type.
4[Describe]
5internal partial class Invoice
6{
7 public decimal Amount { get; set; }
8
9 public decimal GetTotal( decimal taxRate ) => this.Amount * (1 + taxRate);
10}
11
1namespace Doc.LAMA0003.Fixed;
2
3// Fixed: the type aspect is applied to the type.
4[Describe]
5internal partial class Invoice
6{
7 public decimal Amount { get; set; }
8
9 public decimal GetTotal(decimal taxRate) => this.Amount * (1 + taxRate);
10
11 public string Describe()
12 {
13 return "Invoice";
14 }
15}
16