Skip to content

Commit b11fc2f

Browse files
committed
C#: Extract relational patterns
1 parent c751c51 commit b11fc2f

File tree

12 files changed

+177
-2
lines changed

12 files changed

+177
-2
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 `RelationalPatternExpr` and its 4 subclass have been added to support C# 9
3+
relational `<`, `>`, `<=`, and `>=` patterns.

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ internal static Expression Create(Context cx, PatternSyntax syntax, IExpressionP
4242
case RecursivePatternSyntax recPattern:
4343
return new RecursivePattern(cx, recPattern, parent, child);
4444

45+
case RelationalPatternSyntax relPattern:
46+
return new RelationalPattern(cx, relPattern, parent, child);
47+
4548
case VarPatternSyntax varPattern:
4649
switch (varPattern.Designation)
4750
{

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
namespace Semmle.Extraction.CSharp.Entities.Expressions
88
{
9-
internal class RecursivePattern : Expression
9+
internal partial class RecursivePattern : Expression
1010
{
1111
/// <summary>
1212
/// Creates and populates a recursive pattern.
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
using Microsoft.CodeAnalysis;
2+
using Microsoft.CodeAnalysis.CSharp.Syntax;
3+
using Microsoft.CodeAnalysis.CSharp;
4+
using Semmle.Extraction.Kinds;
5+
using Semmle.Extraction.Entities;
6+
7+
namespace Semmle.Extraction.CSharp.Entities.Expressions
8+
{
9+
internal class RelationalPattern : Expression
10+
{
11+
public RelationalPattern(Context cx, RelationalPatternSyntax syntax, IExpressionParentEntity parent, int child) :
12+
base(new ExpressionInfo(cx, NullType.Create(cx), cx.Create(syntax.GetLocation()), GetKind(syntax.OperatorToken), parent, child, false, null))
13+
{
14+
Expression.Create(cx, syntax.Expression, this, 0);
15+
}
16+
17+
private static ExprKind GetKind(SyntaxToken operatorToken)
18+
{
19+
return operatorToken.Kind() switch
20+
{
21+
SyntaxKind.LessThanEqualsToken => ExprKind.LE_PATTERN,
22+
SyntaxKind.GreaterThanEqualsToken => ExprKind.GE_PATTERN,
23+
SyntaxKind.LessThanToken => ExprKind.LT_PATTERN,
24+
SyntaxKind.GreaterThanToken => ExprKind.GT_PATTERN,
25+
_ => throw new InternalError(operatorToken.Parent, $"Relation pattern with operator token '{operatorToken.Kind()}' is not supported."),
26+
};
27+
}
28+
}
29+
}

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,10 @@ 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+
LT_PATTERN = 122,
120+
GT_PATTERN = 123,
121+
LE_PATTERN = 124,
122+
GE_PATTERN = 125,
119123
}
120124
}

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

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,45 @@ class ConstantPatternExpr extends PatternExpr {
342342
override string getAPrimaryQlClass() { result = "ConstantPatternExpr" }
343343
}
344344

345+
/** A relational pattern, for example `>1` in `x is >1`. */
346+
abstract class RelationalPatternExpr extends PatternExpr, @relational_pattern_expr {
347+
/** Get the operator of this relational pattern. */
348+
string getOperator() { none() }
349+
350+
/** Gets the expression of this relational pattern. */
351+
Expr getExpr() { result = this.getChild(0) }
352+
353+
override string toString() { result = getOperator() + " ..." }
354+
}
355+
356+
/** A 'less than' pattern */
357+
class LTPattern extends RelationalPatternExpr, @lt_pattern_expr {
358+
override string getOperator() { result = "<" }
359+
360+
override string getAPrimaryQlClass() { result = "LTPattern" }
361+
}
362+
363+
/** A 'greater than' pattern */
364+
class GTPattern extends RelationalPatternExpr, @gt_pattern_expr {
365+
override string getOperator() { result = ">" }
366+
367+
override string getAPrimaryQlClass() { result = "GTPattern" }
368+
}
369+
370+
/** A 'less than equal' pattern */
371+
class LEPattern extends RelationalPatternExpr, @le_pattern_expr {
372+
override string getOperator() { result = "<=" }
373+
374+
override string getAPrimaryQlClass() { result = "LEPattern" }
375+
}
376+
377+
/** A 'greater than equal' pattern */
378+
class GEPattern extends RelationalPatternExpr, @ge_pattern_expr {
379+
override string getOperator() { result = ">=" }
380+
381+
override string getAPrimaryQlClass() { result = "GEPattern" }
382+
}
383+
345384
/**
346385
* A type pattern, for example `string` in `x is string`, `string s` in
347386
* `x is string s`, or `string _` in `x is string _`.

csharp/ql/src/semmlecode.csharp.dbscheme

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -998,11 +998,17 @@ case @expr.kind of
998998
| 119 = @assign_coalesce_expr
999999
| 120 = @suppress_nullable_warning_expr
10001000
| 121 = @namespace_access_expr
1001+
/* C# 9.0 */
1002+
| 122 = @lt_pattern_expr
1003+
| 123 = @gt_pattern_expr
1004+
| 124 = @le_pattern_expr
1005+
| 125 = @ge_pattern_expr
10011006
;
10021007

10031008
@switch = @switch_stmt | @switch_expr;
10041009
@case = @case_stmt | @switch_case_expr;
10051010
@pattern_match = @case | @is_expr;
1011+
@relational_pattern_expr = @gt_pattern_expr | @lt_pattern_expr | @ge_pattern_expr | @le_pattern_expr;
10061012

10071013
@integer_literal_expr = @int_literal_expr | @long_literal_expr | @uint_literal_expr | @ulong_literal_expr;
10081014
@real_literal_expr = @float_literal_expr | @double_literal_expr | @decimal_literal_expr;

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

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,63 @@ ParenthesizedPattern.cs:
335335
# 27| 0: [TypeAccessPatternExpr] access to type String
336336
# 27| 0: [TypeMention] string
337337
# 27| 2: [IntLiteral] 5
338+
RelationalPattern.cs:
339+
# 3| [Class] RelationalPattern
340+
# 5| 5: [Method] M1
341+
# 5| -1: [TypeMention] bool
342+
#-----| 2: (Parameters)
343+
# 5| 0: [Parameter] c
344+
# 5| -1: [TypeMention] char
345+
# 6| 4: [IsExpr] ... is ...
346+
# 6| 0: [ParameterAccess] access to parameter c
347+
# 6| 1: [GEPattern] >= ...
348+
# 6| 0: [CharLiteral] a
349+
# 7| 6: [Method] M2
350+
# 7| -1: [TypeMention] bool
351+
#-----| 2: (Parameters)
352+
# 7| 0: [Parameter] c
353+
# 7| -1: [TypeMention] char
354+
# 8| 4: [IsExpr] ... is ...
355+
# 8| 0: [ParameterAccess] access to parameter c
356+
# 8| 1: [GTPattern] > ...
357+
# 8| 0: [CharLiteral] a
358+
# 9| 7: [Method] M3
359+
# 9| -1: [TypeMention] bool
360+
#-----| 2: (Parameters)
361+
# 9| 0: [Parameter] c
362+
# 9| -1: [TypeMention] char
363+
# 10| 4: [IsExpr] ... is ...
364+
# 10| 0: [ParameterAccess] access to parameter c
365+
# 10| 1: [LEPattern] <= ...
366+
# 10| 0: [CharLiteral] a
367+
# 11| 8: [Method] M4
368+
# 11| -1: [TypeMention] bool
369+
#-----| 2: (Parameters)
370+
# 11| 0: [Parameter] c
371+
# 11| -1: [TypeMention] char
372+
# 12| 4: [IsExpr] ... is ...
373+
# 12| 0: [ParameterAccess] access to parameter c
374+
# 12| 1: [LTPattern] < ...
375+
# 12| 0: [CharLiteral] a
376+
# 14| 9: [Method] M5
377+
# 14| -1: [TypeMention] string
378+
#-----| 2: (Parameters)
379+
# 14| 0: [Parameter] i
380+
# 14| -1: [TypeMention] int
381+
# 15| 4: [BlockStmt] {...}
382+
# 16| 0: [ReturnStmt] return ...;
383+
# 16| 0: [SwitchExpr] ... switch { ... }
384+
# 16| -1: [ParameterAccess] access to parameter i
385+
# 18| 0: [SwitchCaseExpr] ... => ...
386+
# 18| 0: [ConstantPatternExpr,IntLiteral] 1
387+
# 18| 2: [StringLiteral] "1"
388+
# 19| 1: [SwitchCaseExpr] ... => ...
389+
# 19| 0: [GTPattern] > ...
390+
# 19| 0: [IntLiteral] 1
391+
# 19| 2: [StringLiteral] ">1"
392+
# 20| 2: [SwitchCaseExpr] ... => ...
393+
# 20| 0: [DiscardPatternExpr] _
394+
# 20| 2: [StringLiteral] "other"
338395
TargetType.cs:
339396
# 5| [Class] TargetType
340397
# 7| 5: [Method] M2
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
using System;
2+
3+
public class RelationalPattern
4+
{
5+
public static bool M1(char c) =>
6+
c is >= 'a';
7+
public static bool M2(char c) =>
8+
c is > 'a';
9+
public static bool M3(char c) =>
10+
c is <= 'a';
11+
public static bool M4(char c) =>
12+
c is < 'a';
13+
14+
public static string M5(int i)
15+
{
16+
return i switch
17+
{
18+
1 => "1",
19+
>1 => ">1",
20+
_ => "other"
21+
};
22+
}
23+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
| RelationalPattern.cs:6:14:6:19 | >= ... | >= |
2+
| RelationalPattern.cs:8:14:8:18 | > ... | > |
3+
| RelationalPattern.cs:10:14:10:19 | <= ... | <= |
4+
| RelationalPattern.cs:12:14:12:18 | < ... | < |
5+
| RelationalPattern.cs:19:13:19:14 | > ... | > |

0 commit comments

Comments
 (0)