Skip to content

Commit 927dd51

Browse files
committed
C#: Extract unary patterns
1 parent 842ed62 commit 927dd51

File tree

11 files changed

+133
-1
lines changed

11 files changed

+133
-1
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
lgtm,codescanning
2+
* The `UnaryPatternExpr` and `NotPatternExpr` classes have been added to support
3+
C# 9 unary `not` pattern.

csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Patterns/Pattern.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ internal static Expression Create(Context cx, PatternSyntax syntax, IExpressionP
2020
case TypePatternSyntax typePattern:
2121
return Expressions.TypeAccess.Create(cx, typePattern.Type, parent, child);
2222

23+
case UnaryPatternSyntax unaryPattern:
24+
return new UnaryPattern(cx, unaryPattern, parent, child);
25+
2326
case DeclarationPatternSyntax declPattern:
2427
// Creates a single local variable declaration.
2528
{
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
using Microsoft.CodeAnalysis.CSharp.Syntax;
2+
using Semmle.Extraction.Entities;
3+
using Semmle.Extraction.Kinds;
4+
5+
namespace Semmle.Extraction.CSharp.Entities.Expressions
6+
{
7+
internal class UnaryPattern : Expression
8+
{
9+
public UnaryPattern(Context cx, UnaryPatternSyntax syntax, IExpressionParentEntity parent, int child) :
10+
base(new ExpressionInfo(cx, NullType.Create(cx), cx.Create(syntax.GetLocation()), ExprKind.NOT_PATTERN, parent, child, false, null))
11+
{
12+
Pattern.Create(cx, syntax.Pattern, this, 0);
13+
}
14+
}
15+
}

csharp/extractor/Semmle.Extraction.CSharp/Kinds/ExprKind.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ public enum ExprKind
115115
SWITCH_CASE = 118,
116116
ASSIGN_COALESCE = 119,
117117
SUPPRESS_NULLABLE_WARNING = 120,
118-
NAMESPACE_ACCESS = 121
118+
NAMESPACE_ACCESS = 121,
119+
NOT_PATTERN = 126
119120
}
120121
}

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

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,12 @@ private predicate hasChildPattern(ControlFlowElement pm, Expr child) {
295295
child = mid.getChild(2).getAChildExpr() or
296296
child = mid.getChild(3).getAChildExpr()
297297
)
298+
or
299+
exists(Expr mid |
300+
hasChildPattern(pm, mid) and
301+
mid instanceof @unary_pattern_expr and
302+
child = mid.getChild(0)
303+
)
298304
}
299305

300306
/**
@@ -444,6 +450,19 @@ class PositionalPatternExpr extends Expr, @positional_pattern_expr {
444450
override string getAPrimaryQlClass() { result = "PositionalPatternExpr" }
445451
}
446452

453+
/** A unary pattern. For example, `not 1`. */
454+
class UnaryPatternExpr extends PatternExpr, @unary_pattern_expr {
455+
/** Gets the underlying pattern. */
456+
PatternExpr getPattern() { result = this.getChild(0) }
457+
}
458+
459+
/** A not pattern. For example, `not 1`. */
460+
class NotPatternExpr extends UnaryPatternExpr, @not_pattern_expr {
461+
override string toString() { result = "not ..." }
462+
463+
override string getAPrimaryQlClass() { result = "NotPatternExpr" }
464+
}
465+
447466
/**
448467
* An expression or statement that matches the value of an expression against
449468
* a pattern. Either an `is` expression or a `case` expression/statement.

csharp/ql/src/semmlecode.csharp.dbscheme

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1009,11 +1009,14 @@ case @expr.kind of
10091009
| 119 = @assign_coalesce_expr
10101010
| 120 = @suppress_nullable_warning_expr
10111011
| 121 = @namespace_access_expr
1012+
/* C# 9.0 */
1013+
| 126 = @not_pattern_expr
10121014
;
10131015

10141016
@switch = @switch_stmt | @switch_expr;
10151017
@case = @case_stmt | @switch_case_expr;
10161018
@pattern_match = @case | @is_expr;
1019+
@unary_pattern_expr = @not_pattern_expr;
10171020

10181021
@integer_literal_expr = @int_literal_expr | @long_literal_expr | @uint_literal_expr | @ulong_literal_expr;
10191022
@real_literal_expr = @float_literal_expr | @double_literal_expr | @decimal_literal_expr;

