Skip to content

Commit bb50116

Browse files
committed
fu: static member init
1 parent 476cc1d commit bb50116

File tree

1 file changed

+59
-28
lines changed

1 file changed

+59
-28
lines changed

csharp/ql/lib/semmle/code/csharp/controlflow/ControlFlowGraph2.qll

Lines changed: 59 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,8 @@ module Ast implements AstSig<CS::Location> {
8585

8686
Callable getEnclosingCallable(AstNode node) {
8787
result = node.(CS::ControlFlowElement).getEnclosingCallable() or
88-
Initializers::obinitInitializes(result, getParent*(node))
88+
result.(CS::ObjectInitMethod).initializes(getParent*(node)) or
89+
Initializers::staticMemberInitializer(result, getParent*(node))
8990
}
9091

9192
class Callable = CS::Callable;
@@ -94,7 +95,8 @@ module Ast implements AstSig<CS::Location> {
9495
result = c.getBody() or
9596
result = c.(CS::Constructor).getObjectInitializerCall() or
9697
result = c.(CS::Constructor).getInitializer() or
97-
Initializers::obinitInitializes(c, result)
98+
c.(CS::ObjectInitMethod).initializes(result) or
99+
Initializers::staticMemberInitializer(c, result)
98100
}
99101

100102
class Stmt = CS::Stmt;
@@ -248,44 +250,64 @@ private CompilationExt getCompilation(CS::File f) {
248250
}
249251

250252
private module Initializers {
253+
private import semmle.code.csharp.ExprOrStmtParent as ExprOrStmtParent
254+
251255
/**
252-
* A non-static member with an initializer, for example a field `int Field = 0`.
256+
* The `expr_parent_top_level_adjusted()` relation restricted to exclude relations
257+
* between properties and their getters' expression bodies in properties such as
258+
* `int P => 0`.
259+
*
260+
* This is in order to only associate the expression body with one CFG scope, namely
261+
* the getter (and not the declaration itself).
253262
*/
254-
class InitializedInstanceMember extends CS::Member {
255-
private CS::AssignExpr ae;
256-
257-
InitializedInstanceMember() {
258-
not this.isStatic() and
259-
expr_parent_top_level(ae, _, this) and
260-
not ae = any(CS::Callable c).getExpressionBody()
261-
}
262-
263-
/** Gets the initializer expression. */
264-
CS::AssignExpr getInitializer() { result = ae }
263+
private predicate expr_parent_top_level_adjusted2(
264+
CS::Expr child, int i, @top_level_exprorstmt_parent parent
265+
) {
266+
ExprOrStmtParent::expr_parent_top_level_adjusted(child, i, parent) and
267+
not exists(CS::Getter g |
268+
g.getDeclaration() = parent and
269+
i = 0
270+
)
265271
}
266272

267273
/**
268-
* Holds if `obinit` is an object initializer method that performs the initialization
269-
* of a member via assignment `init`.
274+
* Holds if `init` is a static member initializer and `staticCtor` is the
275+
* static constructor in the same declaring type. Hence, `staticCtor` can be
276+
* considered to execute `init` prior to the execution of its body.
270277
*/
271-
predicate obinitInitializes(CS::ObjectInitMethod obinit, CS::AssignExpr init) {
272-
exists(InitializedInstanceMember m |
273-
obinit.getDeclaringType().getAMember() = m and
274-
init = m.getInitializer()
278+
predicate staticMemberInitializer(CS::Constructor staticCtor, CS::Expr init) {
279+
exists(CS::Assignable a |
280+
a.(CS::Modifiable).isStatic() and
281+
expr_parent_top_level_adjusted2(init, _, a) and
282+
a.getDeclaringType() = staticCtor.getDeclaringType() and
283+
staticCtor.isStatic()
275284
)
276285
}
277286

287+
/**
288+
* Gets the `i`th static member initializer expression for static constructor `staticCtor`.
289+
*/
290+
CS::Expr initializedStaticMemberOrder(CS::Constructor staticCtor, int i) {
291+
result =
292+
rank[i + 1](CS::Expr init, CS::Location l |
293+
staticMemberInitializer(staticCtor, init) and
294+
l = init.getLocation()
295+
|
296+
init order by l.getStartLine(), l.getStartColumn(), l.getFile().getAbsolutePath()
297+
)
298+
}
299+
278300
/**
279301
* Gets the `i`th member initializer expression for object initializer method `obinit`
280302
* in compilation `comp`.
281303
*/
282304
CS::AssignExpr initializedInstanceMemberOrder(
283305
CS::ObjectInitMethod obinit, CompilationExt comp, int i
284306
) {
285-
obinitInitializes(obinit, result) and
307+
obinit.initializes(result) and
286308
result =
287309
rank[i + 1](CS::AssignExpr ae0, CS::Location l |
288-
obinitInitializes(obinit, ae0) and
310+
obinit.initializes(ae0) and
289311
l = ae0.getLocation() and
290312
getCompilation(l.getFile()) = comp
291313
|
@@ -306,7 +328,6 @@ private module Initializers {
306328
}
307329

308330
// module Consistency = ControlFlow::Consistency;
309-
310331
private module Input implements InputSig1, InputSig2 {
311332
predicate cfgCachedStageRef() { CfgCachedStage::ref() }
312333

@@ -407,12 +428,22 @@ private module Input implements InputSig1, InputSig2 {
407428
predicate step(PreControlFlowNode n1, PreControlFlowNode n2) {
408429
exists(CS::Constructor ctor |
409430
n1.(EntryNodeImpl).getEnclosingCallable() = ctor and
410-
if exists(ctor.getObjectInitializerCall())
411-
then n2.isBefore(ctor.getObjectInitializerCall())
431+
if Initializers::staticMemberInitializer(ctor, _)
432+
then n2.isBefore(Initializers::initializedStaticMemberOrder(ctor, 0))
412433
else
413-
if exists(ctor.getInitializer())
414-
then n2.isBefore(ctor.getInitializer())
415-
else n2.isBefore(ctor.getBody())
434+
if exists(ctor.getObjectInitializerCall())
435+
then n2.isBefore(ctor.getObjectInitializerCall())
436+
else
437+
if exists(ctor.getInitializer())
438+
then n2.isBefore(ctor.getInitializer())
439+
else n2.isBefore(ctor.getBody())
440+
or
441+
exists(int i | n1.isAfter(Initializers::initializedStaticMemberOrder(ctor, i)) |
442+
n2.isBefore(Initializers::initializedStaticMemberOrder(ctor, i + 1))
443+
or
444+
not exists(Initializers::initializedStaticMemberOrder(ctor, i + 1)) and
445+
n2.isBefore(ctor.getBody())
446+
)
416447
or
417448
exists(CompilationExt comp |
418449
n1.isAfter(getObjectInitializerCall(ctor, comp)) and

0 commit comments

Comments
 (0)