Skip to content

Commit 189a713

Browse files
committed
C#: Prototype breaking pattern assignments into multiple steps.
1 parent 990ac0f commit 189a713

File tree

4 files changed

+49
-26
lines changed

4 files changed

+49
-26
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/Nullness.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ private predicate nonNullDef(Ssa::ExplicitDefinition def) {
104104
def.getADefinition().getSource() instanceof NonNullExpr
105105
or
106106
exists(AssignableDefinition ad | ad = def.getADefinition() |
107-
ad instanceof AssignableDefinitions::PatternDefinition
107+
ad.(AssignableDefinitions::PatternDefinition).isTopLevel()
108108
or
109109
ad =
110110
any(AssignableDefinitions::LocalVariableDefinition d |

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

Lines changed: 19 additions & 14 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,25 +624,28 @@ 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
)
639641
or
640642
// Needed for read steps for pattern matching involving properties.
641643
scope = def.getExpr() and
642644
e.(VariablePatternExpr).getVariableDeclExpr() =
643-
def.(AssignableDefinitions::LocalVariableDefinition).getExpr() and
645+
def.(AssignableDefinitions::PatternDefinition).getExpr() and
644646
exactScope = false and
645-
isSuccessor = false
647+
isSuccessor = false and
648+
not def.(AssignableDefinitions::PatternDefinition).isTopLevel()
646649
}
647650
}
648651

@@ -2955,8 +2958,10 @@ class CastNode extends Node {
29552958
CastNode() {
29562959
this.asExpr() instanceof Cast
29572960
or
2958-
this.(AssignableDefinitionNode).getDefinition() instanceof
2959-
AssignableDefinitions::PatternDefinition
2961+
this.(AssignableDefinitionNode)
2962+
.getDefinition()
2963+
.(AssignableDefinitions::PatternDefinition)
2964+
.isTopLevel()
29602965
}
29612966
}
29622967

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)