| Severity | Error |
| Reported by | Metalama.Framework |
| Message | Cannot reference '<ReferencedSymbol>' in '<DeclaringSymbol>' because '<ReferencedSymbol>' is run-time-only but '<DeclaringSymbol>' is <DeclaringSymbolScope>. Run-time-only declarations can be referenced only in templates and in run-time code. |
Cause
Metalama classifies every declaration as compile-time, run-time, or run-time-or-compile-time. Compile-time code, and run-time-or-compile-time code such as aspect classes, is compiled into a separate assembly that Metalama executes during the build. This assembly doesn't contain the run-time-only types of your project, such as the classes of your application, so compile-time code can't reference them.
Metalama reports LAMA0236 when code that isn't run-time-only references a run-time-only declaration, outside of a template and outside of a nameof or typeof expression. This typically happens in the following situations:
- A non-template member of an aspect, such as a field, a property, or a helper method, uses a run-time-only type.
- A member of a class marked with CompileTimeAttribute uses a run-time-only type.
In the message, <ReferencedSymbol> is the run-time-only declaration, <DeclaringSymbol> is the declaration that references it, and <DeclaringSymbolScope> is the scope of <DeclaringSymbol>. When the offending member has a run-time-only type, as a field in the example below, <DeclaringSymbol> is the containing type instead of the member.
In the following example, the Log aspect stores an instance of the run-time Logger class in a field of the aspect.
1using Metalama.Framework.Aspects;
2
3namespace Doc.LAMA0236.Error;
4
5public class LogAttribute : OverrideMethodAspect
6{
7 // Error: the aspect class cannot hold an instance of the run-time-only Logger class.
Error LAMA0236: Cannot reference 'Logger' in 'LogAttribute' because 'Logger' is run-time-only but 'LogAttribute' is run-time-or-compile-time. Run-time-only declarations can be referenced only in templates and in run-time code.
8 private readonly Logger _logger = new Logger();
9
10 public override dynamic? OverrideMethod()
11 {
12 this._logger.Write( $"Entering {meta.Target.Method}." );
13
14 return meta.Proceed();
15 }
16}
17
1using System;
2
3namespace Doc.LAMA0236.Error;
4
5public class Logger
6{
7 public void Write( string message ) => Console.WriteLine( message );
8}
9
10internal partial class OrderService
11{
12 [Log]
13 public void PlaceOrder( int orderId ) { }
14}
15
Resolution
Depending on your intent, do one of the following:
- If the run-time object belongs to the target code, introduce the member into the target type by marking it with IntroduceAttribute. Introduced members are templates, so they can use run-time types. The following example uses this fix.
- If the code only needs the run-time object while the program runs, move it into a template, for instance by creating the object in the body of
OverrideMethod. - If the code only needs to identify the type, use
typeofornameof, which are allowed, or use the code model, for instancemeta.Target.Type, at compile time. - If the referenced type must be available at compile time, mark it with RunTimeOrCompileTimeAttribute. This is only possible if all its dependencies are also available at compile time.
1using Metalama.Framework.Aspects;
2
3namespace Doc.LAMA0236.Fixed;
4
5public class LogAttribute : OverrideMethodAspect
6{
7 // Fixed: the field is introduced into the target type, where Logger is available at run time.
8 [Introduce]
9 private readonly Logger _logger = new Logger();
10
11 public override dynamic? OverrideMethod()
12 {
13 this._logger.Write( $"Entering {meta.Target.Method}." );
14
15 return meta.Proceed();
16 }
17}
18
1using System;
2
3namespace Doc.LAMA0236.Fixed;
4
5public class Logger
6{
7 public void Write( string message ) => Console.WriteLine( message );
8}
9
10internal partial class OrderService
11{
12 [Log]
13 public void PlaceOrder( int orderId ) { }
14}
15
1using System;
2
3namespace Doc.LAMA0236.Fixed;
4
5public class Logger
6{
7 public void Write(string message) => Console.WriteLine(message);
8}
9
10internal partial class OrderService
11{
12 [Log]
13 public void PlaceOrder(int orderId)
14 {
15 _logger.Write("Entering OrderService.PlaceOrder(int).");
16 }
17
18 private readonly Logger _logger = new Logger();
19}
20