| Severity | Error |
| Reported by | Metalama.Framework |
| Message | The aspect '<AspectType>' applied to <ParentTargetKind> '<ParentTarget>' cannot add an aspect of the same type to <ChildTargetKind> '<ChildTarget>' because this <ChildTargetKind> has already been processed by aspects of this type. An aspect can add an aspect of its own type only to a declaration that is processed later, such as a member or a parameter of its target. |
Cause
An aspect can add other aspects with builder.Outbound, including aspects of its own type. Metalama processes all instances of the same aspect type in a single pass, ordered by the depth of their target declaration: types before their members, and members before their parameters. When an aspect adds an aspect of its own type, the new aspect joins the same pass, so its target must come after the target of the current aspect in this order.
Metalama reports LAMA0055 when an aspect adds an aspect of its own type to a declaration that comes earlier in this order or at the same position, for instance when a method-level aspect adds itself to its declaring type, or when a parameter-level aspect adds itself to its method. In the message, <AspectType> is the aspect type, <ParentTarget> and <ParentTargetKind> are the target of the aspect and its kind, and <ChildTarget> and <ChildTargetKind> are the declaration to which the aspect tried to add an aspect and its kind. The error is reported on the aspect that adds the other aspect.
When the added aspect has a different type, the order of the two aspect types matters instead, and Metalama reports LAMA0022 if it's wrong.
In the following example, the Log aspect can be applied to a type or to a method. When it's applied to a method, it tries to add itself to the declaring type, but the type has already been processed.
1using Metalama.Framework.Aspects;
2using Metalama.Framework.Code;
3using Metalama.Framework.Eligibility;
4using System;
5
6namespace Doc.LAMA0055.Error;
7
8[AttributeUsage( AttributeTargets.Class | AttributeTargets.Method )]
Error LAMA0055: The aspect 'Log' applied to method 'Calculator.Add(int, int)' cannot add an aspect of the same type to type 'Calculator' because this type has already been processed by aspects of this type. An aspect can add an aspect of its own type only to a declaration that is processed later, such as a member or a parameter of its target.
9public class LogAttribute : OverrideMethodAspect, IAspect<INamedType>
10{
11 public override void BuildAspect( IAspectBuilder<IMethod> builder )
12 {
13 base.BuildAspect( builder );
14
15 // Error: a method-level aspect cannot add an aspect of the same type to the declaring type,
16 // because the type has already been processed.
17 builder.Outbound.Select( m => m.DeclaringType ).AddAspect( _ => this );
18 }
19
20 public void BuildAspect( IAspectBuilder<INamedType> builder )
21 {
22 // Adds the aspect to all methods of the type.
23 builder.Outbound.SelectMany( t => t.Methods ).AddAspectIfEligible( _ => this );
24 }
25
26 public void BuildEligibility( IEligibilityBuilder<INamedType> builder ) { }
27
28 public override dynamic? OverrideMethod()
29 {
30 Console.WriteLine( $"Executing {meta.Target.Method}." );
31
32 return meta.Proceed();
33 }
34}
35
1namespace Doc.LAMA0055.Error;
2
3internal class Calculator
4{
5 // The [Log] aspect tries to add itself to the Calculator type.
6 [Log]
7 public int Add( int a, int b ) => a + b;
8
9 public int Subtract( int a, int b ) => a - b;
10}
11
Resolution
Reverse the direction: apply the aspect to the outer declaration and let it add aspects of its own type to the inner declarations, which are processed later. For instance, apply the aspect to the type and add it to the methods of the type from the type-level BuildAspect method.
In the following example, the Log aspect is applied to the type, and it adds itself to each method.
1using Metalama.Framework.Aspects;
2using Metalama.Framework.Code;
3using Metalama.Framework.Eligibility;
4using System;
5
6namespace Doc.LAMA0055.Fixed;
7
8[AttributeUsage( AttributeTargets.Class | AttributeTargets.Method )]
9public class LogAttribute : OverrideMethodAspect, IAspect<INamedType>
10{
11 public void BuildAspect( IAspectBuilder<INamedType> builder )
12 {
13 // Adds the aspect to all methods of the type. The methods are processed after the type, so this is allowed.
14 builder.Outbound.SelectMany( t => t.Methods ).AddAspectIfEligible( _ => this );
15 }
16
17 public void BuildEligibility( IEligibilityBuilder<INamedType> builder ) { }
18
19 public override dynamic? OverrideMethod()
20 {
21 Console.WriteLine( $"Executing {meta.Target.Method}." );
22
23 return meta.Proceed();
24 }
25}
26
1namespace Doc.LAMA0055.Fixed;
2
3// Fixed: the [Log] aspect is applied to the type, and it adds itself to the methods.
4[Log]
5internal class Calculator
6{
7 public int Add( int a, int b ) => a + b;
8
9 public int Subtract( int a, int b ) => a - b;
10}
11
1using System;
2
3namespace Doc.LAMA0055.Fixed;
4
5// Fixed: the [Log] aspect is applied to the type, and it adds itself to the methods.
6[Log]
7internal class Calculator
8{
9 public int Add(int a, int b)
10 {
11 Console.WriteLine("Executing Calculator.Add(int, int).");
12 return a + b;
13 }
14
15 public int Subtract(int a, int b)
16 {
17 Console.WriteLine("Executing Calculator.Subtract(int, int).");
18 return a - b;
19 }
20}
21