Skip to content

Commit 81a1deb

Browse files
authored
Merge pull request #22757 from michaelnebel/csharp/propertypatternflow
C#: Data flow for property patterns.
2 parents 22bb94e + d5209cc commit 81a1deb

4 files changed

Lines changed: 461 additions & 17 deletions

File tree

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: minorAnalysis
3+
---
4+
* Added support for data flow through property patterns in `is` expressions and `switch` cases.

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

Lines changed: 47 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1990,16 +1990,22 @@ class FieldOrProperty extends Assignable, Modifiable {
19901990
}
19911991
}
19921992

1993-
/** A string that is a reference to a late-bound target of a dynamic member access. */
1993+
/** A member name used by a dynamic access or a property pattern without type information. */
19941994
class DynamicProperty extends string {
1995-
private DynamicMemberAccess dma;
1996-
1997-
DynamicProperty() { this = dma.getLateBoundTargetName() }
1995+
DynamicProperty() {
1996+
this = any(DynamicMemberAccess dma).getLateBoundTargetName()
1997+
or
1998+
exists(PatternExpr qualifier, LabeledPatternExpr label |
1999+
propertyPatternQualifier(qualifier, label) and
2000+
qualifier.getType() instanceof NullType and
2001+
this = label.getLabel()
2002+
)
2003+
}
19982004

19992005
ContentSet getContentSet() { result.isDynamicProperty(this) }
20002006

20012007
/** Gets an access of this dynamic property. */
2002-
DynamicMemberAccess getAnAccess() { result = dma }
2008+
DynamicMemberAccess getAnAccess() { result.getLateBoundTargetName() = this }
20032009
}
20042010

20052011
private class InstanceFieldOrProperty extends FieldOrProperty {
@@ -2308,6 +2314,40 @@ private predicate readStepDelegateCall(Node node1, ContentSet c, OutNode node2)
23082314
)
23092315
}
23102316

2317+
private predicate propertyPatternQualifier(PatternExpr qualifier, LabeledPatternExpr label) {
2318+
qualifier.(RecursivePatternExpr).getPropertyPatterns().getPattern(_) = label
2319+
or
2320+
qualifier instanceof LabeledPatternExpr and
2321+
qualifier.(PropertyPatternExpr).getPattern(_) = label
2322+
}
2323+
2324+
/**
2325+
* Holds if data can flow from `node1` to `node2` via a property pattern.
2326+
*/
2327+
private predicate fieldOrPropertyPatternRead(Node node1, ContentSet c, Node node2) {
2328+
exists(PatternExpr pattern, LabeledPatternExpr label |
2329+
node1.asExpr() = pattern and
2330+
propertyPatternQualifier(pattern, label) and
2331+
if pattern.getType() instanceof NullType
2332+
then c.isDynamicProperty(label.getLabel())
2333+
else
2334+
exists(FieldOrProperty f |
2335+
c = f.getContentSet() and
2336+
f.getName() = label.getLabel() and
2337+
pattern.getType().(ValueOrRefType).hasMember(f)
2338+
)
2339+
|
2340+
node2.asExpr() = label.(RecursivePatternExpr)
2341+
or
2342+
node2.asExpr() = label.(PropertyPatternExpr)
2343+
or
2344+
exists(AssignableDefinitions::PatternDefinition lvd |
2345+
node2.(AssignableDefinitionNode).getDefinition() = lvd and
2346+
label.(BindingPatternExpr).getVariableDeclExpr() = lvd.getDeclaration()
2347+
)
2348+
)
2349+
}
2350+
23112351
/**
23122352
* Holds if data can flow from `node1` to `node2` via a read of content `c`.
23132353
*/
@@ -2319,6 +2359,8 @@ predicate readStep(Node node1, ContentSet c, Node node2) {
23192359
or
23202360
fieldOrPropertyRead(node1.asExpr(), c, node2.asExpr())
23212361
or
2362+
fieldOrPropertyPatternRead(node1, c, node2)
2363+
or
23222364
dynamicPropertyRead(node1.asExpr(), c, node2.asExpr())
23232365
or
23242366
node2.asExpr().(AwaitExpr).getExpr() = node1.asExpr() and

0 commit comments

Comments
 (0)