From 19faf8f30bda96f32d7b2eb286b9c0d74ae75de6 Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Tue, 17 Mar 2026 13:29:36 +0100 Subject: [PATCH 1/2] C#: Add ObjectInitMethod as enclosing callable for the instance initializers. --- csharp/ql/lib/semmle/code/csharp/Callable.qll | 27 +++++++++++++++ .../semmle/code/csharp/ExprOrStmtParent.qll | 2 ++ .../internal/ControlFlowGraphImpl.qll | 33 ++----------------- 3 files changed, 32 insertions(+), 30 deletions(-) diff --git a/csharp/ql/lib/semmle/code/csharp/Callable.qll b/csharp/ql/lib/semmle/code/csharp/Callable.qll index f8346cfe01e2..611b578b859a 100644 --- a/csharp/ql/lib/semmle/code/csharp/Callable.qll +++ b/csharp/ql/lib/semmle/code/csharp/Callable.qll @@ -336,6 +336,22 @@ class ExtensionTypeExtensionMethod extends ExtensionMethodImpl { ExtensionTypeExtensionMethod() { this.isInExtension() } } +/** + * A non-static member with an initializer, for example a field `int Field = 0`. + */ +private class InitializedInstanceMember extends Member { + private AssignExpr ae; + + InitializedInstanceMember() { + not this.isStatic() and + expr_parent_top_level(ae, _, this) and + not ae = getExpressionBody(_) + } + + /** Gets the initializer expression. */ + AssignExpr getInitializer() { result = ae } +} + /** * An object initializer method. * @@ -347,6 +363,17 @@ class ExtensionTypeExtensionMethod extends ExtensionMethodImpl { */ class ObjectInitMethod extends Method { ObjectInitMethod() { this.getName() = "" } + + /** + * Holds if this object initializer method performs the initialization + * of a member via assignment `init`. + */ + predicate initializes(AssignExpr init) { + exists(InitializedInstanceMember m | + this.getDeclaringType().getAMember() = m and + init = m.getInitializer() + ) + } } /** diff --git a/csharp/ql/lib/semmle/code/csharp/ExprOrStmtParent.qll b/csharp/ql/lib/semmle/code/csharp/ExprOrStmtParent.qll index aa834ef91038..5afacf608a8c 100644 --- a/csharp/ql/lib/semmle/code/csharp/ExprOrStmtParent.qll +++ b/csharp/ql/lib/semmle/code/csharp/ExprOrStmtParent.qll @@ -214,6 +214,8 @@ private module Cached { parent*(enclosingStart(cfe), c.(Constructor).getInitializer()) or parent*(cfe, c.(Constructor).getObjectInitializerCall()) + or + parent*(cfe, any(AssignExpr init | c.(ObjectInitMethod).initializes(init))) } /** Holds if the enclosing statement of expression `e` is `s`. */ diff --git a/csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImpl.qll b/csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImpl.qll index 1696869e5911..0bdf1f795db2 100644 --- a/csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImpl.qll +++ b/csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImpl.qll @@ -10,42 +10,15 @@ private import semmle.code.csharp.ExprOrStmtParent private import semmle.code.csharp.commons.Compilation private module Initializers { - /** - * A non-static member with an initializer, for example a field `int Field = 0`. - */ - class InitializedInstanceMember extends Member { - private AssignExpr ae; - - InitializedInstanceMember() { - not this.isStatic() and - expr_parent_top_level(ae, _, this) and - not ae = any(Callable c).getExpressionBody() - } - - /** Gets the initializer expression. */ - AssignExpr getInitializer() { result = ae } - } - - /** - * Holds if `obinit` is an object initializer method that performs the initialization - * of a member via assignment `init`. - */ - predicate obinitInitializes(ObjectInitMethod obinit, AssignExpr init) { - exists(InitializedInstanceMember m | - obinit.getDeclaringType().getAMember() = m and - init = m.getInitializer() - ) - } - /** * Gets the `i`th member initializer expression for object initializer method `obinit` * in compilation `comp`. */ AssignExpr initializedInstanceMemberOrder(ObjectInitMethod obinit, CompilationExt comp, int i) { - obinitInitializes(obinit, result) and + obinit.initializes(result) and result = rank[i + 1](AssignExpr ae0, Location l | - obinitInitializes(obinit, ae0) and + obinit.initializes(ae0) and l = ae0.getLocation() and getCompilation(l.getFile()) = comp | @@ -74,7 +47,7 @@ class CfgScope extends Element, @top_level_exprorstmt_parent { any(Callable c | c.(Constructor).hasInitializer() or - Initializers::obinitInitializes(c, _) + c.(ObjectInitMethod).initializes(_) or c.hasBody() ) From af63e636861038f59cceded11c38b5ffb46c35fa Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Tue, 17 Mar 2026 14:12:18 +0100 Subject: [PATCH 2/2] C#: Accept test changes. --- csharp/ql/test/library-tests/dispatch/CallGraph.expected | 1 + .../structuralcomparison/structuralComparison.expected | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/csharp/ql/test/library-tests/dispatch/CallGraph.expected b/csharp/ql/test/library-tests/dispatch/CallGraph.expected index 31e2a99ae24c..e7ebca868ba8 100644 --- a/csharp/ql/test/library-tests/dispatch/CallGraph.expected +++ b/csharp/ql/test/library-tests/dispatch/CallGraph.expected @@ -24,6 +24,7 @@ | ExactCallable.cs:15:25:15:35 | Run`2 | ExactCallable.cs:172:21:172:33 | MethodWithOut | | ExactCallable.cs:15:25:15:35 | Run`2 | ExactCallable.cs:177:21:177:34 | MethodWithOut2 | | ExactCallable.cs:182:21:182:22 | M1 | ExactCallable.cs:187:21:187:22 | M2 | +| TypeFlow.cs:3:7:3:14 | | TypeFlow.cs:22:20:22:22 | set_Prop | | TypeFlow.cs:5:5:5:12 | TypeFlow | TypeFlow.cs:24:10:24:12 | Run | | TypeFlow.cs:24:10:24:12 | Run | TypeFlow.cs:12:29:12:34 | Method | | TypeFlow.cs:24:10:24:12 | Run | TypeFlow.cs:17:30:17:35 | Method | diff --git a/csharp/ql/test/library-tests/structuralcomparison/structuralComparison.expected b/csharp/ql/test/library-tests/structuralcomparison/structuralComparison.expected index 0f131d8c25cb..d9b6636469ab 100644 --- a/csharp/ql/test/library-tests/structuralcomparison/structuralComparison.expected +++ b/csharp/ql/test/library-tests/structuralcomparison/structuralComparison.expected @@ -56,11 +56,11 @@ gvn | StructuralComparison.cs:3:14:3:18 | this access | (kind:Expr(12),false,Class) | | StructuralComparison.cs:3:14:3:18 | {...} | (kind:Stmt(1)) | | StructuralComparison.cs:5:26:5:26 | access to field x | (kind:Expr(16),true,x) | -| StructuralComparison.cs:5:26:5:26 | this access | (kind:Expr(12)) | +| StructuralComparison.cs:5:26:5:26 | this access | (kind:Expr(12),false,Class) | | StructuralComparison.cs:5:26:5:30 | ... = ... | ((kind:Expr(16),true,x) :: (0 :: (kind:Expr(63)))) | | StructuralComparison.cs:5:30:5:30 | 0 | 0 | | StructuralComparison.cs:6:26:6:26 | access to field y | (kind:Expr(16),true,y) | -| StructuralComparison.cs:6:26:6:26 | this access | (kind:Expr(12)) | +| StructuralComparison.cs:6:26:6:26 | this access | (kind:Expr(12),false,Class) | | StructuralComparison.cs:6:26:6:30 | ... = ... | ((kind:Expr(16),true,y) :: (1 :: (kind:Expr(63)))) | | StructuralComparison.cs:6:30:6:30 | 1 | 1 | | StructuralComparison.cs:8:24:8:24 | 0 | 0 |