Skip to content

Commit 8b464fb

Browse files
authored
Merge pull request github#15249 from michaelnebel/csharp/lambdadefaultparams
C# 12: Support for lambda `param` parameter and parameter defaults.
2 parents d782bd9 + 1770bee commit 8b464fb

File tree

10 files changed

+164
-2
lines changed

10 files changed

+164
-2
lines changed

csharp/extractor/Semmle.Extraction.CSharp/Entities/OrdinaryMethod.cs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,12 @@ protected OrdinaryMethod(Context cx, IMethodSymbol init)
1818

1919
public IMethodSymbol SourceDeclaration => Symbol.OriginalDefinition;
2020

21-
public override Microsoft.CodeAnalysis.Location ReportingLocation => Symbol.GetSymbolLocation();
21+
public override Microsoft.CodeAnalysis.Location ReportingLocation =>
22+
IsCompilerGeneratedDelegate()
23+
? Symbol.ContainingType.GetSymbolLocation()
24+
: Symbol.GetSymbolLocation();
25+
26+
public override bool NeedsPopulation => base.NeedsPopulation || IsCompilerGeneratedDelegate();
2227

2328
public override void Populate(TextWriter trapFile)
2429
{
@@ -47,6 +52,13 @@ public override void Populate(TextWriter trapFile)
4752
ExtractCompilerGenerated(trapFile);
4853
}
4954