csharp/ql/test/library-tests/csharp9/PrintAst.expected

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -764,3 +764,59 @@ TypePattern.cs:
764764
# 14| 0: [VariablePatternExpr] Object o
765765
# 14| 0: [TypeMention] object
766766
# 14| 2: [LocalVariableAccess] access to local variable o
767+
UnaryPattern.cs:
768+
# 3| [Class] UnaryPattern
769+
# 5| 5: [Property] P1
770+
# 5| -1: [TypeMention] int
771+
# 5| 3: [Getter] get_P1
772+
# 5| 4: [Setter] set_P1
773+
#-----| 2: (Parameters)
774+
# 5| 0: [Parameter] value
775+
# 7| 6: [Method] M1
776+
# 7| -1: [TypeMention] bool
777+
#-----| 2: (Parameters)
778+
# 7| 0: [Parameter] c
779+
# 7| -1: [TypeMention] char
780+
# 8| 4: [IsExpr] ... is ...
781+
# 8| 0: [ParameterAccess] access to parameter c
782+
# 8| 1: [NotPatternExpr] not ...
783+
# 8| 0: [CharLiteral,ConstantPatternExpr] a
784+
# 9| 7: [Method] M2
785+
# 9| -1: [TypeMention] bool
786+
#-----| 2: (Parameters)
787+
# 9| 0: [Parameter] c
788+
# 9| -1: [TypeMention] object
789+
# 10| 4: [IsExpr] ... is ...
790+
# 10| 0: [ParameterAccess] access to parameter c
791+
# 10| 1: [NotPatternExpr] not ...
792+
# 10| 0: [ConstantPatternExpr,NullLiteral] null
793+
# 11| 8: [Method] M3
794+
# 11| -1: [TypeMention] bool
795+
#-----| 2: (Parameters)
796+
# 11| 0: [Parameter] c
797+
# 11| -1: [TypeMention] object
798+
# 12| 4: [IsExpr] ... is ...
799+
# 12| 0: [ParameterAccess] access to parameter c
800+
# 12| 1: [NotPatternExpr] not ...
801+
# 12| 0: [RecursivePatternExpr] { ... }
802+
# 12| 0: [LocalVariableDeclExpr] UnaryPattern u
803+
# 12| 1: [TypeAccess] access to type UnaryPattern
804+
# 12| 0: [TypeMention] UnaryPattern
805+
# 12| 3: [PropertyPatternExpr] { ... }
806+
# 12| 0: [ConstantPatternExpr,IntLiteral,LabeledPatternExpr] 1
807+
# 14| 9: [Method] M4
808+
# 14| -1: [TypeMention] string
809+
#-----| 2: (Parameters)
810+
# 14| 0: [Parameter] i
811+
# 14| -1: [TypeMention] int
812+
# 15| 4: [BlockStmt] {...}
813+
# 16| 0: [ReturnStmt] return ...;
814+
# 16| 0: [SwitchExpr] ... switch { ... }
815+
# 16| -1: [ParameterAccess] access to parameter i
816+
# 18| 0: [SwitchCaseExpr] ... => ...
817+
# 18| 0: [NotPatternExpr] not ...
818+
# 18| 0: [ConstantPatternExpr,IntLiteral] 1
819+
# 18| 2: [StringLiteral] "not 1"
820+
# 19| 1: [SwitchCaseExpr] ... => ...
821+
# 19| 0: [DiscardPatternExpr] _
822+
# 19| 2: [StringLiteral] "other"
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
using System;
2+
3+
public class UnaryPattern
4+
{
5+
public int P1 { get; set; }
6+
7+
public static bool M1(char c) =>
8+
c is not 'a';
9+
public static bool M2(object c) =>
10+
c is not null;
11+
public static bool M3(object c) =>
12+
c is not UnaryPattern { P1: 1 } u;
13+
14+
public static string M4(int i)
15+
{
16+
return i switch
17+
{
18+
not 1 => "not 1",
19+
_ => "other"
20+
};
21+
}
22+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
| UnaryPattern.cs:8:14:8:20 | not ... | UnaryPattern.cs:8:18:8:20 | a |
2+
| UnaryPattern.cs:10:14:10:21 | not ... | UnaryPattern.cs:10:18:10:21 | null |
3+
| UnaryPattern.cs:12:14:12:41 | not ... | UnaryPattern.cs:12:18:12:41 | { ... } |
4+
| UnaryPattern.cs:18:13:18:17 | not ... | UnaryPattern.cs:18:17:18:17 | 1 |
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import csharp
2+
3+
from UnaryPatternExpr pattern
4+
select pattern, pattern.getAChild()

0 commit comments

Comments
 (0)