Skip to content

Commit cb9a9db

Browse files
committed
C# Improve default argument value extraction
1 parent 2ac1e60 commit cb9a9db

File tree

4 files changed

+94
-40
lines changed

4 files changed

+94
-40
lines changed

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

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,39 @@ private static bool ContainsPattern(SyntaxNode node) =>
164164
}
165165
}
166166

167+
/// <summary>
168+
/// Creates a generated expression for a default argument value.
169+
/// </summary>
170+
public static Expression? CreateGenerated(Context cx, IParameterSymbol parameter, IExpressionParentEntity parent,
171+
int childIndex, Extraction.Entities.Location location)
172+
{
173+
if (!parameter.HasExplicitDefaultValue)
174+
{
175+
return null;
176+
}
177+
178+
var defaultValue = parameter.ExplicitDefaultValue;
179+
180+
if (parameter.Type is INamedTypeSymbol nt && nt.EnumUnderlyingType is not null)
181+
{
182+
// = (MyEnum)1, = MyEnum.Value1, = default(MyEnum), = new MyEnum()
183+
// we're generating a (MyEnum)value cast expression:
184+
defaultValue ??= 0;
185+
Action<Expression, int> createChild = (parent, index) => Literal.CreateGenerated(cx, parent, index, nt.EnumUnderlyingType, defaultValue, location);
186+
return Cast.CreateGenerated(cx, parent, childIndex, parameter.Type, defaultValue, createChild, location);
187+
}
188+
189+
if (defaultValue is null)
190+
{
191+
// = null, = default, = default(T), = new MyStruct()
192+
// we're generating a default expression:
193+
return Default.CreateGenerated(cx, parent, childIndex, location);
194+
}
195+
196+
// const literal:
197+
return Literal.CreateGenerated(cx, parent, childIndex, parameter.Type, defaultValue, location);
198+
}
199+
167200
/// <summary>
168201
/// Adapt the operator kind depending on whether it's a dynamic call or a user-operator call.
169202
/// </summary>

csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Default.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,20 @@ protected override void PopulateExpression(TextWriter trapFile)
1414
{
1515
TypeAccess.Create(Context, Syntax.Type, this, 0);
1616
}
17+
18+
public static Expression CreateGenerated(Context cx, IExpressionParentEntity parent, int childIndex, Extraction.Entities.Location location)
19+
{
20+
var info = new ExpressionInfo(
21+
cx,
22+
null,
23+
location,
24+
ExprKind.DEFAULT,
25+
parent,
26+
childIndex,
27+
true,
28+
ValueAsString(null));
29+
30+
return new Expression(info);
31+
}
1732
}
1833
}

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

Lines changed: 31 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using Microsoft.CodeAnalysis.CSharp.Syntax;
55
using Semmle.Extraction.Entities;
66
using System.IO;
7+
using System;
78

89
namespace Semmle.Extraction.CSharp.Entities
910
{
@@ -124,6 +125,17 @@ Symbol.ContainingSymbol is IMethodSymbol ms &&
124125
trapFile.param_location(this, Context.CreateLocation());
125126
}
126127

128+
if (Symbol.HasExplicitDefaultValue && Context.Defines(Symbol))
129+
{
130+
var defaultValueSyntax = GetDefaultValueFromSyntax(Symbol);
131+
132+
Action defaultValueExpressionCreation = defaultValueSyntax is not null
133+
? () => Expression.Create(Context, defaultValueSyntax.Value, this, 0)
134+
: () => Expression.CreateGenerated(Context, Symbol, this, 0, Location);
135+
136+
Context.PopulateLater(defaultValueExpressionCreation);
137+
}
138+
127139
if (!IsSourceDeclaration || !Symbol.FromSource())
128140
return;
129141

@@ -139,36 +151,30 @@ Symbol.ContainingSymbol is IMethodSymbol ms &&
139151
TypeMention.Create(Context, syntax.Type!, this, type);
140152
}
141153
}
154+
}
142155