55+
private bool IsCompilerGeneratedDelegate() =>
56+
// Lambdas with parameter defaults or a `params` parameter are implemented
57+
// using compiler generated delegate types.
58+
Symbol.MethodKind == MethodKind.DelegateInvoke &&
59+
Symbol.ContainingType is INamedTypeSymbol nt &&
60+
nt.IsImplicitlyDeclared;
61+
5062
public static new OrdinaryMethod Create(Context cx, IMethodSymbol method)
5163
{
5264
if (method.MethodKind == MethodKind.ReducedExtension)
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: minorAnalysis
3+
---
4+
* C# 12: Add extractor support for lambda expressions with parameter defaults like `(int x, int y = 1) => ...` and lambda expressions with a `param` parameter like `(params int[] x) => ...)`.

csharp/ql/lib/semmle/code/csharp/exprs/Call.qll

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,12 +182,22 @@ class Call extends DotNet::Call, Expr, @call {
182182
/**
183183
* Gets the argument that corresponds to parameter `p` of a potential
184184
* run-time target of this call.
185+
*
186+
* Does not consider
187+
* - default arguments,
188+
* - named arguments.
185189
*/
186190
Expr getRuntimeArgumentForParameter(Parameter p) {
187191
exists(Callable c |
188192
c = this.getARuntimeTarget() and
189193
p = c.getAParameter() and
190-
result = this.getRuntimeArgument(p.getPosition())
194+
(
195+
p.isParams() and
196+
result = this.getRuntimeArgument(any(int i | i >= p.getPosition()))
197+
or
198+
not p.isParams() and
199+
result = this.getRuntimeArgument(p.getPosition())
200+
)
191201
)
192202
}
193203

csharp/ql/test/library-tests/arguments/PrintAst.expected

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,3 +353,71 @@ arguments.cs:
353353
# 93| 0: [Parameter] b
354354
# 93| -1: [TypeMention] bool
355355
# 93| 4: [BlockStmt] {...}
356+
lambdas.cs:
357+
# 3| [Class] LambdaArgumentsTest
358+
# 5| 5: [Method] M1
359+
# 5| -1: [TypeMention] Void
360+
# 6| 4: [BlockStmt] {...}
361+
# 7| 0: [LocalVariableDeclStmt] ... ...;
362+
# 7| 0: [LocalVariableDeclAndInitExpr] Func<Int32,Int32> l1 = ...
363+
# 7| -1: [TypeMention] Func<int, int>
364+
# 7| 0: [LocalVariableAccess] access to local variable l1
365+
# 7| 1: [LambdaExpr] (...) => ...
366+
#-----| 2: (Parameters)
367+
# 7| 0: [Parameter] x
368+
# 7| -1: [TypeMention] int
369+
# 7| 4: [AddExpr] ... + ...
370+
# 7| 0: [ParameterAccess] access to parameter x
371+
# 7| 1: [IntLiteral] 1
372+
# 8| 1: [ExprStmt] ...;
373+
# 8| 0: [DelegateCall] delegate call
374+
# 8| -1: [LocalVariableAccess] access to local variable l1
375+
# 8| 0: [IntLiteral] 1
376+
# 10| 2: [LocalVariableDeclStmt] ... ...;
377+
# 10| 0: [LocalVariableDeclAndInitExpr] <>__AnonType0<> l2 = ...
378+
# 10| -1: [TypeMention] <>__AnonType0<>
379+
# 10| 0: [LocalVariableAccess] access to local variable l2
380+
# 10| 1: [LambdaExpr] (...) => ...
381+
#-----| 2: (Parameters)
382+
# 10| 0: [Parameter] x
383+
# 10| -1: [TypeMention] int
384+
# 10| 1: [Parameter] y
385+
# 10| -1: [TypeMention] int
386+
# 10| 1: [IntLiteral] 1
387+
# 10| 4: [AddExpr] ... + ...
388+
# 10| 0: [ParameterAccess] access to parameter x
389+
# 10| 1: [ParameterAccess] access to parameter y
390+
# 11| 3: [ExprStmt] ...;
391+
# 11| 0: [DelegateCall] delegate call
392+
# 11| -1: [LocalVariableAccess] access to local variable l2
393+
# 11| 0: [IntLiteral] 2
394+
# 11| 1: [IntLiteral] 3
395+
# 12| 4: [ExprStmt] ...;
396+
# 12| 0: [DelegateCall] delegate call
397+
# 12| -1: [LocalVariableAccess] access to local variable l2
398+
# 12| 0: [IntLiteral] 4
399+
# 13| 5: [ExprStmt] ...;
400+
# 13| 0: [DelegateCall] delegate call
401+
# 13| -1: [LocalVariableAccess] access to local variable l2
402+
# 13| 0: [IntLiteral] 5
403+
# 13| 1: [IntLiteral] 6
404+
# 15| 6: [LocalVariableDeclStmt] ... ...;
405+
# 15| 0: [LocalVariableDeclAndInitExpr] <>__AnonType0<> l3 = ...
406+
# 15| -1: [TypeMention] <>__AnonType0<>
407+
# 15| 0: [LocalVariableAccess] access to local variable l3
408+
# 15| 1: [LambdaExpr] (...) => ...
409+
#-----| 2: (Parameters)
410+
# 15| 0: [Parameter] x
411+
# 15| -1: [TypeMention] Int32[]
412+
# 15| 1: [TypeMention] int
413+
# 15| 4: [PropertyCall] access to property Length
414+
# 15| -1: [ParameterAccess] access to parameter x
415+
# 16| 7: [ExprStmt] ...;
416+
# 16| 0: [DelegateCall] delegate call
417+
# 16| -1: [LocalVariableAccess] access to local variable l3
418+
# 17| 8: [ExprStmt] ...;
419+
# 17| 0: [DelegateCall] delegate call
420+
# 17| -1: [LocalVariableAccess] access to local variable l3
421+
# 17| 0: [IntLiteral] 7
422+
# 17| 1: [IntLiteral] 8
423+
# 17| 2: [IntLiteral] 9

csharp/ql/test/library-tests/arguments/argumentType.expected

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,3 +70,12 @@
7070
| arguments.cs:84:23:84:43 | array creation of type Double[] | 0 |
7171
| arguments.cs:85:20:85:20 | 0 | 0 |
7272
| arguments.cs:85:23:85:43 | array creation of type Double[] | 0 |
73+
| lambdas.cs:8:12:8:12 | 1 | 0 |
74+
| lambdas.cs:11:12:11:12 | 2 | 0 |
75+
| lambdas.cs:11:15:11:15 | 3 | 0 |
76+
| lambdas.cs:12:12:12:12 | 4 | 0 |
77+
| lambdas.cs:13:12:13:12 | 5 | 0 |
78+
| lambdas.cs:13:15:13:15 | 6 | 0 |
79+
| lambdas.cs:17:12:17:12 | 7 | 0 |
80+
| lambdas.cs:17:15:17:15 | 8 | 0 |
81+
| lambdas.cs:17:18:17:18 | 9 | 0 |
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
| lambdas.cs:8:9:8:13 | delegate call | lambdas.cs:7:23:7:23 | x | lambdas.cs:8:12:8:12 | 1 |
2+
| lambdas.cs:11:9:11:16 | delegate call | lambdas.cs:10:23:10:23 | x | lambdas.cs:11:12:11:12 | 2 |
3+
| lambdas.cs:11:9:11:16 | delegate call | lambdas.cs:10:30:10:30 | y | lambdas.cs:11:15:11:15 | 3 |
4+
| lambdas.cs:12:9:12:13 | delegate call | lambdas.cs:10:23:10:23 | x | lambdas.cs:12:12:12:12 | 4 |
5+
| lambdas.cs:13:9:13:16 | delegate call | lambdas.cs:10:23:10:23 | x | lambdas.cs:13:12:13:12 | 5 |
6+
| lambdas.cs:13:9:13:16 | delegate call | lambdas.cs:10:30:10:30 | y | lambdas.cs:13:15:13:15 | 6 |
7+
| lambdas.cs:17:9:17:19 | delegate call | lambdas.cs:15:32:15:32 | x | lambdas.cs:17:12:17:12 | 7 |
8+
| lambdas.cs:17:9:17:19 | delegate call | lambdas.cs:15:32:15:32 | x | lambdas.cs:17:15:17:15 | 8 |
9+
| lambdas.cs:17:9:17:19 | delegate call | lambdas.cs:15:32:15:32 | x | lambdas.cs:17:18:17:18 | 9 |
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import csharp
2+
3+
from Call call, Parameter p, Expr arg
4+
where
5+
call.getARuntimeTarget() instanceof LambdaExpr and
6+
arg = call.getRuntimeArgumentForParameter(p)
7+
select call, p, arg
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
using System;
2+
3+
class LambdaArgumentsTest
4+
{
5+
void M1()
6+
{
7+
var l1 = (int x) => x + 1;
8+
l1(1);
9+
10+
var l2 = (int x, int y = 1) => x + y;
11+
l2(2, 3);
12+
l2(4);
13+
l2(5, 6);
14+
15+
var l3 = (params int[] x) => x.Length;
16+
l3();
17+
l3(7, 8, 9);
18+
}
19+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
using System;
2+
using System.Runtime.InteropServices;
3+
using System.Runtime.CompilerServices;
4+
5+
public class LambdaParameters
6+
{
7+
public void M1()
8+
{
9+
var l1 = (int x, int y = 1) => x + y;
10+
var l2 = (object? o = default) => o;
11+
var l3 = (int x, int y = 1, int z = 2) => x + y + z;
12+
var l4 = ([Optional, DefaultParameterValue(7)] int x) => x;
13+
var l5 = ([Optional, DateTimeConstant(14L)] DateTime x) => x;
14+
}
15+
}

csharp/ql/test/library-tests/parameters/Parameters.expected

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
noDefaultValue
2+
| LambdaParameters.cs:9:18:9:44 | (...) => ... | LambdaParameters.cs:9:23:9:23 | x | 0 |
3+
| LambdaParameters.cs:11:18:11:59 | (...) => ... | LambdaParameters.cs:11:23:11:23 | x | 0 |
24
| Parameters.cs:7:17:7:18 | M1 | Parameters.cs:7:24:7:24 | a | 0 |
35
| Parameters.cs:7:17:7:18 | M1 | Parameters.cs:7:34:7:34 | b | 1 |
46
| Parameters.cs:7:17:7:18 | M1 | Parameters.cs:7:44:7:44 | c | 2 |
@@ -16,6 +18,12 @@ noDefaultValue
1618
| Parameters.dll:0:0:0:0 | implicit conversion | Parameters.dll:0:0:0:0 | i | 0 |
1719
| Parameters.dll:0:0:0:0 | implicit conversion | Parameters.dll:0:0:0:0 | s | 0 |
1820
withDefaultValue
21+
| LambdaParameters.cs:9:18:9:44 | (...) => ... | LambdaParameters.cs:9:30:9:30 | y | 1 | LambdaParameters.cs:9:34:9:34 | 1 | 1 |
22+
| LambdaParameters.cs:10:18:10:43 | (...) => ... | LambdaParameters.cs:10:27:10:27 | o | 0 | LambdaParameters.cs:10:31:10:37 | default | null |
23+
| LambdaParameters.cs:11:18:11:59 | (...) => ... | LambdaParameters.cs:11:30:11:30 | y | 1 | LambdaParameters.cs:11:34:11:34 | 1 | 1 |
24+
| LambdaParameters.cs:11:18:11:59 | (...) => ... | LambdaParameters.cs:11:41:11:41 | z | 2 | LambdaParameters.cs:11:45:11:45 | 2 | 2 |
25+
| LambdaParameters.cs:12:18:12:66 | (...) => ... | LambdaParameters.cs:12:60:12:60 | x | 0 | LambdaParameters.cs:12:19:12:60 | 7 | 7 |
26+
| LambdaParameters.cs:13:18:13:68 | (...) => ... | LambdaParameters.cs:13:62:13:62 | x | 0 | LambdaParameters.cs:13:19:13:62 | object creation of type DateTime | - |
1927
| Parameters.cs:8:17:8:18 | M2 | Parameters.cs:8:34:8:34 | b | 1 | Parameters.cs:8:38:8:41 | null | null |
2028
| Parameters.cs:8:17:8:18 | M2 | Parameters.cs:8:51:8:51 | c | 2 | Parameters.cs:8:55:8:70 | "default string" | default string |
2129
| Parameters.cs:9:17:9:18 | M3 | Parameters.cs:9:24:9:24 | a | 0 | Parameters.cs:9:28:9:28 | 1 | 1 |
@@ -81,6 +89,7 @@ withDefaultValue
8189
| Parameters.dll:0:0:0:0 | M23 | Parameters.dll:0:0:0:0 | arg12 | 0 | Parameters.dll:0:0:0:0 | (...) ... | 0 |
8290
| Parameters.dll:0:0:0:0 | M24 | Parameters.dll:0:0:0:0 | arg13 | 0 | Parameters.dll:0:0:0:0 | (...) ... | 7 |
8391
dateTimeDefaults
92+
| LambdaParameters.cs:13:18:13:68 | (...) => ... | LambdaParameters.cs:13:62:13:62 | x | LambdaParameters.cs:13:19:13:62 | object creation of type DateTime | DateTime(long) | 14 |
8493
| Parameters.cs:22:17:22:19 | M14 | Parameters.cs:22:64:22:67 | arg4 | Parameters.cs:22:21:22:67 | object creation of type DateTime | DateTime(long) | 14 |
8594
| Parameters.cs:23:17:23:19 | M15 | Parameters.cs:23:68:23:71 | arg5 | Parameters.cs:23:21:23:71 | object creation of type DateTime | DateTime(long) | 10001 |
8695
| Parameters.dll:0:0:0:0 | M14 | Parameters.dll:0:0:0:0 | arg4 | Parameters.dll:0:0:0:0 | object creation of type DateTime | DateTime(long) | 14 |

0 commit comments

Comments
 (0)