@@ -21,6 +21,7 @@ private import semmle.code.csharp.frameworks.system.threading.Tasks
2121private import semmle.code.cil.Ssa:: Ssa as CilSsa
2222private import semmle.code.cil.internal.SsaImpl as CilSsaImpl
2323private import codeql.util.Unit
24+ private import codeql.util.Boolean
2425
2526/** Gets the callable in which this node occurs. */
2627DataFlowCallable nodeGetEnclosingCallable ( Node n ) {
@@ -37,6 +38,21 @@ predicate isArgumentNode(ArgumentNode arg, DataFlowCall c, ArgumentPosition pos)
3738 arg .argumentOf ( c , pos )
3839}
3940
41+ /**
42+ * Gets the control flow node used for data flow purposes for the primary constructor
43+ * parameter access `pa`.
44+ */
45+ private ControlFlow:: Node getPrimaryConstructorParameterCfn ( ParameterAccess pa ) {
46+ pa .getTarget ( ) .getCallable ( ) instanceof PrimaryConstructor and
47+ (
48+ pa instanceof ParameterRead and
49+ result = pa .getAControlFlowNode ( )
50+ or
51+ pa instanceof ParameterWrite and
52+ exists ( AssignExpr ae | pa = ae .getLValue ( ) and result = ae .getAControlFlowNode ( ) )
53+ )
54+ }
55+
4056abstract class NodeImpl extends Node {
4157 /** Do not call: use `getEnclosingCallable()` instead. */
4258 abstract DataFlowCallable getEnclosingCallableImpl ( ) ;
@@ -124,9 +140,21 @@ private module ThisFlow {
124140 n .( InstanceParameterNode ) .getCallable ( ) = cfn .( ControlFlow:: Nodes:: EntryNode ) .getCallable ( )
125141 or
126142 n .asExprAtNode ( cfn ) = any ( Expr e | e instanceof ThisAccess or e instanceof BaseAccess )
143+ or
144+ exists ( InstanceParameterAccessNode pan | pan = n |
145+ pan .getUnderlyingControlFlowNode ( ) = cfn and pan .isPreUpdate ( )
146+ )
127147 }
128148
129- private predicate thisAccess ( Node n , BasicBlock bb , int i ) { thisAccess ( n , bb .getNode ( i ) ) }
149+ private predicate thisAccess ( Node n , BasicBlock bb , int i ) {
150+ thisAccess ( n , bb .getNode ( i ) )
151+ or
152+ exists ( Parameter p | n .( PrimaryConstructorThisAccessNode ) .getParameter ( ) = p |
153+ bb .getCallable ( ) = p .getCallable ( ) and
154+ i = p .getPosition ( ) + 1 and
155+ not n instanceof PostUpdateNode
156+ )
157+ }
130158
131159 private predicate thisRank ( Node n , BasicBlock bb , int rankix ) {
132160 exists ( int i |
@@ -925,7 +953,17 @@ private module Cached {
925953 TParamsArgumentNode ( ControlFlow:: Node callCfn ) {
926954 callCfn = any ( Call c | isParamsArg ( c , _, _) ) .getAControlFlowNode ( )
927955 } or
928- TFlowInsensitiveFieldNode ( FieldOrProperty f ) { f .isFieldLike ( ) }
956+ TFlowInsensitiveFieldNode ( FieldOrProperty f ) { f .isFieldLike ( ) } or
957+ TInstanceParameterAccessNode ( ControlFlow:: Node cfn , boolean isPostUpdate ) {
958+ exists ( ParameterAccess pa | cfn = getPrimaryConstructorParameterCfn ( pa ) |
959+ isPostUpdate = false
960+ or
961+ pa instanceof ParameterWrite and isPostUpdate = true
962+ )
963+ } or
964+ TPrimaryConstructorThisAccessNode ( Parameter p , Boolean isPostUpdate ) {
965+ p .getCallable ( ) instanceof PrimaryConstructor
966+ }
929967
930968 /**
931969 * Holds if data flows from `nodeFrom` to `nodeTo` in exactly one local
@@ -961,14 +999,20 @@ private module Cached {
961999 TFieldContent ( Field f ) { f .isUnboundDeclaration ( ) } or
9621000 TPropertyContent ( Property p ) { p .isUnboundDeclaration ( ) } or
9631001 TElementContent ( ) or
964- TSyntheticFieldContent ( SyntheticField f )
1002+ TSyntheticFieldContent ( SyntheticField f ) or
1003+ TPrimaryConstructorParameterContent ( Parameter p ) {
1004+ p .getCallable ( ) instanceof PrimaryConstructor
1005+ }
9651006
9661007 cached
9671008 newtype TContentApprox =
9681009 TFieldApproxContent ( string firstChar ) { firstChar = approximateFieldContent ( _) } or
9691010 TPropertyApproxContent ( string firstChar ) { firstChar = approximatePropertyContent ( _) } or
9701011 TElementApproxContent ( ) or
971- TSyntheticFieldApproxContent ( )
1012+ TSyntheticFieldApproxContent ( ) or
1013+ TPrimaryConstructorParameterApproxContent ( string firstChar ) {
1014+ firstChar = approximatePrimaryConstructorParameterContent ( _)
1015+ }
9721016
9731017 pragma [ nomagic]
9741018 private predicate commonSubTypeGeneral ( DataFlowTypeOrUnifiable t1 , RelevantGvnType t2 ) {
@@ -1037,6 +1081,10 @@ predicate nodeIsHidden(Node n) {
10371081 n .asExpr ( ) = any ( WithExpr we ) .getInitializer ( )
10381082 or
10391083 n instanceof FlowInsensitiveFieldNode
1084+ or
1085+ n instanceof InstanceParameterAccessNode
1086+ or
1087+ n instanceof PrimaryConstructorThisAccessNode
10401088}
10411089
10421090/** A CIL SSA definition, viewed as a node in a data flow graph. */
@@ -1745,6 +1793,77 @@ class FlowSummaryNode extends NodeImpl, TFlowSummaryNode {
17451793 override string toStringImpl ( ) { result = this .getSummaryNode ( ) .toString ( ) }
17461794}
17471795
1796+ /**
1797+ * A data-flow node used to model reading and writing of primary constructor parameters.
1798+ */
1799+ class InstanceParameterAccessNode extends NodeImpl , TInstanceParameterAccessNode {
1800+ private ControlFlow:: Node cfn ;
1801+ private boolean isPostUpdate ;
1802+ private Parameter p ;
1803+
1804+ InstanceParameterAccessNode ( ) {
1805+ this = TInstanceParameterAccessNode ( cfn , isPostUpdate ) and
1806+ exists ( ParameterAccess pa | cfn = getPrimaryConstructorParameterCfn ( pa ) and pa .getTarget ( ) = p )
1807+ }
1808+
1809+ override DataFlowCallable getEnclosingCallableImpl ( ) {
1810+ result .asCallable ( ) = cfn .getEnclosingCallable ( )
1811+ }
1812+
1813+ override Type getTypeImpl ( ) { result = cfn .getEnclosingCallable ( ) .getDeclaringType ( ) }
1814+
1815+ override ControlFlow:: Nodes:: ElementNode getControlFlowNodeImpl ( ) { none ( ) }
1816+
1817+ override Location getLocationImpl ( ) { result = cfn .getLocation ( ) }
1818+
1819+ override string toStringImpl ( ) { result = "this" }
1820+
1821+ /**
1822+ * Gets the underlying control flow node.
1823+ */
1824+ ControlFlow:: Node getUnderlyingControlFlowNode ( ) { result = cfn }
1825+
1826+ /**
1827+ * Gets the primary constructor parameter that this is a this access to.
1828+ */
1829+ Parameter getParameter ( ) { result = p }
1830+
1831+ /**
1832+ * Holds if the this parameter access node corresponds to a pre-update node.
1833+ */
1834+ predicate isPreUpdate ( ) { isPostUpdate = false }
1835+ }
1836+
1837+ /**
1838+ * A data-flow node used to synthesize the body of a primary constructor.
1839+ */
1840+ class PrimaryConstructorThisAccessNode extends NodeImpl , TPrimaryConstructorThisAccessNode {
1841+ private Parameter p ;
1842+ private boolean isPostUpdate ;
1843+
1844+ PrimaryConstructorThisAccessNode ( ) { this = TPrimaryConstructorThisAccessNode ( p , isPostUpdate ) }
1845+
1846+ override DataFlowCallable getEnclosingCallableImpl ( ) { result .asCallable ( ) = p .getCallable ( ) }
1847+
1848+ override Type getTypeImpl ( ) { result = p .getCallable ( ) .getDeclaringType ( ) }
1849+
1850+ override ControlFlow:: Nodes:: ElementNode getControlFlowNodeImpl ( ) { none ( ) }
1851+
1852+ override Location getLocationImpl ( ) { result = p .getLocation ( ) }
1853+
1854+ override string toStringImpl ( ) { result = "this" }
1855+
1856+ /**
1857+ * Gets the primary constructor parameter that this is a this access to.
1858+ */
1859+ Parameter getParameter ( ) { result = p }
1860+
1861+ /**
1862+ * Holds if this is a this access for a primary constructor parameter write.
1863+ */
1864+ predicate isPostUpdate ( ) { isPostUpdate = true }
1865+ }
1866+
17481867/** A field or a property. */
17491868class FieldOrProperty extends Assignable , Modifiable {
17501869 FieldOrProperty ( ) {
@@ -1881,6 +2000,16 @@ private PropertyContent getResultContent() {
18812000 result .getProperty ( ) = any ( SystemThreadingTasksTaskTClass c_ ) .getResultProperty ( )
18822001}
18832002
2003+ private predicate primaryConstructorParameterStore ( Node node1 , ContentSet c , Node node2 ) {
2004+ exists ( AssignExpr ae , ParameterWrite pa , PrimaryConstructor constructor |
2005+ ae .getLValue ( ) = pa and
2006+ pa .getTarget ( ) = constructor .getAParameter ( ) and
2007+ node1 .asExpr ( ) = ae .getRValue ( ) and
2008+ node2 = TInstanceParameterAccessNode ( ae .getAControlFlowNode ( ) , true ) and
2009+ c .( PrimaryConstructorParameterContent ) .getParameter ( ) = pa .getTarget ( )
2010+ )
2011+ }
2012+
18842013/**
18852014 * Holds if data can flow from `node1` to `node2` via an assignment to
18862015 * content `c`.
@@ -1918,6 +2047,14 @@ predicate storeStep(Node node1, ContentSet c, Node node2) {
19182047 c = getResultContent ( )
19192048 )
19202049 or
2050+ primaryConstructorParameterStore ( node1 , c , node2 )
2051+ or
2052+ exists ( Parameter p |
2053+ node1 = TExplicitParameterNode ( p ) and
2054+ node2 = TPrimaryConstructorThisAccessNode ( p , true ) and
2055+ c .( PrimaryConstructorParameterContent ) .getParameter ( ) = p
2056+ )
2057+ or
19212058 FlowSummaryImpl:: Private:: Steps:: summaryStoreStep ( node1 .( FlowSummaryNode ) .getSummaryNode ( ) , c ,
19222059 node2 .( FlowSummaryNode ) .getSummaryNode ( ) )
19232060}
@@ -2010,6 +2147,12 @@ predicate readStep(Node node1, ContentSet c, Node node2) {
20102147 node2 .asExpr ( ) .( AwaitExpr ) .getExpr ( ) = node1 .asExpr ( ) and
20112148 c = getResultContent ( )
20122149 or
2150+ exists ( InstanceParameterAccessNode n | n = node1 |
2151+ n .getUnderlyingControlFlowNode ( ) = node2 .( ExprNode ) .getControlFlowNode ( ) and
2152+ n .getParameter ( ) = c .( PrimaryConstructorParameterContent ) .getParameter ( )
2153+ ) and
2154+ node2 .asExpr ( ) instanceof ParameterRead
2155+ or
20132156 // node1 = (..., node2, ...)
20142157 // node1.ItemX flows to node2
20152158 exists ( TupleExpr te , int i , Expr item |
@@ -2072,6 +2215,10 @@ predicate clearsContent(Node n, ContentSet c) {
20722215 c .( FieldContent ) .getField ( ) = f .getUnboundDeclaration ( ) and
20732216 not f .isRef ( )
20742217 )
2218+ or
2219+ exists ( Node n1 |
2220+ primaryConstructorParameterStore ( _, c , n1 ) and n = n1 .( PostUpdateNode ) .getPreUpdateNode ( )
2221+ )
20752222}
20762223
20772224/**
@@ -2361,6 +2508,32 @@ module PostUpdateNodes {
23612508
23622509 override Node getPreUpdateNode ( ) { result .( FlowSummaryNode ) .getSummaryNode ( ) = preUpdateNode }
23632510 }
2511+
2512+ private class InstanceParameterAccessPostUpdateNode extends PostUpdateNode ,
2513+ InstanceParameterAccessNode
2514+ {
2515+ private ControlFlow:: Node cfg ;
2516+
2517+ InstanceParameterAccessPostUpdateNode ( ) { this = TInstanceParameterAccessNode ( cfg , true ) }
2518+
2519+ override Node getPreUpdateNode ( ) { result = TInstanceParameterAccessNode ( cfg , false ) }
2520+
2521+ override string toStringImpl ( ) { result = "[post] this" }
2522+ }
2523+
2524+ private class PrimaryConstructorThisAccessPostUpdateNode extends PostUpdateNode ,
2525+ PrimaryConstructorThisAccessNode
2526+ {
2527+ private Parameter p ;
2528+
2529+ PrimaryConstructorThisAccessPostUpdateNode ( ) {
2530+ this = TPrimaryConstructorThisAccessNode ( p , true )
2531+ }
2532+
2533+ override Node getPreUpdateNode ( ) { result = TPrimaryConstructorThisAccessNode ( p , false ) }
2534+
2535+ override string toStringImpl ( ) { result = "[post] this" }
2536+ }
23642537}
23652538
23662539private import PostUpdateNodes
@@ -2537,6 +2710,11 @@ class ContentApprox extends TContentApprox {
25372710 this = TElementApproxContent ( ) and result = "element"
25382711 or
25392712 this = TSyntheticFieldApproxContent ( ) and result = "approximated synthetic field"
2713+ or
2714+ exists ( string firstChar |
2715+ this = TPrimaryConstructorParameterApproxContent ( firstChar ) and
2716+ result = "approximated parameter field " + firstChar
2717+ )
25402718 }
25412719}
25422720
@@ -2550,6 +2728,14 @@ private string approximatePropertyContent(PropertyContent pc) {
25502728 result = pc .getProperty ( ) .getName ( ) .prefix ( 1 )
25512729}
25522730
2731+ /**
2732+ * Gets a string for approximating the name of a synthetic field corresponding
2733+ * to a primary constructor parameter.
2734+ */
2735+ private string approximatePrimaryConstructorParameterContent ( PrimaryConstructorParameterContent pc ) {
2736+ result = pc .getParameter ( ) .getName ( ) .prefix ( 1 )
2737+ }
2738+
25532739/** Gets an approximated value for content `c`. */
25542740pragma [ nomagic]
25552741ContentApprox getContentApprox ( Content c ) {
@@ -2560,6 +2746,9 @@ ContentApprox getContentApprox(Content c) {
25602746 c instanceof ElementContent and result = TElementApproxContent ( )
25612747 or
25622748 c instanceof SyntheticFieldContent and result = TSyntheticFieldApproxContent ( )
2749+ or
2750+ result =
2751+ TPrimaryConstructorParameterApproxContent ( approximatePrimaryConstructorParameterContent ( c ) )
25632752}
25642753
25652754/**
0 commit comments