Open sandboxFocusImprove this doc

LAMA0003: The aspect doesn't support this kind of declaration

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
Source Code
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
Transformed Code
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

See also