Skip to content

Commit 69c0f0c

Browse files
committed
C#: Address review comments.
1 parent 68b920f commit 69c0f0c

File tree

4 files changed

+69
-44
lines changed

4 files changed

+69
-44
lines changed

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

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -370,15 +370,6 @@ class Constructor extends DotNet::Constructor, Callable, Member, Attributable, @
370370
predicate isParameterless() { this.getNumberOfParameters() = 0 }
371371

372372
override string getUndecoratedName() { result = ".ctor" }
373-
374-
/**
375-
* Holds if this a primary constructor in source code.
376-
*/
377-
predicate isPrimary() {
378-
not this.hasBody() and
379-
this.getDeclaringType().fromSource() and
380-
this.fromSource()
381-
}
382373
}
383374

384375
/**
@@ -424,7 +415,11 @@ class InstanceConstructor extends Constructor {
424415
* ```
425416
*/
426417
class PrimaryConstructor extends Constructor {
427-
PrimaryConstructor() { this.isPrimary() }
418+
PrimaryConstructor() {
419+
not this.hasBody() and
420+
this.getDeclaringType().fromSource() and
421+
this.fromSource()
422+
}
428423

429424
override string getAPrimaryQlClass() { result = "PrimaryConstructor" }
430425
}

csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll

Lines changed: 63 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -39,17 +39,15 @@ predicate isArgumentNode(ArgumentNode arg, DataFlowCall c, ArgumentPosition pos)
3939
}
4040

