Skip to content

Commit 6a4b54e

Browse files
committed
C#: Extract global statements
1 parent 8c4563b commit 6a4b54e

File tree

17 files changed

+230
-29
lines changed

17 files changed

+230
-29
lines changed

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,11 @@ public override void Populate(TextWriter trapFile)
5151
Overrides(trapFile);
5252
ExtractRefReturn(trapFile, symbol, this);
5353
ExtractCompilerGenerated(trapFile);
54+
55+
if (SymbolEqualityComparer.Default.Equals(symbol, Context.Compilation.GetEntryPoint(System.Threading.CancellationToken.None)))
56+
{
57+
trapFile.entry_methods(this);
58+
}
5459
}
5560

5661
public static new OrdinaryMethod Create(Context cx, IMethodSymbol method) => OrdinaryMethodFactory.Instance.CreateEntityFromSymbol(cx, method);
Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,41 @@
11
using Microsoft.CodeAnalysis.CSharp.Syntax;
22
using Microsoft.CodeAnalysis.CSharp;
3+
using System.IO;
34

45
namespace Semmle.Extraction.CSharp.Entities
56
{
67
internal abstract class Statement : FreshEntity, IExpressionParentEntity, IStatementParentEntity
78
{
8-
protected Statement(Context cx) : base(cx) { }
9-
10-
public static Statement Create(Context cx, StatementSyntax node, Statement parent, int child) =>
9+
private readonly int child;
10+
private readonly Kinds.StmtKind kind;
11+
private readonly IStatementParentEntity parent;
12+
13+
protected Statement(Context cx, Kinds.StmtKind kind, IStatementParentEntity parent, int child)
14+
: base(cx)
15+
{
16+
this.kind = kind;
17+
this.parent = parent;
18+
this.child = child;
19+
}
20+
21+
protected override void Populate(TextWriter trapFile)
22+
{
23+
trapFile.statements(this, kind);
24+
if (parent.IsTopLevelParent)
25+
{
26+
trapFile.stmt_parent_top_level(this, child, parent);
27+
}
28+
else
29+
{
30+
trapFile.stmt_parent(this, child, parent);
31+
}
32+
33+
PopulateStatement(trapFile);
34+
}
35+
36+
protected abstract void PopulateStatement(TextWriter trapFile);
37+
38+
public static Statement Create(Context cx, StatementSyntax node, IStatementParentEntity parent, int child) =>
1139
Statements.Factory.Create(cx, node, parent, child);
1240

1341
/// <summary>
@@ -16,14 +44,10 @@ public static Statement Create(Context cx, StatementSyntax node, Statement paren
1644
/// </summary>
1745
public virtual int NumberOfStatements => 1;
1846

19-
public override Microsoft.CodeAnalysis.Location ReportingLocation => GetStatementSyntax().GetLocation();
20-
2147
bool IExpressionParentEntity.IsTopLevelParent => false;
2248

2349
bool IStatementParentEntity.IsTopLevelParent => false;
2450

25-
protected abstract CSharpSyntaxNode GetStatementSyntax();
26-
2751
public override TrapStackBehaviour TrapStackBehaviour => TrapStackBehaviour.NeedsLabel;
2852
}
2953
}

csharp/extractor/Semmle.Extraction.CSharp/Entities/Statement`1.cs

Lines changed: 7 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,39 +8,27 @@ namespace Semmle.Extraction.CSharp.Entities
88
internal abstract class Statement<TSyntax> : Statement where TSyntax : CSharpSyntaxNode
99
{
1010
protected readonly TSyntax Stmt;
11-
private readonly int child;
12-
private readonly Kinds.StmtKind kind;
13-
private readonly IStatementParentEntity parent;
1411
private readonly Location location;
1512

16-
protected override CSharpSyntaxNode GetStatementSyntax() => Stmt;
17-
1813
protected Statement(Context cx, TSyntax stmt, Kinds.StmtKind kind, IStatementParentEntity parent, int child, Location location)
19-
: base(cx)
14+
: base(cx, kind, parent, child)
2015
{
2116
Stmt = stmt;
22-
this.parent = parent;
23-
this.child = child;
2417
this.location = location;
25-
this.kind = kind;
2618
cx.BindComments(this, location.symbol);
2719
}
2820

21+
protected Statement(Context cx, TSyntax stmt, Kinds.StmtKind kind, IStatementParentEntity parent, int child)
22+
: this(cx, stmt, kind, parent, child, cx.CreateLocation(stmt.FixedLocation())) { }
23+
2924
protected sealed override void Populate(TextWriter trapFile)
3025
{
31-
trapFile.statements(this, kind);
32-
if (parent.IsTopLevelParent)
33-
trapFile.stmt_parent_top_level(this, child, parent);
34-
else
35-
trapFile.stmt_parent(this, child, parent);
26+
base.Populate(trapFile);
27+
3628
trapFile.stmt_location(this, location);
37-
PopulateStatement(trapFile);
3829
}
3930

40-
protected abstract void PopulateStatement(TextWriter trapFile);
41-
42-
protected Statement(Context cx, TSyntax stmt, Kinds.StmtKind kind, IStatementParentEntity parent, int child)
43-
: this(cx, stmt, kind, parent, child, cx.CreateLocation(stmt.FixedLocation())) { }
31+
public override Microsoft.CodeAnalysis.Location ReportingLocation => Stmt.GetLocation();
4432

4533
public override string ToString() => Label.ToString();
4634
}

csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Factory.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ namespace Semmle.Extraction.CSharp.Entities.Statements
55
{
66
internal static class Factory
77
{
8-
internal static Statement Create(Context cx, StatementSyntax node, Statement parent, int child)
8+
internal static Statement Create(Context cx, StatementSyntax node, IStatementParentEntity parent, int child)
99
{
1010
switch (node.Kind())
1111
{
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
using Semmle.Extraction.Kinds;
2+
using System.Linq;
3+
using System.IO;
4+
using Semmle.Extraction.Entities;
5+
6+
namespace Semmle.Extraction.CSharp.Entities.Statements
7+
{
8+
internal class GlobalStatementsBlock : Statement
9+
{
10+
private GlobalStatementsBlock(Context cx, Method parent)
11+
: base(cx, StmtKind.BLOCK, parent, 0) { }
12+
13+
public override Microsoft.CodeAnalysis.Location ReportingLocation
14+
{
15+
get
16+
{
17+
// We only create a `GlobalStatementsBlock` if there are global statements. This also means that the
18+
// entry point is going to be the generated method around those global statements
19+
return cx.Compilation.GetEntryPoint(System.Threading.CancellationToken.None)
20+
?.DeclaringSyntaxReferences
21+
.FirstOrDefault()
22+
?.GetSyntax()
23+
.GetLocation();
24+
}
25+
}
26+
27+
public static GlobalStatementsBlock Create(Context cx, Method parent)
28+
{
29+
var ret = new GlobalStatementsBlock(cx, parent);
30+
ret.TryPopulate();
31+
return ret;
32+
}
33+
34+
protected override void PopulateStatement(TextWriter trapFile)
35+
{
36+
trapFile.global_stmt_block(this);
37+
38+
trapFile.stmt_location(this, cx.CreateLocation(ReportingLocation));
39+
}
40+
}
41+
}

csharp/extractor/Semmle.Extraction.CSharp/Populators/CompilationUnitVisitor.cs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
using Microsoft.CodeAnalysis.CSharp;
33
using Microsoft.CodeAnalysis.CSharp.Syntax;
44
using Semmle.Util.Logging;
5+
using Semmle.Extraction.CSharp.Entities;
6+
using Semmle.Extraction.CSharp.Entities.Statements;
7+
using System.Linq;
58

69
namespace Semmle.Extraction.CSharp.Populators
710
{
@@ -23,6 +26,8 @@ public override void VisitCompilationUnit(CompilationUnitSyntax compilationUnit)
2326
Cx.Try(m, null, () => ((CSharpSyntaxNode)m).Accept(this));
2427
}
2528

29+
ExtractGlobalStatements(compilationUnit);
30+
2631
// Gather comments:
2732
foreach (var trivia in compilationUnit.DescendantTrivia(compilationUnit.Span, descendIntoTrivia: true))
2833
{
@@ -39,5 +44,30 @@ public override void VisitCompilationUnit(CompilationUnitSyntax compilationUnit)
3944
CommentPopulator.ExtractComment(Cx, trivia);
4045
}
4146
}
47+
48+
private void ExtractGlobalStatements(CompilationUnitSyntax compilationUnit)
49+
{
50+
var globalStatements = compilationUnit
51+
.ChildNodes()
52+
.OfType<GlobalStatementSyntax>()
53+
.ToList();
54+
55+
if (!globalStatements.Any())
56+
{
57+
return;
58+
}
59+
60+
var entryPoint = Cx.Compilation.GetEntryPoint(System.Threading.CancellationToken.None);
61+
var entryMethod = Method.Create(Cx, entryPoint);
62+
var block = GlobalStatementsBlock.Create(Cx, entryMethod);
63+
64+
for (var i = 0; i < globalStatements.Count; i++)
65+
{
66+
if (globalStatements[i].Statement is object)
67+
{
68+
Statement.Create(Cx, globalStatements[i].Statement, block, i);
69+
}
70+
}
71+
}
4272
}
4373
}

csharp/extractor/Semmle.Extraction.CSharp/Populators/TypeContainerVisitor.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,12 @@ public override void DefaultVisit(SyntaxNode node)
3939
throw new InternalError(node, "Unhandled top-level syntax node");
4040
}
4141

42+
public override void VisitGlobalStatement(GlobalStatementSyntax node)
43+
{
44+
// Intentionally left empty.
45+
// Global statements are handled in CompilationUnitVisitor
46+
}
47+
4248
public override void VisitDelegateDeclaration(DelegateDeclarationSyntax node)
4349
{
4450
Entities.NamedType.Create(Cx, Cx.GetModel(node).GetDeclaredSymbol(node)).ExtractRecursive(TrapFile, Parent);
@@ -84,7 +90,7 @@ public override void VisitAttributeList(AttributeListSyntax node)
8490
{
8591
if (attributeLookup.Value(attribute) is AttributeData attributeData)
8692
{
87-
var ae = Semmle.Extraction.CSharp.Entities.Attribute.Create(Cx, attributeData, outputAssembly);
93+
var ae = Entities.Attribute.Create(Cx, attributeData, outputAssembly);
8894
Cx.BindComments(ae, attribute.GetLocation());
8995
}
9096
}

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,11 @@ internal static void methods(this TextWriter trapFile, Method method, string nam
379379
trapFile.WriteTuple("methods", method, name, declType, retType, originalDefinition);
380380
}
381381

382+
internal static void entry_methods(this TextWriter trapFile, Method method)
383+
{
384+
trapFile.WriteTuple("entry_methods", method);
385+
}
386+
382387
internal static void modifiers(this TextWriter trapFile, Label entity, string modifier)
383388
{
384389
trapFile.WriteTuple("modifiers", entity, modifier);
@@ -509,6 +514,11 @@ internal static void stackalloc_array_creation(this TextWriter trapFile, Express
509514
trapFile.WriteTuple("stackalloc_array_creation", array);
510515
}
511516

517+
internal static void global_stmt_block(this TextWriter trapFile, Entities.Statements.GlobalStatementsBlock block)
518+
{
519+
trapFile.WriteTuple("global_stmt_block", block);
520+
}
521+
512522
internal static void stmt_location(this TextWriter trapFile, Statement stmt, Location location)
513523
{
514524
trapFile.WriteTuple("stmt_location", stmt, location);

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,9 @@ class Method extends Callable, Virtualizable, Attributable, @method {
292292
}
293293

294294
override string getAPrimaryQlClass() { result = "Method" }
295+
296+
/** Holds if this method is the entry method of the compilation. */
297+
predicate isEntry() { entry_methods(this) }
295298
}
296299

297300
/**

csharp/ql/src/semmle/code/csharp/PrintAst.qll

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,8 @@ private newtype TPrintAstNode =
134134
TParametersNode(Parameterizable parameterizable) {
135135
shouldPrint(parameterizable, _) and
136136
parameterizable.getNumberOfParameters() > 0 and
137-
not isNotNeeded(parameterizable)
137+
not isNotNeeded(parameterizable) and
138+
exists(Parameter p | p.getDeclaringElement() = parameterizable and shouldPrint(p, _))
138139
} or
139140
TAttributesNode(Attributable attributable) {
140141
shouldPrint(attributable, _) and

0 commit comments

Comments
 (0)