Skip to content

Commit 139dc0a

Browse files
authored
Merge pull request #20922 from aschackmull/csharp/object-initializer
C#: Replace initializer splitting with an ObjectInitMethod.
2 parents 5c6d83e + 28e9420 commit 139dc0a

File tree

96 files changed

+2582
-1641
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

96 files changed

+2582
-1641
lines changed

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

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ protected override void ExtractInitializers(TextWriter trapFile)
7474
{
7575
case SyntaxKind.BaseConstructorInitializer:
7676
initializerType = Symbol.ContainingType.BaseType!;
77+
ExtractObjectInitCall(trapFile);
7778
break;
7879
case SyntaxKind.ThisConstructorInitializer:
7980
initializerType = Symbol.ContainingType;
@@ -90,10 +91,12 @@ protected override void ExtractInitializers(TextWriter trapFile)
9091
var primaryInfo = Context.GetSymbolInfo(primaryInitializer);
9192
var primarySymbol = primaryInfo.Symbol;
9293

94+
ExtractObjectInitCall(trapFile);
9395
ExtractSourceInitializer(trapFile, primarySymbol?.ContainingType, (IMethodSymbol?)primarySymbol, primaryInitializer.ArgumentList, primaryInitializer.GetLocation());
9496
}
9597
else if (Symbol.MethodKind is MethodKind.Constructor)
9698
{
99+
ExtractObjectInitCall(trapFile);
97100
var baseType = Symbol.ContainingType.BaseType;
98101
if (baseType is null)
99102
{
@@ -127,6 +130,27 @@ protected override void ExtractInitializers(TextWriter trapFile)
127130
}
128131
}
129132

