Open sandboxFocusImprove this doc

LAMA1002: A declaration introduced by an aspect can't be an interception scope

Severity Error
Reported by Metalama.Extensions.Interceptors
Message The <Source> cannot use '<Scope>' as an interception scope because '<Scope>' is introduced by an aspect. Interceptors apply only to call sites that exist in the source code.

Cause

Metalama.Extensions.Interceptors lets an aspect or a fabric replace the calls to selected methods, in the source code of the current project, with calls to other methods. An aspect or fabric registers an interceptor with the InterceptMethods method of its adviser, builder, or amender. The declaration on which you call this method is the interception scope: only the calls located inside this declaration are intercepted.

Interceptors apply only to the calls that are written in the source code. A member or type that an aspect introduces doesn't exist in the source code, so the calls it contains can't be intercepted.

Metalama reports LAMA1002 when the scope is a declaration introduced by an aspect, for example the result of IntroduceMethod. In the message, <Source> describes the aspect or fabric, and <Scope> is the introduced declaration.

In the following example, the aspect introduces the Flush method and then registers an interceptor on it.

1using System.Linq;
2using Metalama.Extensions.Interceptors;
3using Metalama.Framework.Aspects;
4using Metalama.Framework.Code;
5
6namespace Doc.LAMA1002.Error;
7
8public class FlushLogAttribute : TypeAspect
9{
10    public override void BuildAspect( IAspectBuilder<INamedType> builder )
11    {
12        var introduced = builder.IntroduceMethod( nameof(this.Flush) );
13
14        // The scope is a method that the aspect introduces.
15        builder.With( introduced.Declaration ).InterceptMethods(
16            b => b
17                .Type( typeof(LegacyLogger) )
18                .Methods( "Write" )
19                .RedirectToExistingMethod( typeof(Logger).AsINamedType().Methods.OfName( "Write" ).Single() ) );
20    }
21
22    [Template]
23    public void Flush() => LegacyLogger.Write( "Flushed." );
24}
25
1using System;
2
3namespace Doc.LAMA1002.Error;
4
5public static class LegacyLogger
6{
7    public static void Write( string message ) => Console.WriteLine( message );
8}
9
10public static class Logger
11{
12    public static void Write( string message ) => Console.WriteLine( $"[{DateTime.Now:T}] {message}" );
13}
14
15[FlushLog]
Error LAMA1002: The interceptor method 'Logger.Write(string)' registered by the aspect [FlushLogAttribute] applied to 'OrderService' cannot use 'OrderService.Flush()' as an interception scope because 'OrderService.Flush()' is introduced by an aspect. Interceptors apply only to call sites that exist in the source code.

16internal partial class OrderService
17{
18    public void Place() => LegacyLogger.Write( "Order placed." );
19}
20

Resolution

Register the interceptor on the target of the aspect, or on another declaration that exists in the source code. The introduced method isn't intercepted, but the calls of the source code in the target are. In the following example, the aspect registers the interceptor on its target type.

1using System.Linq;
2using Metalama.Extensions.Interceptors;
3using Metalama.Framework.Aspects;
4using Metalama.Framework.Code;
5
6namespace Doc.LAMA1002.Fixed;
7
8public class FlushLogAttribute : TypeAspect
9{
10    public override void BuildAspect( IAspectBuilder<INamedType> builder )
11    {
12        builder.IntroduceMethod( nameof(this.Flush) );
13
14        // The scope is the target type, which exists in the source code.
15        builder.InterceptMethods(
16            b => b
17                .Type( typeof(LegacyLogger) )
18                .Methods( "Write" )
19                .RedirectToExistingMethod( typeof(Logger).AsINamedType().Methods.OfName( "Write" ).Single() ) );
20    }
21
22    [Template]
23    public void Flush() => LegacyLogger.Write( "Flushed." );
24}
25
Source Code
1using System;
2
3namespace Doc.LAMA1002.Fixed;
4
5public static class LegacyLogger
6{
7    public static void Write( string message ) => Console.WriteLine( message );
8}
9
10public static class Logger
11{
12    public static void Write( string message ) => Console.WriteLine( $"[{DateTime.Now:T}] {message}" );
13}
14
15[FlushLog]
16internal partial class OrderService
17{
18    public void Place() => LegacyLogger.Write( "Order placed." );
19}
20
Transformed Code
1using System;
2
3namespace Doc.LAMA1002.Fixed;
4
5public static class LegacyLogger
6{
7    public static void Write(string message) => Console.WriteLine(message);
8}
9
10public static class Logger
11{
12    public static void Write(string message) => Console.WriteLine($"[{DateTime.Now:T}] {message}");
13}
14
15[FlushLog]
16internal partial class OrderService
17{
18    public void Place() => Logger.Write("Order placed.");
19
20    public void Flush()
21    {
22        LegacyLogger.Write("Flushed.");
23    }
24}
25

See also