| Severity | Error |
| Reported by | Metalama.Framework |
| Message | Cannot use '<ReferencedSymbol>' in '<DeclaringSymbol>' because it is only allowed inside a template.<Explanation> |
Cause
Some compile-time APIs are template-only: they only make sense while Metalama expands a template, because they generate run-time code at the place where they're used. These include most members of the meta class that return dynamic or insert statements, such as meta.This, meta.Proceed, and meta.InsertStatement, the Value property of IExpression, and any API whose signature includes dynamic, such as ExpressionFactory.Capture.
Metalama reports LAMA0233 when such an API is used in compile-time code that isn't a template, for instance in BuildAspect, in a fabric, or in a compile-time helper method. Using the API in a nameof or typeof expression is allowed. In the message, <DeclaringSymbol> is the declaration that contains the code and <ReferencedSymbol> is the template-only API. For some APIs, the message ends with a suggested replacement.
In the following example, the GetTarget helper method returns an IExpression, so it's compile-time code, but it isn't a template. It uses meta.This, which is template-only.
1using Metalama.Framework.Aspects;
2using Metalama.Framework.Code;
3using Metalama.Framework.Code.SyntaxBuilders;
4using System;
5
6namespace Doc.LAMA0233.Error;
7
8public class LogAttribute : OverrideMethodAspect
9{
10 public override dynamic? OverrideMethod()
11 {
12 Console.WriteLine( $"Entering {meta.Target.Method} on {GetTarget().Value}." );
13
14 return meta.Proceed();
15 }
16
17 // Compile-time helper that returns the type name for static methods, and the current instance otherwise.
18 private static IExpression GetTarget()
19 => meta.Target.Method.IsStatic
20 ? ExpressionFactory.Literal( meta.Target.Type.Name )
Error LAMA0233: Cannot use 'meta.This' in 'LogAttribute.GetTarget()' because it is only allowed inside a template. Use ExpressionFactory.This() instead of meta.This.
21 : meta.This; // Error: meta.This can only be used inside a template.
22}
23
1namespace Doc.LAMA0233.Error;
2
3internal class OrderService
4{
5 [Log]
6 public void PlaceOrder( int orderId ) { }
7
8 [Log]
9 public static void PurgeOrders() { }
10}
11
Resolution
Depending on your intent, do one of the following:
- Replace the template-only API with its compile-time equivalent, which returns an IExpression instead of a
dynamicvalue. For instance, use ExpressionFactory.This instead of meta.This, and pass anIExpressiondirectly instead of reading itsValueproperty. The following example uses this fix. - Move the code into the template itself, or turn the helper method into an auxiliary template by marking it with TemplateAttribute.
1using Metalama.Framework.Aspects;
2using Metalama.Framework.Code;
3using Metalama.Framework.Code.SyntaxBuilders;
4using System;
5
6namespace Doc.LAMA0233.Fixed;
7
8public class LogAttribute : OverrideMethodAspect
9{
10 public override dynamic? OverrideMethod()
11 {
12 Console.WriteLine( $"Entering {meta.Target.Method} on {GetTarget().Value}." );
13
14 return meta.Proceed();
15 }
16
17 // Compile-time helper that returns the type name for static methods, and the current instance otherwise.
18 private static IExpression GetTarget()
19 => meta.Target.Method.IsStatic
20 ? ExpressionFactory.Literal( meta.Target.Type.Name )
21 : ExpressionFactory.This(); // Fixed: ExpressionFactory.This() works in compile-time code.
22}
23
1namespace Doc.LAMA0233.Fixed;
2
3internal class OrderService
4{
5 [Log]
6 public void PlaceOrder( int orderId ) { }
7
8 [Log]
9 public static void PurgeOrders() { }
10}
11
1using System;
2
3namespace Doc.LAMA0233.Fixed;
4
5internal class OrderService
6{
7 [Log]
8 public void PlaceOrder(int orderId)
9 {
10 Console.WriteLine($"Entering OrderService.PlaceOrder(int) on {this}.");
11 }
12 [Log]
13 public static void PurgeOrders()
14 {
15 Console.WriteLine($"Entering OrderService.PurgeOrders() on {("OrderService")}.");
16 }
17}
18