| Severity | Error |
| Reported by | Metalama.Framework |
| Message | Cannot pass the expression '<Expression>' to the 'out' or 'ref' parameter '<ParameterName>' of method '<Method>' because the expression is not a variable. |
Cause
In C#, you can only pass a variable to an out or ref parameter: a local variable, a parameter, a field, or an array element. Properties, indexers, and the results of method calls aren't variables.
When you generate a call with Invoke, Metalama adds the out or ref modifier to the arguments of such parameters. It reports LAMA0019 when it knows that the expression passed to one of these parameters isn't a variable, typically because the expression is the value of a property or the result of another invocation. In the message, <Expression> is the generated expression, <ParameterName> is the name of the parameter, and <Method> is the invoked method. When Metalama can't determine whether the expression is a variable, it doesn't report the error and generates the code as is.
In the following example, the ParseInto aspect passes the member named in its argument to the out parameter of int.TryParse. The target code names the MaxAttempts property.
1using Metalama.Framework.Aspects;
2using Metalama.Framework.Code;
3using System.Linq;
4
5namespace Doc.LAMA0019.Error;
6
7// Parses the first parameter of the target method into the field or property given by name.
8public class ParseIntoAttribute : OverrideMethodAspect
9{
10 private readonly string _memberName;
11
12 public ParseIntoAttribute( string memberName )
13 {
14 this._memberName = memberName;
15 }
16
17 public override dynamic? OverrideMethod()
18 {
19 var tryParse = typeof(int).AsINamedType()
20 .Methods.OfName( "TryParse" )
21 .Single( m => m.Parameters.Count == 2 && m.Parameters[0].Type.SpecialType == SpecialType.String );
22
23 var member = meta.Target.Type.FieldsAndProperties.OfName( this._memberName ).Single();
24
25 // The second parameter of int.TryParse is an 'out' parameter.
26 tryParse.Invoke( meta.Target.Parameters[0].Value, member.Value );
27
28 return meta.Proceed();
29 }
30}
31
1namespace Doc.LAMA0019.Error;
2
3internal class RetryPolicy
4{
5 private int _maxAttempts;
6
7 public int MaxAttempts
8 {
9 get => this._maxAttempts;
10 set => this._maxAttempts = value;
11 }
12
13 // Error: a property can't be passed to the 'out' parameter of int.TryParse.
14 [ParseInto( nameof(MaxAttempts) )]
Error LAMA0019: Cannot pass the expression 'this.MaxAttempts' to the 'out' or 'ref' parameter 'result' of method 'int.TryParse(string?, out int)' because the expression is not a variable.
15 public void Configure( string text ) { }
16}
17
Resolution
Pass a variable to the out or ref parameter:
- Pass a field instead of a property, as in the following example.
- Declare a local variable in the template, pass it to the parameter, then assign its value to the property.
- If you create the expression with ExpressionFactory.Parse and you know that it's a variable, set the
isReferenceableparameter totrue.
1using Metalama.Framework.Aspects;
2using Metalama.Framework.Code;
3using System.Linq;
4
5namespace Doc.LAMA0019.Fixed;
6
7// Parses the first parameter of the target method into the field or property given by name.
8public class ParseIntoAttribute : OverrideMethodAspect
9{
10 private readonly string _memberName;
11
12 public ParseIntoAttribute( string memberName )
13 {
14 this._memberName = memberName;
15 }
16
17 public override dynamic? OverrideMethod()
18 {
19 var tryParse = typeof(int).AsINamedType()
20 .Methods.OfName( "TryParse" )
21 .Single( m => m.Parameters.Count == 2 && m.Parameters[0].Type.SpecialType == SpecialType.String );
22
23 var member = meta.Target.Type.FieldsAndProperties.OfName( this._memberName ).Single();
24
25 // The second parameter of int.TryParse is an 'out' parameter.
26 tryParse.Invoke( meta.Target.Parameters[0].Value, member.Value );
27
28 return meta.Proceed();
29 }
30}
31
1namespace Doc.LAMA0019.Fixed;
2
3internal class RetryPolicy
4{
5 private int _maxAttempts;
6
7 public int MaxAttempts
8 {
9 get => this._maxAttempts;
10 set => this._maxAttempts = value;
11 }
12
13 // Fixed: the aspect passes the field, which is a variable, to the 'out' parameter.
14 [ParseInto( nameof(_maxAttempts) )]
15 public void Configure( string text ) { }
16}
17
1namespace Doc.LAMA0019.Fixed;
2
3internal class RetryPolicy
4{
5 private int _maxAttempts;
6
7 public int MaxAttempts
8 {
9 get => this._maxAttempts;
10 set => this._maxAttempts = value;
11 }
12
13 // Fixed: the aspect passes the field, which is a variable, to the 'out' parameter.
14 [ParseInto(nameof(_maxAttempts))]
15 public void Configure(string text)
16 {
17 int.TryParse(text, out _maxAttempts);
18 }
19}
20