| Severity | Warning |
| Reported by | Metalama.Extensions.Interceptors |
| Message | The <Site> cannot be intercepted by the <Source> because <Reason>. The call site is left unchanged. |
Cause
The Metalama.Extensions.Interceptors package lets an aspect or a fabric replace the calls to selected methods with calls to another method, called an interceptor. The aspect or fabric registers the interception with InterceptMethods, and Metalama rewrites each call site that matches.
Some call sites can't be rewritten in this version of Metalama. When the registration doesn't skip such a site, Metalama reports LAMA1012 as a warning and leaves the call unchanged, so the original method runs. In the message, <Site> describes the call, <Source> is the interceptor and the aspect or fabric that registered it, and <Reason> explains the limitation. The most common reasons are:
- the call is in a lambda that the compiler converts to an expression tree, which describes the call instead of making it;
- the intercepted method returns by reference;
- a pointer type occurs in the signature of the method, in a type argument, or in the type of the receiver;
- the method uses
__arglist, or its signature needs ascopedparameter; - an omitted optional argument has a default value that can't be written in C#;
- an interceptor generated from a template can't handle the call, for instance a base call to a virtual method or a call whose receiver has a type parameter as its type.
To avoid the warning without changing the code, the registration can return Skip for the sites that it doesn't support. The ForEachSite method gives access to the reason through the NonInterceptableReason property of the site.
In the following example, a fabric intercepts the calls to PriceCalculator.ApplyDiscount. The call in the expression tree can't be intercepted.
1using Metalama.Extensions.Interceptors;
2using Metalama.Framework.Code;
3using Metalama.Framework.Fabrics;
4using System.Linq;
5
6namespace Doc.LAMA1012.Error;
7
8internal class Fabric : ProjectFabric
9{
10 public override void AmendProject( IProjectAmender amender )
11 {
12 amender.SelectTypes()
13 .Where( t => t.Name == "Checkout" )
14 .InterceptMethods(
15 b => b
16 .Type( typeof(PriceCalculator) )
17 .Methods( "ApplyDiscount" )
18 .RedirectToExistingMethod(
19 typeof(DiscountedPriceCalculator).AsINamedType().Methods.OfName( "ApplyDiscount" ).Single() ) );
20 }
21}
22
1using System;
2using System.Linq.Expressions;
3
4namespace Doc.LAMA1012.Error;
5
6internal static class PriceCalculator
7{
8 public static decimal ApplyDiscount( decimal price ) => price * 0.9m;
9}
10
11internal static class DiscountedPriceCalculator
12{
13 public static decimal ApplyDiscount( decimal price ) => price * 0.8m;
14}
15
16internal static class Checkout
17{
18 public static void Run()
19 {
20 // The call is in an expression tree, so Metalama cannot intercept it.
Warning LAMA1012: The call to 'PriceCalculator.ApplyDiscount(decimal)' cannot be intercepted by the interceptor method 'DiscountedPriceCalculator.ApplyDiscount(decimal)' registered by the fabric Doc.LAMA1012.Error.Fabric because it is in a lambda that is converted to an expression tree, which describes the call instead of making it. The call site is left unchanged.
21 Expression<Func<decimal>> expression = () => PriceCalculator.ApplyDiscount( 100m );
22
23 Console.WriteLine( expression.Compile()() );
24 }
25}
26
Resolution
Depending on the reason given in the message, do one of the following:
- Change the code at the call site so that it doesn't hit the limitation. In the following example, the expression tree is replaced by a delegate.
- Select another method, or restrict the scope of the interception so that it doesn't include the call.
- Call
Skipfrom aForEachSitedelegate for the sites with a limitation, and handle them in another way. - Suppress the warning if you accept that the call is left unchanged.
1using Metalama.Extensions.Interceptors;
2using Metalama.Framework.Code;
3using Metalama.Framework.Fabrics;
4using System.Linq;
5
6namespace Doc.LAMA1012.Fixed;
7
8internal class Fabric : ProjectFabric
9{
10 public override void AmendProject( IProjectAmender amender )
11 {
12 amender.SelectTypes()
13 .Where( t => t.Name == "Checkout" )
14 .InterceptMethods(
15 b => b
16 .Type( typeof(PriceCalculator) )
17 .Methods( "ApplyDiscount" )
18 .RedirectToExistingMethod(
19 typeof(DiscountedPriceCalculator).AsINamedType().Methods.OfName( "ApplyDiscount" ).Single() ) );
20 }
21}
22
1using System;
2
3namespace Doc.LAMA1012.Fixed;
4
5internal static class PriceCalculator
6{
7 public static decimal ApplyDiscount( decimal price ) => price * 0.9m;
8}
9
10internal static class DiscountedPriceCalculator
11{
12 public static decimal ApplyDiscount( decimal price ) => price * 0.8m;
13}
14
15internal static class Checkout
16{
17 public static void Run()
18 {
19 // The call is in a delegate, so Metalama can intercept it.
20 Func<decimal> function = () => PriceCalculator.ApplyDiscount( 100m );
21
22 Console.WriteLine( function() );
23 }
24}
25
1using System;
2
3namespace Doc.LAMA1012.Fixed;
4
5internal static class PriceCalculator
6{
7 public static decimal ApplyDiscount(decimal price) => price * 0.9m;
8}
9
10internal static class DiscountedPriceCalculator
11{
12 public static decimal ApplyDiscount(decimal price) => price * 0.8m;
13}
14
15internal static class Checkout
16{
17 public static void Run()
18 {
19 // The call is in a delegate, so Metalama can intercept it.
20 Func<decimal> function = () => DiscountedPriceCalculator.ApplyDiscount(100m);
21
22 Console.WriteLine(function());
23 }
24}
25