Skip to content

Commit 5591e48

Browse files
committed
C#: Basic pattern match data flow support.
1 parent c537246 commit 5591e48

File tree

3 files changed

+91
-27
lines changed

3 files changed

+91
-27
lines changed

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

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -273,12 +273,20 @@ module AssignableInternal {
273273
def = TPatternDefinition(result)
274274
}
275275

276-
/** A local variable declaration at the top-level of a pattern. */
277-
class TopLevelPatternDecl extends LocalVariableDeclExpr {
276+
/** A pattern containing a local variable declaration. */
277+
class LocalVariablePatternDecl extends LocalVariableDeclExpr {
278278
private PatternMatch pm;
279279

280-
TopLevelPatternDecl() { this = pm.getPattern().(BindingPatternExpr).getVariableDeclExpr() }
280+
LocalVariablePatternDecl() {
281+
exists(BindingPatternExpr bpe |
282+
this = bpe.getVariableDeclExpr() and pm = bpe.getPatternMatch()
283+
)
284+
}
281285

286+
/** Holds if the local variable definition is at the top level of the pattern. */
287+
predicate isTopLevel() { this = pm.getPattern().(BindingPatternExpr).getVariableDeclExpr() }
288+
289+
/** Gets the pattern match that this local variable declaration (pattern) belongs to. */
282290
PatternMatch getMatch() { result = pm }
283291
}
284292

@@ -297,7 +305,7 @@ module AssignableInternal {
297305
TLocalVariableDefinition(LocalVariableDeclExpr lvde) {
298306
not lvde.hasInitializer() and
299307
not exists(getTupleSource(TTupleAssignmentDefinition(_, lvde))) and
300-
not lvde instanceof TopLevelPatternDecl and
308+
not lvde instanceof LocalVariablePatternDecl and
301309
not lvde.isOutArgument()
302310
} or
303311
TImplicitParameterDefinition(Parameter p) {
@@ -309,7 +317,7 @@ module AssignableInternal {
309317
)
310318
} or
311319
TAddressOfDefinition(AddressOfExpr aoe) or
312-
TPatternDefinition(TopLevelPatternDecl tlpd)
320+
TPatternDefinition(LocalVariablePatternDecl lvpd)
313321

314322
/**
315323
* Gets the source expression assigned in tuple definition `def`, if any.
@@ -699,22 +707,25 @@ module AssignableDefinitions {
699707
}
700708

701709
/**
702-
* A local variable definition in a pattern, for example `x is int i`.
710+
* A local variable definition in a pattern, for example `int i` in `x is int i`.
703711
*/
704712
class PatternDefinition extends AssignableDefinition, TPatternDefinition {
705-
TopLevelPatternDecl tlpd;
713+
LocalVariablePatternDecl lvpd;
706714

707-
PatternDefinition() { this = TPatternDefinition(tlpd) }
715+
PatternDefinition() { this = TPatternDefinition(lvpd) }
708716

709717
/** Gets the element matches against this pattern. */
710-
PatternMatch getMatch() { result = tlpd.getMatch() }
718+
PatternMatch getMatch() { result = lvpd.getMatch() }
711719

712720
/** Gets the underlying local variable declaration. */
713-
LocalVariableDeclExpr getDeclaration() { result = tlpd }
721+
LocalVariableDeclExpr getDeclaration() { result = lvpd }
714722

715-
override Expr getSource() { result = this.getMatch().getExpr() }
723+
override Expr getSource() { this.isTopLevel() and result = this.getMatch().getExpr() }
716724

717725
override string toString() { result = this.getDeclaration().toString() }
726+
727+
/** Holds if the local variable definition is at the top level of the pattern. */
728+
predicate isTopLevel() { lvpd.isTopLevel() }
718729
}
719730

720731
/**

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

Lines changed: 62 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -605,7 +605,7 @@ module LocalFlow {
605605
isSuccessor = false
606606
or
607607
isSuccessor = true and
608-
exists(ControlFlowElement cfe | cfe = e2.(TupleExpr).(PatternExpr).getPatternMatch() |
608+
exists(ControlFlowElement cfe | cfe = e2.(TuplePatternExpr).getPatternMatch() |
609609
cfe.(IsExpr).getExpr() = e1 and scope = cfe
610610
or
611611
exists(Switch sw | sw.getACase() = cfe and sw.getExpr() = e1 and scope = sw)
@@ -624,18 +624,29 @@ module LocalFlow {
624624
scope = def.getExpr() and
625625
isSuccessor = true
626626
or
627-
scope = def.(AssignableDefinitions::PatternDefinition).getMatch().(IsExpr) and
628-
isSuccessor = false
629-
or
630-
exists(Switch s |
631-
s.getACase() = def.(AssignableDefinitions::PatternDefinition).getMatch() and
632-
isSuccessor = true
633-
|
634-
scope = s.getExpr()
627+
exists(AssignableDefinitions::PatternDefinition def0 | def = def0 and def0.isTopLevel() |
628+
scope = def0.getMatch().(IsExpr) and
629+
isSuccessor = false
635630
or
636-
scope = s.getACase()
631+
exists(Switch s |
632+
s.getACase() = def0.getMatch() and
633+
isSuccessor = true
634+
|
635+
scope = s.getExpr()
636+
or
637+
scope = s.getACase()
638+
)
637639
)
638640
)
641+
or
642+
// Needed for read steps for pattern matching involving properties.
643+
scope = def.getExpr() and
644+
exactScope = false and
645+
isSuccessor = false and
646+
def =
647+
any(AssignableDefinitions::PatternDefinition apd |
648+
e = apd.getDeclaration() and not apd.isTopLevel()
649+
)
639650
}
640651
}
641652

@@ -896,6 +907,30 @@ private predicate fieldOrPropertyStore(Expr e, ContentSet c, Expr src, Expr q, b
896907
)
897908
}
898909

910+
private predicate patternPropertyRead0(RecursivePatternExpr rpe, ContentSet c, VariablePatternExpr e) {
911+
exists(TypeAccess ta, Property prop |
912+
ta = rpe.getTypeAccess() and
913+
e = rpe.getPropertyPatterns().getPattern(_) and
914+
prop.getDeclaringType() = ta.getType() and
915+
prop.getName() = e.(LabeledPatternExpr).getLabel() and
916+
c.isProperty(prop)
917+
)
918+
}
919+
920+
private predicate patternPropertyRead(Expr e1, ContentSet c, VariablePatternExpr e2) {
921+
exists(IsExpr ie, RecursivePatternExpr rpe |
922+
e1 = ie.getExpr() and
923+
rpe = ie.getPattern() and
924+
patternPropertyRead0(rpe, c, e2)
925+
)
926+
or
927+
exists(Switch sw, RecursivePatternExpr rpe |
928+
e1 = sw.getExpr() and
929+
rpe = sw.getACase().getPattern() and
930+
patternPropertyRead0(rpe, c, e2)
931+
)
932+
}
933+
899934
/**
900935
* Holds if `e2` is an expression that reads field or property `c` from
901936
* expression `e1`.
@@ -1124,6 +1159,8 @@ private module Cached {
11241159
or
11251160
dynamicPropertyRead(e, _, read)
11261161
or
1162+
patternPropertyRead(e, _, read)
1163+
or
11271164
arrayRead(e, read)
11281165
)
11291166
)
@@ -2415,6 +2452,11 @@ private class ReadStepConfiguration extends ControlFlowReachabilityConfiguration
24152452
e2 = e1.(TupleExpr).getAnArgument() and
24162453
scope = e1 and
24172454
isSuccessor = false
2455+
or
2456+
exactScope = false and
2457+
isSuccessor = true and
2458+
patternPropertyRead(e1, _, e2) and
2459+
scope = e1
24182460
}
24192461

24202462
override predicate candidateDef(
@@ -2446,8 +2488,8 @@ private class ReadStepConfiguration extends ControlFlowReachabilityConfiguration
24462488
)
24472489
or
24482490
scope =
2449-
any(TupleExpr te |
2450-
te.getAnArgument() = defTo.(AssignableDefinitions::LocalVariableDefinition).getDeclaration() and
2491+
any(TuplePatternExpr te |
2492+
te.getAnArgument() = defTo.(AssignableDefinitions::PatternDefinition).getDeclaration() and
24512493
e = te and
24522494
exactScope = false and
24532495
isSuccessor = false
@@ -2496,8 +2538,8 @@ private predicate readContentStep(Node node1, Content c, Node node2) {
24962538
)
24972539
or
24982540
// item = variable in node1 = (..., variable, ...) in a case/is var (..., ...)
2499-
te = any(PatternExpr pe).getAChildExpr*() and
2500-
exists(AssignableDefinitions::LocalVariableDefinition lvd |
2541+
te = any(TuplePatternExpr pe).getAChildExpr*() and
2542+
exists(AssignableDefinitions::PatternDefinition lvd |
25012543
node2.(AssignableDefinitionNode).getDefinition() = lvd and
25022544
lvd.getDeclaration() = item and
25032545
hasNodePath(x, node1, node2)
@@ -2537,6 +2579,8 @@ predicate readStep(Node node1, ContentSet c, Node node2) {
25372579
or
25382580
dynamicPropertyRead(node1.asExpr(), c, node2.asExpr())
25392581
or
2582+
patternPropertyRead(node1.asExpr(), c, node2.asExpr())
2583+
or
25402584
node2.asExpr().(AwaitExpr).getExpr() = node1.asExpr() and
25412585
c = getResultContent()
25422586
)
@@ -2927,8 +2971,10 @@ class CastNode extends Node {
29272971
CastNode() {
29282972
this.asExpr() instanceof Cast
29292973
or
2930-
this.(AssignableDefinitionNode).getDefinition() instanceof
2931-
AssignableDefinitions::PatternDefinition
2974+
this.(AssignableDefinitionNode)
2975+
.getDefinition()
2976+
.(AssignableDefinitions::PatternDefinition)
2977+
.isTopLevel()
29322978
}
29332979
}
29342980

csharp/ql/lib/semmle/code/csharp/exprs/Expr.qll

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -531,6 +531,13 @@ class LabeledPatternExpr extends PatternExpr {
531531
override string getAPrimaryQlClass() { result = "LabeledPatternExpr" }
532532
}
533533

534+
/**
535+
* A tuple pattern. For example, `var (x, y)`.
536+
*/
537+
class TuplePatternExpr extends TupleExpr, PatternExpr {
538+
override string getAPrimaryQlClass() { result = "TuplePatternExpr" }
539+
}
540+
534541
/** A positional pattern. For example, `(int x, int y)`. */
535542
class PositionalPatternExpr extends PatternExpr, @positional_pattern_expr {
536543
override string toString() { result = "( ... )" }

0 commit comments

Comments
 (0)