133+
private void ExtractObjectInitCall(TextWriter trapFile)
134+
{
135+
var target = ObjectInitMethod.Create(Context, ContainingType!);
136+
137+
var type = Context.Compilation.GetSpecialType(SpecialType.System_Void);
138+
139+
var info = new ExpressionInfo(Context,
140+
AnnotatedTypeSymbol.CreateNotAnnotated(type),
141+
Location,
142+
Kinds.ExprKind.METHOD_INVOCATION,
143+
this,
144+
-2,
145+
isCompilerGenerated: true,
146+
null);
147+
var obinitCall = new Expression(info);
148+
149+
trapFile.expr_call(obinitCall, target);
150+
151+
Expressions.This.CreateImplicit(Context, Symbol.ContainingType, Location, obinitCall, -1);
152+
}
153+
130154
private void ExtractSourceInitializer(TextWriter trapFile, ITypeSymbol? type, IMethodSymbol? symbol, ArgumentListSyntax arguments, Microsoft.CodeAnalysis.Location location)
131155
{
132156
var initInfo = new ExpressionInfo(Context,
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
namespace Semmle.Extraction.CSharp.Entities
2+
{
3+
/// <summary>
4+
/// Marker interface for method entities.
5+
/// </summary>
6+
public interface IMethodEntity : IEntity
7+
{
8+
}
9+
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
namespace Semmle.Extraction.CSharp.Entities
1111
{
12-
internal abstract class Method : CachedSymbol<IMethodSymbol>, IExpressionParentEntity, IStatementParentEntity
12+
internal abstract class Method : CachedSymbol<IMethodSymbol>, IExpressionParentEntity, IStatementParentEntity, IMethodEntity
1313
{
1414
protected Method(Context cx, IMethodSymbol init)
1515
: base(cx, init) { }
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
using System.IO;
2+
using Microsoft.CodeAnalysis;
3+
4+
namespace Semmle.Extraction.CSharp.Entities
5+
{
6+
internal sealed class ObjectInitMethod : CachedEntity, IMethodEntity
7+
{
8+
private Type ContainingType { get; }
9+
10+
private ObjectInitMethod(Context cx, Type containingType)
11+
: base(cx)
12+
{
13+
this.ContainingType = containingType;
14+
}
15+
16+
private static readonly string Name = "<object initializer>";
17+
18+
public static ObjectInitMethod Create(Context cx, Type containingType)
19+
{
20+
return ObjectInitMethodFactory.Instance.CreateEntity(cx, (typeof(ObjectInitMethod), containingType), containingType);
21+
}
22+
23+
public override void Populate(TextWriter trapFile)
24+
{
25+
var returnType = Type.Create(Context, Context.Compilation.GetSpecialType(SpecialType.System_Void));
26+
27+
trapFile.methods(this, Name, ContainingType, returnType.TypeRef, this);
28+
29+
trapFile.compiler_generated(this);
30+
31+
trapFile.method_location(this, Context.CreateLocation(ReportingLocation));
32+
}
33+
34+
public override void WriteId(EscapingTextWriter trapFile)
35+
{
36+
trapFile.WriteSubId(ContainingType);
37+
trapFile.Write(".");
38+
trapFile.Write(Name);
39+
trapFile.Write(";method");
40+
}
41+
42+
public override Microsoft.CodeAnalysis.Location? ReportingLocation => ContainingType.ReportingLocation;
43+
44+
public override bool NeedsPopulation => true;
45+
46+
public override TrapStackBehaviour TrapStackBehaviour => TrapStackBehaviour.NoLabel;
47+
48+
private class ObjectInitMethodFactory : CachedEntityFactory<Type, ObjectInitMethod>
49+
{
50+
public static ObjectInitMethodFactory Instance { get; } = new ObjectInitMethodFactory();
51+
52+
public override ObjectInitMethod Create(Context cx, Type containingType) =>
53+
new ObjectInitMethod(cx, containingType);
54+
}
55+
}
56+
}

csharp/extractor/Semmle.Extraction.CSharp/Trap/Tuples.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ internal static void expr_argument(this TextWriter trapFile, Expression expr, in
175175
internal static void expr_argument_name(this TextWriter trapFile, Expression expr, string name) =>
176176
trapFile.WriteTuple("expr_argument_name", expr, name);
177177

178-
internal static void expr_call(this TextWriter trapFile, Expression expr, Method target) =>
178+
internal static void expr_call(this TextWriter trapFile, Expression expr, IMethodEntity target) =>
179179
trapFile.WriteTuple("expr_call", expr, target);
180180

181181
internal static void expr_flowstate(this TextWriter trapFile, Expression expr, int flowState) =>
@@ -247,10 +247,10 @@ internal static void localvar_location(this TextWriter trapFile, LocalVariable v
247247
internal static void localvars(this TextWriter trapFile, LocalVariable key, VariableKind kind, string name, int @var, Type type, Expression expr) =>
248248
trapFile.WriteTuple("localvars", key, (int)kind, name, @var, type, expr);
249249

250-
internal static void method_location(this TextWriter trapFile, Method method, Location location) =>
250+
internal static void method_location(this TextWriter trapFile, IMethodEntity method, Location location) =>
251251
trapFile.WriteTuple("method_location", method, location);
252252

253-
internal static void methods(this TextWriter trapFile, Method method, string name, Type declType, Type retType, Method originalDefinition) =>
253+
internal static void methods(this TextWriter trapFile, IMethodEntity method, string name, Type declType, Type retType, IMethodEntity originalDefinition) =>
254254
trapFile.WriteTuple("methods", method, name, declType, retType, originalDefinition);
255255

256256
internal static void modifiers(this TextWriter trapFile, Label entity, string modifier) =>
Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
diagnosticAttributes
2-
| Scanning C# code completed successfully, but the scan encountered issues. This may be caused by problems identifying dependencies or use of generated source code. Some metrics of the database quality are: Percentage of calls with call target: 25 % (threshold 85 %). Percentage of expressions with known type: 58 % (threshold 85 %). Ideally these metrics should be above their thresholds. Addressing these issues is advisable to avoid false-positives or missing results. If they cannot be addressed, consider scanning C# using either the `autobuild` or `manual` [build modes](https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages#comparison-of-the-build-modes). | visibilityCliSummaryTable | true |
3-
| Scanning C# code completed successfully, but the scan encountered issues. This may be caused by problems identifying dependencies or use of generated source code. Some metrics of the database quality are: Percentage of calls with call target: 25 % (threshold 85 %). Percentage of expressions with known type: 58 % (threshold 85 %). Ideally these metrics should be above their thresholds. Addressing these issues is advisable to avoid false-positives or missing results. If they cannot be addressed, consider scanning C# using either the `autobuild` or `manual` [build modes](https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages#comparison-of-the-build-modes). | visibilityStatusPage | true |
4-
| Scanning C# code completed successfully, but the scan encountered issues. This may be caused by problems identifying dependencies or use of generated source code. Some metrics of the database quality are: Percentage of calls with call target: 25 % (threshold 85 %). Percentage of expressions with known type: 58 % (threshold 85 %). Ideally these metrics should be above their thresholds. Addressing these issues is advisable to avoid false-positives or missing results. If they cannot be addressed, consider scanning C# using either the `autobuild` or `manual` [build modes](https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages#comparison-of-the-build-modes). | visibilityTelemetry | true |
2+
| Scanning C# code completed successfully, but the scan encountered issues. This may be caused by problems identifying dependencies or use of generated source code. Some metrics of the database quality are: Percentage of calls with call target: 40 % (threshold 85 %). Percentage of expressions with known type: 64 % (threshold 85 %). Ideally these metrics should be above their thresholds. Addressing these issues is advisable to avoid false-positives or missing results. If they cannot be addressed, consider scanning C# using either the `autobuild` or `manual` [build modes](https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages#comparison-of-the-build-modes). | visibilityCliSummaryTable | true |
3+
| Scanning C# code completed successfully, but the scan encountered issues. This may be caused by problems identifying dependencies or use of generated source code. Some metrics of the database quality are: Percentage of calls with call target: 40 % (threshold 85 %). Percentage of expressions with known type: 64 % (threshold 85 %). Ideally these metrics should be above their thresholds. Addressing these issues is advisable to avoid false-positives or missing results. If they cannot be addressed, consider scanning C# using either the `autobuild` or `manual` [build modes](https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages#comparison-of-the-build-modes). | visibilityStatusPage | true |
4+
| Scanning C# code completed successfully, but the scan encountered issues. This may be caused by problems identifying dependencies or use of generated source code. Some metrics of the database quality are: Percentage of calls with call target: 40 % (threshold 85 %). Percentage of expressions with known type: 64 % (threshold 85 %). Ideally these metrics should be above their thresholds. Addressing these issues is advisable to avoid false-positives or missing results. If they cannot be addressed, consider scanning C# using either the `autobuild` or `manual` [build modes](https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages#comparison-of-the-build-modes). | visibilityTelemetry | true |
55
#select
6-
| Scanning C# code completed successfully, but the scan encountered issues. This may be caused by problems identifying dependencies or use of generated source code. Some metrics of the database quality are: Percentage of calls with call target: 25 % (threshold 85 %). Percentage of expressions with known type: 58 % (threshold 85 %). Ideally these metrics should be above their thresholds. Addressing these issues is advisable to avoid false-positives or missing results. If they cannot be addressed, consider scanning C# using either the `autobuild` or `manual` [build modes](https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages#comparison-of-the-build-modes). | Scanning C# code completed successfully, but the scan encountered issues. This may be caused by problems identifying dependencies or use of generated source code. Some metrics of the database quality are: Percentage of calls with call target: 25 % (threshold 85 %). Percentage of expressions with known type: 58 % (threshold 85 %). Ideally these metrics should be above their thresholds. Addressing these issues is advisable to avoid false-positives or missing results. If they cannot be addressed, consider scanning C# using either the `autobuild` or `manual` [build modes](https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages#comparison-of-the-build-modes). | 1 |
6+
| Scanning C# code completed successfully, but the scan encountered issues. This may be caused by problems identifying dependencies or use of generated source code. Some metrics of the database quality are: Percentage of calls with call target: 40 % (threshold 85 %). Percentage of expressions with known type: 64 % (threshold 85 %). Ideally these metrics should be above their thresholds. Addressing these issues is advisable to avoid false-positives or missing results. If they cannot be addressed, consider scanning C# using either the `autobuild` or `manual` [build modes](https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages#comparison-of-the-build-modes). | Scanning C# code completed successfully, but the scan encountered issues. This may be caused by problems identifying dependencies or use of generated source code. Some metrics of the database quality are: Percentage of calls with call target: 40 % (threshold 85 %). Percentage of expressions with known type: 64 % (threshold 85 %). Ideally these metrics should be above their thresholds. Addressing these issues is advisable to avoid false-positives or missing results. If they cannot be addressed, consider scanning C# using either the `autobuild` or `manual` [build modes](https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages#comparison-of-the-build-modes). | 1 |

csharp/ql/integration-tests/all-platforms/standalone_resx/Members.expected

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
| Program | Program.<Main>$ |
2+
| Program | Program.<object initializer> |
23
| Program | Program.Program |
34
| Resx.Test1.Test2.test | Resx.Test1.Test2.test.Culture |
45
| Resx.Test1.Test2.test | Resx.Test1.Test2.test.GetResourceString |

csharp/ql/lib/semmle/code/csharp/Callable.qll

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,6 @@ class Method extends Callable, Virtualizable, Attributable, @method {
281281
/** Holds if this method has a `params` parameter. */
282282
predicate hasParams() { exists(this.getParamsType()) }
283283

284-
// Remove when `Callable.isOverridden()` is removed
285284
override predicate fromSource() {
286285
Callable.super.fromSource() and
287286
not this.isCompilerGenerated()
@@ -317,6 +316,19 @@ class ExtensionMethod extends Method {
317316
override string getAPrimaryQlClass() { result = "ExtensionMethod" }
318317
}
319318

319+
/**
320+
* An object initializer method.
321+
*
322+
* This is an extractor-synthesized method that executes the field
323+
* initializers. Note that the AST nodes for the field initializers are nested
324+
* directly under the class, and therefore this method has no body in the AST.
325+
* On the other hand, this provides the unique enclosing callable for the field
326+
* initializers and their control flow graph.
327+
*/
328+
class ObjectInitMethod extends Method {
329+
ObjectInitMethod() { this.getName() = "<object initializer>" }
330+
}
331+
320332
/**
321333
* A constructor, for example `public C() { }` on line 2 in
322334
*
@@ -350,6 +362,9 @@ class Constructor extends Callable, Member, Attributable, @constructor {
350362
*/
351363
ConstructorInitializer getInitializer() { result = this.getChildExpr(-1) }
352364

365+
/** Gets the object initializer call of this constructor, if any. */
366+
MethodCall getObjectInitializerCall() { result = this.getChildExpr(-2) }
367+
353368
/** Holds if this constructor has an initializer. */
354369
predicate hasInitializer() { exists(this.getInitializer()) }
355370

csharp/ql/lib/semmle/code/csharp/ExprOrStmtParent.qll

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,8 @@ class TopLevelExprParent extends Element, @top_level_expr_parent {
5555
/** INTERNAL: Do not use. */
5656
Expr getExpressionBody(Callable c) {
5757
result = c.getAChildExpr() and
58-
not result = c.(Constructor).getInitializer()
58+
not result = c.(Constructor).getInitializer() and
59+
not result = c.(Constructor).getObjectInitializerCall()
5960
}
6061

6162
/** INTERNAL: Do not use. */
@@ -211,6 +212,8 @@ private module Cached {
211212
enclosingBody(cfe, getBody(c))
212213
or
213214
parent*(enclosingStart(cfe), c.(Constructor).getInitializer())
215+
or
216+
parent*(cfe, c.(Constructor).getObjectInitializerCall())
214217
}
215218

216219
/** Holds if the enclosing statement of expression `e` is `s`. */

0 commit comments

Comments
 (0)