143-
if (Symbol.HasExplicitDefaultValue && Context.Defines(Symbol))
156+
private static EqualsValueClauseSyntax? GetDefaultValueFromSyntax(IParameterSymbol symbol)
157+
{
158+
// This is a slight bug in the dbscheme
159+
// We should really define param_default(param, string)
160+
// And use parameter child #0 to encode the default expression.
161+
var defaultValue = GetParameterDefaultValue(symbol);
162+
if (defaultValue is null)
144163
{
145-
// This is a slight bug in the dbscheme
146-
// We should really define param_default(param, string)
147-
// And use parameter child #0 to encode the default expression.
148-
var defaultValue = GetParameterDefaultValue(Symbol);
149-
if (defaultValue is null)
150-
{
151-
// In case this parameter belongs to an accessor of an indexer, we need
152-
// to get the default value from the corresponding parameter belonging
153-
// to the indexer itself
154-
var method = (IMethodSymbol)Symbol.ContainingSymbol;
155-
if (method is not null)
156-
{
157-
var i = method.Parameters.IndexOf(Symbol);
158-
var indexer = (IPropertySymbol?)method.AssociatedSymbol;
159-
if (indexer is not null)
160-
defaultValue = GetParameterDefaultValue(indexer.Parameters[i]);
161-
}
162-
}
163-
164-
if (defaultValue is not null)
164+
// In case this parameter belongs to an accessor of an indexer, we need
165+
// to get the default value from the corresponding parameter belonging
166+
// to the indexer itself
167+
var method = (IMethodSymbol)symbol.ContainingSymbol;
168+
if (method is not null)
165169
{
166-
Context.PopulateLater(() =>
167-
{
168-
Expression.Create(Context, defaultValue.Value, this, 0);
169-
});
170+
var i = method.Parameters.IndexOf(symbol);
171+
var indexer = (IPropertySymbol?)method.AssociatedSymbol;
172+
if (indexer is not null)
173+
defaultValue = GetParameterDefaultValue(indexer.Parameters[i]);
170174
}
171175
}
176+
177+
return defaultValue;
172178
}
173179

