| Severity | Error |
| Reported by | Metalama.Framework |
| Message | The implementation of '<TargetDeclaration>' provided by '<AspectType>' cannot be inlined, but Metalama can only generate inlined code for constructors and indexers. Call meta.Proceed() at most once in the template, in a simple statement such as 'meta.Proceed();' or 'return meta.Proceed();'. |
Cause
When an aspect overrides a member, meta.Proceed stands for the previous implementation of the member: the source code or the override of a previous aspect. Metalama prefers to inline this implementation, that is, to copy its code in place of the meta.Proceed() call. When inlining isn't possible, Metalama moves the implementation to a separate, private member and calls this member instead.
Constructors and indexers can't be moved to a separate member, so their implementations must always be inlined. Metalama reports LAMA0699 when an implementation of a constructor or an indexer can't be inlined. An implementation is inlined only when the next template references it once, in a supported statement:
- For a constructor, a single
meta.Proceed();statement. - For an indexer accessor, a single
meta.Proceed()call in a statement such asreturn meta.Proceed();,var value = meta.Proceed();, ormeta.Proceed();.
In the message, <TargetDeclaration> is the overridden constructor or indexer, and <AspectType> is the aspect that provides the implementation that can't be inlined, or source code for the original implementation.
In the following example, the LogConstructorErrors aspect retries the constructor by calling meta.Proceed() a second time in a catch block.
1using Metalama.Framework.Aspects;
2using Metalama.Framework.Code;
3using System;
4
5namespace Doc.LAMA0699.Error;
6
7public class LogConstructorErrorsAttribute : TypeAspect
8{
9 public override void BuildAspect( IAspectBuilder<INamedType> builder )
10 {
11 foreach ( var constructor in builder.Target.Constructors )
12 {
13 builder.With( constructor ).Override( nameof(this.OverrideConstructor) );
14 }
15 }
16
17 [Template]
18 public void OverrideConstructor()
19 {
20 try
21 {
22 meta.Proceed();
23 }
24 catch ( Exception e )
25 {
26 Console.WriteLine( $"{meta.Target.Constructor} failed: {e.Message}" );
27
28 // Error: retrying with a second call to meta.Proceed() makes the constructor impossible to inline.
29 meta.Proceed();
30 }
31 }
32}
33
1using System;
2
3namespace Doc.LAMA0699.Error;
4
5[LogConstructorErrors]
6public class DatabaseConnection
7{
Error LAMA0699: The implementation of 'DatabaseConnection.DatabaseConnection(string)' provided by 'source code' cannot be inlined, but Metalama can only generate inlined code for constructors and indexers. Call meta.Proceed() at most once in the template, in a simple statement such as 'meta.Proceed();' or 'return meta.Proceed();'.
8 public DatabaseConnection( string connectionString )
9 {
10 Console.WriteLine( $"Connecting to '{connectionString}'." );
11 }
12}
13
Resolution
Call meta.Proceed() only once in templates that override constructors or indexers, and use it in a simple statement. If you need to execute some logic several times, move it to a method, which can be called any number of times, and override this method instead.
In the following example, the aspect calls meta.Proceed() once and rethrows the exception instead of retrying.
1using Metalama.Framework.Aspects;
2using Metalama.Framework.Code;
3using System;
4
5namespace Doc.LAMA0699.Fixed;
6
7public class LogConstructorErrorsAttribute : TypeAspect
8{
9 public override void BuildAspect( IAspectBuilder<INamedType> builder )
10 {
11 foreach ( var constructor in builder.Target.Constructors )
12 {
13 builder.With( constructor ).Override( nameof(this.OverrideConstructor) );
14 }
15 }
16
17 [Template]
18 public void OverrideConstructor()
19 {
20 try
21 {
22 meta.Proceed();
23 }
24 catch ( Exception e )
25 {
26 Console.WriteLine( $"{meta.Target.Constructor} failed: {e.Message}" );
27
28 // Fixed: meta.Proceed() is called only once, and the exception is rethrown.
29 throw;
30 }
31 }
32}
33
1using System;
2
3namespace Doc.LAMA0699.Fixed;
4
5[LogConstructorErrors]
6public class DatabaseConnection
7{
8 public DatabaseConnection( string connectionString )
9 {
10 Console.WriteLine( $"Connecting to '{connectionString}'." );
11 }
12}
13
1using System;
2
3namespace Doc.LAMA0699.Fixed;
4
5[LogConstructorErrors]
6public class DatabaseConnection
7{
8 public DatabaseConnection(string connectionString)
9 {
10 try
11 {
12 Console.WriteLine($"Connecting to '{connectionString}'.");
13 }
14 catch (Exception e)
15 {
16 Console.WriteLine($"DatabaseConnection.DatabaseConnection(string) failed: {e.Message}");
17 throw;
18 }
19 }
20}
21