@@ -21,6 +21,7 @@ private import semmle.code.csharp.frameworks.system.threading.Tasks
21
21
private import semmle.code.cil.Ssa:: Ssa as CilSsa
22
22
private import semmle.code.cil.internal.SsaImpl as CilSsaImpl
23
23
private import codeql.util.Unit
24
+ private import codeql.util.Boolean
24
25
25
26
/** Gets the callable in which this node occurs. */
26
27
DataFlowCallable nodeGetEnclosingCallable ( Node n ) {
@@ -37,6 +38,21 @@ predicate isArgumentNode(ArgumentNode arg, DataFlowCall c, ArgumentPosition pos)
37
38
arg .argumentOf ( c , pos )
38
39
}
39
40
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
+
40
56
abstract class NodeImpl extends Node {
41
57
/** Do not call: use `getEnclosingCallable()` instead. */
42
58
abstract DataFlowCallable getEnclosingCallableImpl ( ) ;
@@ -124,9 +140,21 @@ private module ThisFlow {
124
140
n .( InstanceParameterNode ) .getCallable ( ) = cfn .( ControlFlow:: Nodes:: EntryNode ) .getCallable ( )
125
141
or
126
142
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
+ )
127
147
}
128
148
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
+ }
130
158
131
159
private predicate thisRank ( Node n , BasicBlock bb , int rankix ) {
132
160
exists ( int i |
@@ -925,7 +953,17 @@ private module Cached {
925
953
TParamsArgumentNode ( ControlFlow:: Node callCfn ) {
926
954
callCfn = any ( Call c | isParamsArg ( c , _, _) ) .getAControlFlowNode ( )
927
955
} 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
+ }
929
967
930
968
/**
931
969
* Holds if data flows from `nodeFrom` to `nodeTo` in exactly one local
@@ -961,14 +999,20 @@ private module Cached {
961
999
TFieldContent ( Field f ) { f .isUnboundDeclaration ( ) } or
962
1000
TPropertyContent ( Property p ) { p .isUnboundDeclaration ( ) } or
963
1001
TElementContent ( ) or
964
- TSyntheticFieldContent ( SyntheticField f )
1002
+ TSyntheticFieldContent ( SyntheticField f ) or
1003
+ TPrimaryConstructorParameterContent ( Parameter p ) {
1004
+ p .getCallable ( ) instanceof PrimaryConstructor
1005
+ }
965
1006
966
1007
cached
967
1008
newtype TContentApprox =
968
1009
TFieldApproxContent ( string firstChar ) { firstChar = approximateFieldContent ( _) } or
969
1010
TPropertyApproxContent ( string firstChar ) { firstChar = approximatePropertyContent ( _) } or
970
1011
TElementApproxContent ( ) or
971
- TSyntheticFieldApproxContent ( )
1012
+ TSyntheticFieldApproxContent ( ) or
1013
+ TPrimaryConstructorParameterApproxContent ( string firstChar ) {
1014
+ firstChar = approximatePrimaryConstructorParameterContent ( _)
1015
+ }
972
1016
973
1017
pragma [ nomagic]
974
1018
private predicate commonSubTypeGeneral ( DataFlowTypeOrUnifiable t1 , RelevantGvnType t2 ) {
@@ -1037,6 +1081,10 @@ predicate nodeIsHidden(Node n) {
1037
1081
n .asExpr ( ) = any ( WithExpr we ) .getInitializer ( )
1038
1082
or
1039
1083
n instanceof FlowInsensitiveFieldNode
1084
+ or
1085
+ n instanceof InstanceParameterAccessNode
1086
+ or
1087
+ n instanceof PrimaryConstructorThisAccessNode
1040
1088
}
1041
1089
1042
1090
/** A CIL SSA definition, viewed as a node in a data flow graph. */
@@ -1745,6 +1793,77 @@ class FlowSummaryNode extends NodeImpl, TFlowSummaryNode {
1745
1793
override string toStringImpl ( ) { result = this .getSummaryNode ( ) .toString ( ) }
1746
1794
}
1747
1795
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
+
1748
1867
/** A field or a property. */
1749
1868
class FieldOrProperty extends Assignable , Modifiable {
1750
1869
FieldOrProperty ( ) {
@@ -1881,6 +2000,16 @@ private PropertyContent getResultContent() {
1881
2000
result .getProperty ( ) = any ( SystemThreadingTasksTaskTClass c_ ) .getResultProperty ( )
1882
2001
}
1883
2002
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
+
1884
2013
/**
1885
2014
* Holds if data can flow from `node1` to `node2` via an assignment to
1886
2015
* content `c`.
@@ -1918,6 +2047,14 @@ predicate storeStep(Node node1, ContentSet c, Node node2) {
1918
2047
c = getResultContent ( )
1919
2048
)
1920
2049
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
1921
2058
FlowSummaryImpl:: Private:: Steps:: summaryStoreStep ( node1 .( FlowSummaryNode ) .getSummaryNode ( ) , c ,
1922
2059
node2 .( FlowSummaryNode ) .getSummaryNode ( ) )
1923
2060
}
@@ -2010,6 +2147,12 @@ predicate readStep(Node node1, ContentSet c, Node node2) {
2010
2147
node2 .asExpr ( ) .( AwaitExpr ) .getExpr ( ) = node1 .asExpr ( ) and
2011
2148
c = getResultContent ( )
2012
2149
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
2013
2156
// node1 = (..., node2, ...)
2014
2157
// node1.ItemX flows to node2
2015
2158
exists ( TupleExpr te , int i , Expr item |
@@ -2072,6 +2215,10 @@ predicate clearsContent(Node n, ContentSet c) {
2072
2215
c .( FieldContent ) .getField ( ) = f .getUnboundDeclaration ( ) and
2073
2216
not f .isRef ( )
2074
2217
)
2218
+ or
2219
+ exists ( Node n1 |
2220
+ primaryConstructorParameterStore ( _, c , n1 ) and n = n1 .( PostUpdateNode ) .getPreUpdateNode ( )
2221
+ )
2075
2222
}
2076
2223
2077
2224
/**
@@ -2361,6 +2508,32 @@ module PostUpdateNodes {
2361
2508
2362
2509
override Node getPreUpdateNode ( ) { result .( FlowSummaryNode ) .getSummaryNode ( ) = preUpdateNode }
2363
2510
}
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
+ }
2364
2537
}
2365
2538
2366
2539
private import PostUpdateNodes
@@ -2537,6 +2710,11 @@ class ContentApprox extends TContentApprox {
2537
2710
this = TElementApproxContent ( ) and result = "element"
2538
2711
or
2539
2712
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
+ )
2540
2718
}
2541
2719
}
2542
2720
@@ -2550,6 +2728,14 @@ private string approximatePropertyContent(PropertyContent pc) {
2550
2728
result = pc .getProperty ( ) .getName ( ) .prefix ( 1 )
2551
2729
}
2552
2730
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
+
2553
2739
/** Gets an approximated value for content `c`. */
2554
2740
pragma [ nomagic]
2555
2741
ContentApprox getContentApprox ( Content c ) {
@@ -2560,6 +2746,9 @@ ContentApprox getContentApprox(Content c) {
2560
2746
c instanceof ElementContent and result = TElementApproxContent ( )
2561
2747
or
2562
2748
c instanceof SyntheticFieldContent and result = TSyntheticFieldApproxContent ( )
2749
+ or
2750
+ result =
2751
+ TPrimaryConstructorParameterApproxContent ( approximatePrimaryConstructorParameterContent ( c ) )
2563
2752
}
2564
2753
2565
2754
/**
0 commit comments