4141
/**
42-
* Gets the control flow node used for data flow purposes for the primary constructor
42+
* Gets a control flow node used for data flow purposes for the primary constructor
4343
* parameter access `pa`.
4444
*/
45-
private ControlFlow::Node getPrimaryConstructorParameterCfn(ParameterAccess pa) {
45+
private ControlFlow::Node getAPrimaryConstructorParameterCfn(ParameterAccess pa) {
4646
pa.getTarget().getCallable() instanceof PrimaryConstructor and
4747
(
48-
pa instanceof ParameterRead and
49-
result = pa.getAControlFlowNode()
48+
result = pa.(ParameterRead).getAControlFlowNode()
5049
or
51-
pa instanceof ParameterWrite and
52-
exists(AssignExpr ae | pa = ae.getLValue() and result = ae.getAControlFlowNode())
50+
pa = any(AssignableDefinition def | result = def.getAControlFlowNode()).getTargetAccess()
5351
)
5452
}
5553

@@ -141,9 +139,10 @@ private module ThisFlow {
141139
or
142140
n.asExprAtNode(cfn) = any(Expr e | e instanceof ThisAccess or e instanceof BaseAccess)
143141
or
144-
exists(InstanceParameterAccessNode pan | pan = n |
145-
pan.getUnderlyingControlFlowNode() = cfn and pan.isPreUpdate()
146-
)
142+
n =
143+
any(InstanceParameterAccessNode pan |
144+
pan.getUnderlyingControlFlowNode() = cfn and pan.isPreUpdate()
145+
)
147146
}
148147

149148
private predicate thisAccess(Node n, BasicBlock bb, int i) {
@@ -230,7 +229,7 @@ CIL::DataFlowNode asCilDataFlowNode(Node node) {
230229

231230
/** Provides predicates related to local data flow. */
232231
module LocalFlow {
233-
private class LocalExprStepConfiguration extends ControlFlowReachabilityConfiguration {
232+
class LocalExprStepConfiguration extends ControlFlowReachabilityConfiguration {
234233
LocalExprStepConfiguration() { this = "LocalExprStepConfiguration" }
235234

236235
override predicate candidate(
@@ -955,7 +954,7 @@ private module Cached {
955954
} or
956955
TFlowInsensitiveFieldNode(FieldOrProperty f) { f.isFieldLike() } or
957956
TInstanceParameterAccessNode(ControlFlow::Node cfn, boolean isPostUpdate) {
958-
exists(ParameterAccess pa | cfn = getPrimaryConstructorParameterCfn(pa) |
957+
exists(ParameterAccess pa | cfn = getAPrimaryConstructorParameterCfn(pa) |
959958
isPostUpdate = false
960959
or
961960
pa instanceof ParameterWrite and isPostUpdate = true
@@ -1795,6 +1794,19 @@ class FlowSummaryNode extends NodeImpl, TFlowSummaryNode {
17951794

17961795
/**
17971796
* A data-flow node used to model reading and writing of primary constructor parameters.
1797+
* For example, in
1798+
* ```csharp
1799+
* public class C(object o)
1800+
* {
1801+
* public object GetParam() => o; // (1)
1802+
*
1803+
* public void SetParam(object value) => o = value; // (2)
1804+
* }
1805+
* ```
1806+
* the first access to o (1) is modeled as this.o_backing_field and
1807+
* the second access to o (2) is modeled as this.o_backing_field = value.
1808+
* Both models need a pre-update this node, and the latter need an additional post-update this access,
1809+
* all of which are represented by an InstanceParameterAccessNode node.
17981810
*/
17991811
class InstanceParameterAccessNode extends NodeImpl, TInstanceParameterAccessNode {
18001812
private ControlFlow::Node cfn;
@@ -1803,7 +1815,7 @@ class InstanceParameterAccessNode extends NodeImpl, TInstanceParameterAccessNode
18031815

18041816
InstanceParameterAccessNode() {
18051817
this = TInstanceParameterAccessNode(cfn, isPostUpdate) and
1806-
exists(ParameterAccess pa | cfn = getPrimaryConstructorParameterCfn(pa) and pa.getTarget() = p)
1818+
exists(ParameterAccess pa | cfn = getAPrimaryConstructorParameterCfn(pa) and pa.getTarget() = p)
18071819
}
18081820

18091821
override DataFlowCallable getEnclosingCallableImpl() {
@@ -1836,6 +1848,16 @@ class InstanceParameterAccessNode extends NodeImpl, TInstanceParameterAccessNode
18361848

18371849
/**
18381850
* A data-flow node used to synthesize the body of a primary constructor.
1851+
*
1852+
* For example, in
1853+
* ```csharp
1854+
* public class C(object o) { }
1855+
* ```
1856+
* we synthesize the primary constructor for C as
1857+
* ```csharp
1858+
* public C(object o) => this.o_backing_field = o;
1859+
* ```
1860+
* The synthesized (pre/post-update) this access is represented an PrimaryConstructorThisAccessNode node.
18391861
*/
18401862
class PrimaryConstructorThisAccessNode extends NodeImpl, TPrimaryConstructorThisAccessNode {
18411863
private Parameter p;
@@ -2000,13 +2022,25 @@ private PropertyContent getResultContent() {
20002022
result.getProperty() = any(SystemThreadingTasksTaskTClass c_).getResultProperty()
20012023
}
20022024

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()
2025+
private predicate primaryConstructorParameterStore(
2026+
Node node1, PrimaryConstructorParameterContent c, Node node2
2027+
) {
2028+
exists(ControlFlow::Node cfn, Parameter p |
2029+
node2 = TInstanceParameterAccessNode(cfn, true) and
2030+
c.getParameter() = p
2031+
|
2032+
// direct assignment
2033+
exists(LocalFlow::LocalExprStepConfiguration conf, AssignableDefinition def |
2034+
conf.hasDefPath(_, node1.(ExprNode).getControlFlowNode(), def, cfn) and
2035+
p = def.getTarget()
2036+
)
2037+
or
2038+
// indirect assignment (for example as an `out` argument)
2039+
exists(Ssa::ExplicitDefinition def |
2040+
def = node1.(SsaDefinitionExtNode).getDefinitionExt() and
2041+
p = def.getSourceVariable().getAssignable() and
2042+
cfn = def.getControlFlowNode()
2043+
)
20102044
)
20112045
}
20122046

@@ -2147,10 +2181,12 @@ predicate readStep(Node node1, ContentSet c, Node node2) {
21472181
node2.asExpr().(AwaitExpr).getExpr() = node1.asExpr() and
21482182
c = getResultContent()
21492183
or
2150-
exists(InstanceParameterAccessNode n | n = node1 |
2151-
n.getUnderlyingControlFlowNode() = node2.(ExprNode).getControlFlowNode() and
2152-
n.getParameter() = c.(PrimaryConstructorParameterContent).getParameter()
2153-
) and
2184+
node1 =
2185+
any(InstanceParameterAccessNode n |
2186+
n.getUnderlyingControlFlowNode() = node2.(ExprNode).getControlFlowNode() and
2187+
n.getParameter() = c.(PrimaryConstructorParameterContent).getParameter() and
2188+
n.isPreUpdate()
2189+
) and
21542190
node2.asExpr() instanceof ParameterRead
21552191
or
21562192
// node1 = (..., node2, ...)
@@ -2216,9 +2252,7 @@ predicate clearsContent(Node n, ContentSet c) {
22162252
not f.isRef()
22172253
)
22182254
or
2219-
exists(Node n1 |
2220-
primaryConstructorParameterStore(_, c, n1) and n = n1.(PostUpdateNode).getPreUpdateNode()
2221-
)
2255+
n = any(PostUpdateNode n1 | primaryConstructorParameterStore(_, c, n1)).getPreUpdateNode()
22222256
}
22232257

22242258
/**
@@ -2512,11 +2546,11 @@ module PostUpdateNodes {
25122546
private class InstanceParameterAccessPostUpdateNode extends PostUpdateNode,
25132547
InstanceParameterAccessNode
25142548
{
2515-
private ControlFlow::Node cfg;
2549+
private ControlFlow::Node cfn;
25162550

2517-
InstanceParameterAccessPostUpdateNode() { this = TInstanceParameterAccessNode(cfg, true) }
2551+
InstanceParameterAccessPostUpdateNode() { this = TInstanceParameterAccessNode(cfn, true) }
25182552

2519-
override Node getPreUpdateNode() { result = TInstanceParameterAccessNode(cfg, false) }
2553+
override Node getPreUpdateNode() { result = TInstanceParameterAccessNode(cfn, false) }
25202554

25212555
override string toStringImpl() { result = "[post] this" }
25222556
}

csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPublic.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,7 @@ class PrimaryConstructorParameterContent extends Content, TPrimaryConstructorPar
251251
/** Gets the underlying parameter. */
252252
Parameter getParameter() { result = p }
253253

254-
override string toString() { result = "parameter field " + p.getName() }
254+
override string toString() { result = "parameter " + p.getName() }
255255

256256
override Location getLocation() { result = p.getLocation() }
257257
}

csharp/ql/test/library-tests/constructors/PrimaryConstructor.ql

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,3 @@
1-
/**
2-
* @name Test for primary constructors
3-
*/
4-
51
import csharp
62

73
from PrimaryConstructor c

0 commit comments

Comments
 (0)