diff --git a/csharp/ql/lib/semmle/code/csharp/Assignable.qll b/csharp/ql/lib/semmle/code/csharp/Assignable.qll index d98d78331c83..b4014f09f5ce 100644 --- a/csharp/ql/lib/semmle/code/csharp/Assignable.qll +++ b/csharp/ql/lib/semmle/code/csharp/Assignable.qll @@ -277,15 +277,28 @@ module AssignableInternal { def = TParameterDefaultDefinition(_, result) } - /** A local variable declaration at the top-level of a pattern. */ - class TopLevelPatternDecl extends LocalVariableDeclExpr { + /** A pattern containing a local variable declaration. */ + class LocalVariablePatternDecl extends LocalVariableDeclExpr { private PatternMatch pm; - TopLevelPatternDecl() { this = pm.getPattern().(BindingPatternExpr).getVariableDeclExpr() } + LocalVariablePatternDecl() { + exists(BindingPatternExpr bpe | + this = bpe.getVariableDeclExpr() and pm = bpe.getPatternMatch() + ) + } + + /** Holds if the local variable definition is at the top level of the pattern. */ + predicate isTopLevel() { this = pm.getPattern().(BindingPatternExpr).getVariableDeclExpr() } + /** Gets the pattern match that this local variable declaration (pattern) belongs to. */ PatternMatch getMatch() { result = pm } } + /** A local variable declaration at the top-level of a pattern. */ + class TopLevelPatternDecl extends LocalVariablePatternDecl { + TopLevelPatternDecl() { this.isTopLevel() } + } + cached private module Cached { cached @@ -305,7 +318,7 @@ module AssignableInternal { TLocalVariableDefinition(LocalVariableDeclExpr lvde) { not lvde.hasInitializer() and not exists(getTupleSource(TTupleAssignmentDefinition(_, lvde))) and - not lvde instanceof TopLevelPatternDecl and + not lvde instanceof LocalVariablePatternDecl and not lvde.isOutArgument() } or TImplicitParameterDefinition(Parameter p) { @@ -324,7 +337,7 @@ module AssignableInternal { default = p.getDefaultValue() } or TAddressOfDefinition(AddressOfExpr aoe) or - TPatternDefinition(TopLevelPatternDecl tlpd) or + TPatternDefinition(LocalVariablePatternDecl lvpd) or TAssignOperationDefinition(AssignOperation ao) { ao instanceof AssignCallExpr and not ao instanceof CompoundAssignmentOperatorCall or @@ -737,24 +750,31 @@ module AssignableDefinitions { } /** - * A local variable definition in a pattern, for example `x is int i`. + * A local variable definition in a pattern, for example `int i` in `x is int i`. */ class PatternDefinition extends AssignableDefinition, TPatternDefinition { - TopLevelPatternDecl tlpd; + LocalVariablePatternDecl lvpd; - PatternDefinition() { this = TPatternDefinition(tlpd) } + PatternDefinition() { this = TPatternDefinition(lvpd) } /** Gets the element matches against this pattern. */ - PatternMatch getMatch() { result = tlpd.getMatch() } + PatternMatch getMatch() { result = lvpd.getMatch() } /** Gets the underlying local variable declaration. */ - LocalVariableDeclExpr getDeclaration() { result = tlpd } - - override Expr getSource() { result = this.getMatch().getExpr() } + LocalVariableDeclExpr getDeclaration() { result = lvpd } override string toString() { result = this.getDeclaration().toString() } } + /** + * A local variable definition at the top level of a pattern. + */ + class TopLevelPatternDefinition extends PatternDefinition { + TopLevelPatternDefinition() { lvpd.isTopLevel() } + + override Expr getSource() { result = this.getMatch().getExpr() } + } + /** * An initializer definition for a field or a property, for example * line 2 in diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/Nullness.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/Nullness.qll index be2ed39a7d45..4b57930bc378 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/Nullness.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/Nullness.qll @@ -110,7 +110,7 @@ private predicate nonNullDef(SsaExplicitWrite def) { def.getValue() instanceof NonNullExpr or exists(AssignableDefinition ad | ad = def.getDefinition() | - ad instanceof AssignableDefinitions::PatternDefinition + ad instanceof AssignableDefinitions::TopLevelPatternDefinition or ad = any(AssignableDefinitions::LocalVariableDefinition d | diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll index 8b86347ce460..b4b1bb5e1e0d 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll @@ -560,7 +560,7 @@ module LocalFlow { or exists(AssignExpr ae | ae.getLeftOperand().(TupleExpr) = e2 and ae.getRightOperand() = e1) or - exists(ControlFlowElement cfe | cfe = e2.(TupleExpr).(PatternExpr).getPatternMatch() | + exists(ControlFlowElement cfe | cfe = e2.(TuplePatternExpr).getPatternMatch() | cfe.(IsExpr).getExpr() = e1 or exists(Switch sw | sw.getACase() = cfe and sw.getExpr() = e1) @@ -2242,8 +2242,8 @@ private predicate readContentStep(Node node1, Content c, Node node2) { ) or // item = variable in node1 = (..., variable, ...) in a case/is var (..., ...) - isPatternExprDescendant(te) and - exists(AssignableDefinitions::LocalVariableDefinition lvd | + te instanceof TuplePatternExpr and + exists(AssignableDefinitions::PatternDefinition lvd | node2.(AssignableDefinitionNode).getDefinition() = lvd and lvd.getDeclaration() = item ) @@ -2677,7 +2677,7 @@ class CastNode extends Node { this.asExpr() instanceof Cast or this.(AssignableDefinitionNode).getDefinition() instanceof - AssignableDefinitions::PatternDefinition + AssignableDefinitions::TopLevelPatternDefinition } } diff --git a/csharp/ql/lib/semmle/code/csharp/exprs/Expr.qll b/csharp/ql/lib/semmle/code/csharp/exprs/Expr.qll index c8648741d011..53e6d359c212 100644 --- a/csharp/ql/lib/semmle/code/csharp/exprs/Expr.qll +++ b/csharp/ql/lib/semmle/code/csharp/exprs/Expr.qll @@ -547,6 +547,13 @@ class PositionalPatternExpr extends PatternExpr, @positional_pattern_expr { override string getAPrimaryQlClass() { result = "PositionalPatternExpr" } } +/** + * A tuple pattern. For example, `var (x, y)`. + */ +class TuplePatternExpr extends TupleExpr, PatternExpr { + override string getAPrimaryQlClass() { result = "TuplePatternExpr" } +} + /** A list pattern. For example `[1, 2, int y]` in `x is [1, 2, int y]`. */ class ListPatternExpr extends PatternExpr, @list_pattern_expr { override string toString() { result = "[ ... ]" } diff --git a/csharp/ql/src/Dead Code/DeadStoreOfLocal.ql b/csharp/ql/src/Dead Code/DeadStoreOfLocal.ql index af6aa286ef07..dc1647f14f83 100644 --- a/csharp/ql/src/Dead Code/DeadStoreOfLocal.ql +++ b/csharp/ql/src/Dead Code/DeadStoreOfLocal.ql @@ -35,7 +35,7 @@ class RelevantDefinition extends AssignableDefinition { not lvde.getName() = "_" ) or - this instanceof AssignableDefinitions::PatternDefinition + this instanceof AssignableDefinitions::TopLevelPatternDefinition or this instanceof AssignableDefinitions::AssignOperationDefinition } diff --git a/csharp/ql/test/library-tests/csharp8/PrintAst.expected b/csharp/ql/test/library-tests/csharp8/PrintAst.expected index 4efcaad323ac..b2471d3ca201 100644 --- a/csharp/ql/test/library-tests/csharp8/PrintAst.expected +++ b/csharp/ql/test/library-tests/csharp8/PrintAst.expected @@ -929,7 +929,7 @@ patterns.cs: # 57| 1: [ConstantPatternExpr,IntLiteral] 2 # 58| 10: [BreakStmt] break; # 59| 11: [CaseStmt] case ...: -# 59| 0: [TupleExpr] (..., ...) +# 59| 0: [TuplePatternExpr] (..., ...) # 59| 0: [VariablePatternExpr] Int32 x # 59| 1: [VariablePatternExpr] Int32 y # 60| 12: [BreakStmt] break; @@ -1156,7 +1156,7 @@ patterns.cs: # 130| 1: [ConstantPatternExpr,IntLiteral] 2 # 130| 2: [IntLiteral] 2 # 131| 3: [SwitchCaseExpr] ... => ... -# 131| 0: [TupleExpr] (..., ...) +# 131| 0: [TuplePatternExpr] (..., ...) # 131| 0: [VariablePatternExpr] Int32 x # 131| 1: [DiscardPatternExpr] _ # 131| 2: [IntLiteral] 3 diff --git a/csharp/ql/test/library-tests/dataflow/tuples/PrintAst.expected b/csharp/ql/test/library-tests/dataflow/tuples/PrintAst.expected index 11e14eeb6c59..c7416e3c874e 100644 --- a/csharp/ql/test/library-tests/dataflow/tuples/PrintAst.expected +++ b/csharp/ql/test/library-tests/dataflow/tuples/PrintAst.expected @@ -262,9 +262,9 @@ Tuples.cs: # 65| -1: [LocalVariableAccess] access to local variable t # 66| 4: [BreakStmt] break; # 67| 5: [CaseStmt] case ...: -# 67| 0: [TupleExpr] (..., ...) +# 67| 0: [TuplePatternExpr] (..., ...) # 67| 0: [VariablePatternExpr] String a -# 67| 1: [TupleExpr] (..., ...) +# 67| 1: [TuplePatternExpr] (..., ...) # 67| 0: [VariablePatternExpr] Int32 b # 67| 1: [VariablePatternExpr] String c # 67| 2: [DiscardPatternExpr] _ @@ -302,7 +302,7 @@ Tuples.cs: # 78| 0: [RecursivePatternExpr] { ... } # 78| 2: [PositionalPatternExpr] ( ... ) # 78| 0: [VariablePatternExpr] String a -# 78| 1: [TupleExpr] (..., ...) +# 78| 1: [TuplePatternExpr] (..., ...) # 78| 0: [VariablePatternExpr] Int32 b # 78| 1: [VariablePatternExpr] String c # 78| 2: [DiscardPatternExpr] _ @@ -335,9 +335,9 @@ Tuples.cs: # 87| 7: [IfStmt] if (...) ... # 87| 0: [IsExpr] ... is ... # 87| 0: [LocalVariableAccess] access to local variable x -# 87| 1: [TupleExpr] (..., ...) +# 87| 1: [TuplePatternExpr] (..., ...) # 87| 0: [VariablePatternExpr] String p -# 87| 1: [TupleExpr] (..., ...) +# 87| 1: [TuplePatternExpr] (..., ...) # 87| 0: [VariablePatternExpr] Int32 q # 87| 1: [VariablePatternExpr] String r # 87| 2: [DiscardPatternExpr] _ @@ -417,7 +417,7 @@ Tuples.cs: # 107| 6: [SwitchStmt] switch (...) {...} # 107| 0: [LocalVariableAccess] access to local variable r # 109| 0: [CaseStmt] case ...: -# 109| 0: [TupleExpr] (..., ...) +# 109| 0: [TuplePatternExpr] (..., ...) # 109| 0: [VariablePatternExpr] String x # 109| 1: [VariablePatternExpr] Int32 y # 110| 1: [ExprStmt] ...;