174180
public override bool IsSourceDeclaration => Symbol.IsSourceDeclaration();

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

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,23 +9,8 @@ noDefaultValue
99
| Parameters.dll:0:0:0:0 | M1 | Parameters.dll:0:0:0:0 | b | 1 |
1010
| Parameters.dll:0:0:0:0 | M1 | Parameters.dll:0:0:0:0 | c | 2 |
1111
| Parameters.dll:0:0:0:0 | M2 | Parameters.dll:0:0:0:0 | a | 0 |
12-
| Parameters.dll:0:0:0:0 | M2 | Parameters.dll:0:0:0:0 | b | 1 |
13-
| Parameters.dll:0:0:0:0 | M2 | Parameters.dll:0:0:0:0 | c | 2 |
14-
| Parameters.dll:0:0:0:0 | M3 | Parameters.dll:0:0:0:0 | a | 0 |
15-
| Parameters.dll:0:0:0:0 | M3 | Parameters.dll:0:0:0:0 | b | 1 |
16-
| Parameters.dll:0:0:0:0 | M3 | Parameters.dll:0:0:0:0 | c | 2 |
17-
| Parameters.dll:0:0:0:0 | M4 | Parameters.dll:0:0:0:0 | a | 0 |
18-
| Parameters.dll:0:0:0:0 | M4 | Parameters.dll:0:0:0:0 | b | 1 |
19-
| Parameters.dll:0:0:0:0 | M5 | Parameters.dll:0:0:0:0 | a | 0 |
20-
| Parameters.dll:0:0:0:0 | M5 | Parameters.dll:0:0:0:0 | b | 1 |
2112
| Parameters.dll:0:0:0:0 | M6 | Parameters.dll:0:0:0:0 | s1 | 0 |
22-
| Parameters.dll:0:0:0:0 | M6 | Parameters.dll:0:0:0:0 | s2 | 1 |
23-
| Parameters.dll:0:0:0:0 | M6 | Parameters.dll:0:0:0:0 | s3 | 2 |
2413
| Parameters.dll:0:0:0:0 | M7 | Parameters.dll:0:0:0:0 | e1 | 0 |
25-
| Parameters.dll:0:0:0:0 | M7 | Parameters.dll:0:0:0:0 | e2 | 1 |
26-
| Parameters.dll:0:0:0:0 | M7 | Parameters.dll:0:0:0:0 | e3 | 2 |
27-
| Parameters.dll:0:0:0:0 | M7 | Parameters.dll:0:0:0:0 | e4 | 3 |
28-
| Parameters.dll:0:0:0:0 | M7 | Parameters.dll:0:0:0:0 | e5 | 4 |
2914
withDefaultValue
3015
| Parameters.cs:4:17:4:18 | M2 | Parameters.cs:4:34:4:34 | b | 1 | Parameters.cs:4:38:4:41 | null | null |
3116
| Parameters.cs:4:17:4:18 | M2 | Parameters.cs:4:51:4:51 | c | 2 | Parameters.cs:4:55:4:70 | "default string" | default string |
@@ -42,3 +27,18 @@ withDefaultValue
4227
| Parameters.cs:9:17:9:18 | M7 | Parameters.cs:9:67:9:68 | e3 | 2 | Parameters.cs:9:72:9:83 | object creation of type MyEnum | 0 |
4328
| Parameters.cs:9:17:9:18 | M7 | Parameters.cs:9:93:9:94 | e4 | 3 | Parameters.cs:9:98:9:105 | access to constant A | 1 |
4429
| Parameters.cs:9:17:9:18 | M7 | Parameters.cs:9:115:9:116 | e5 | 4 | Parameters.cs:9:120:9:128 | (...) ... | 5 |
30+
| Parameters.dll:0:0:0:0 | M2 | Parameters.dll:0:0:0:0 | b | 1 | Parameters.dll:0:0:0:0 | default | null |
31+
| Parameters.dll:0:0:0:0 | M2 | Parameters.dll:0:0:0:0 | c | 2 | Parameters.dll:0:0:0:0 | "default string" | default string |
32+
| Parameters.dll:0:0:0:0 | M3 | Parameters.dll:0:0:0:0 | a | 0 | Parameters.dll:0:0:0:0 | 1 | 1 |
33+
| Parameters.dll:0:0:0:0 | M3 | Parameters.dll:0:0:0:0 | b | 1 | Parameters.dll:0:0:0:0 | default | null |
34+
| Parameters.dll:0:0:0:0 | M3 | Parameters.dll:0:0:0:0 | c | 2 | Parameters.dll:0:0:0:0 | "null" | null |
35+
| Parameters.dll:0:0:0:0 | M4 | Parameters.dll:0:0:0:0 | a | 0 | Parameters.dll:0:0:0:0 | 0 | 0 |
36+
| Parameters.dll:0:0:0:0 | M4 | Parameters.dll:0:0:0:0 | b | 1 | Parameters.dll:0:0:0:0 | default | null |
37+
| Parameters.dll:0:0:0:0 | M5 | Parameters.dll:0:0:0:0 | a | 0 | Parameters.dll:0:0:0:0 | 0 | 0 |
38+
| Parameters.dll:0:0:0:0 | M5 | Parameters.dll:0:0:0:0 | b | 1 | Parameters.dll:0:0:0:0 | default | null |
39+
| Parameters.dll:0:0:0:0 | M6 | Parameters.dll:0:0:0:0 | s2 | 1 | Parameters.dll:0:0:0:0 | default | null |
40+
| Parameters.dll:0:0:0:0 | M6 | Parameters.dll:0:0:0:0 | s3 | 2 | Parameters.dll:0:0:0:0 | default | null |
41+
| Parameters.dll:0:0:0:0 | M7 | Parameters.dll:0:0:0:0 | e2 | 1 | Parameters.dll:0:0:0:0 | (...) ... | 0 |
42+
| Parameters.dll:0:0:0:0 | M7 | Parameters.dll:0:0:0:0 | e3 | 2 | Parameters.dll:0:0:0:0 | (...) ... | 0 |
43+
| Parameters.dll:0:0:0:0 | M7 | Parameters.dll:0:0:0:0 | e4 | 3 | Parameters.dll:0:0:0:0 | (...) ... | 1 |
44+
| Parameters.dll:0:0:0:0 | M7 | Parameters.dll:0:0:0:0 | e5 | 4 | Parameters.dll:0:0:0:0 | (...) ... | 5 |

0 commit comments

Comments
 (0)