Skip to content

C#: Extract relational patterns #4653

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Nov 27, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions csharp/change-notes/2020-11-19-Relational-pattern.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
lgtm,codescanning
* The `RelationalPatternExpr` and its 4 sub class have been added to support C# 9
relational `<`, `>`, `<=`, and `>=` patterns.
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ internal static Expression Create(Context cx, PatternSyntax syntax, IExpressionP
case RecursivePatternSyntax recPattern:
return new RecursivePattern(cx, recPattern, parent, child);

case RelationalPatternSyntax relPattern:
return new RelationalPattern(cx, relPattern, parent, child);

case VarPatternSyntax varPattern:
switch (varPattern.Designation)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ internal class RecursivePattern : Expression
/// <param name="syntax">The syntax node of the recursive pattern.</param>
/// <param name="parent">The parent pattern/expression.</param>
/// <param name="child">The child index of this pattern.</param>
/// <param name="isTopLevel">If this pattern is in the top level of a case/is. In that case, the variable and type access are populated elsewhere.</param>
public RecursivePattern(Context cx, RecursivePatternSyntax syntax, IExpressionParentEntity parent, int child) :
base(new ExpressionInfo(cx, Entities.NullType.Create(cx), cx.Create(syntax.GetLocation()), ExprKind.RECURSIVE_PATTERN, parent, child, false, null))
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.CSharp;
using Semmle.Extraction.Kinds;
using Semmle.Extraction.Entities;

namespace Semmle.Extraction.CSharp.Entities.Expressions
{
internal class RelationalPattern : Expression
{
public RelationalPattern(Context cx, RelationalPatternSyntax syntax, IExpressionParentEntity parent, int child) :
base(new ExpressionInfo(cx, NullType.Create(cx), cx.Create(syntax.GetLocation()), GetKind(syntax.OperatorToken), parent, child, false, null))
{
Expression.Create(cx, syntax.Expression, this, 0);
}

private static ExprKind GetKind(SyntaxToken operatorToken)
{
return operatorToken.Kind() switch
{
SyntaxKind.LessThanEqualsToken => ExprKind.LE_PATTERN,
SyntaxKind.GreaterThanEqualsToken => ExprKind.GE_PATTERN,
SyntaxKind.LessThanToken => ExprKind.LT_PATTERN,
SyntaxKind.GreaterThanToken => ExprKind.GT_PATTERN,
_ => throw new InternalError(operatorToken.Parent, $"Relation pattern with operator token '{operatorToken.Kind()}' is not supported."),
};
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,10 @@ public enum ExprKind
SWITCH_CASE = 118,
ASSIGN_COALESCE = 119,
SUPPRESS_NULLABLE_WARNING = 120,
NAMESPACE_ACCESS = 121
NAMESPACE_ACCESS = 121,
LT_PATTERN = 122,
GT_PATTERN = 123,
LE_PATTERN = 124,
GE_PATTERN = 125,
}
}
39 changes: 39 additions & 0 deletions csharp/ql/src/semmle/code/csharp/exprs/Expr.qll
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,45 @@ class ConstantPatternExpr extends PatternExpr {
override string getAPrimaryQlClass() { result = "ConstantPatternExpr" }
}

/** A relational pattern, for example `>1` in `x is >1`. */
class RelationalPatternExpr extends PatternExpr, @relational_pattern_expr {
/** Gets the name of the operator in this pattern. */
string getOperator() { none() }

/** Gets the expression of this relational pattern. */
Expr getExpr() { result = this.getChild(0) }

override string toString() { result = getOperator() + " ..." }
}

/** A less-than pattern, for example `< 10` in `x is < 10`. */
class LTPattern extends RelationalPatternExpr, @lt_pattern_expr {
override string getOperator() { result = "<" }

override string getAPrimaryQlClass() { result = "LTPattern" }
}

/** A greater-than pattern, for example `> 10` in `x is > 10`. */
class GTPattern extends RelationalPatternExpr, @gt_pattern_expr {
override string getOperator() { result = ">" }

override string getAPrimaryQlClass() { result = "GTPattern" }
}

/** A less-than or equals pattern, for example `<= 10` in `x is <= 10`. */
class LEPattern extends RelationalPatternExpr, @le_pattern_expr {
override string getOperator() { result = "<=" }

override string getAPrimaryQlClass() { result = "LEPattern" }
}

/** A greater-than or equals pattern, for example `>= 10` in `x is >= 10` */
class GEPattern extends RelationalPatternExpr, @ge_pattern_expr {
override string getOperator() { result = ">=" }

override string getAPrimaryQlClass() { result = "GEPattern" }
}

/**
* A type pattern, for example `string` in `x is string`, `string s` in
* `x is string s`, or `string _` in `x is string _`.
Expand Down
6 changes: 6 additions & 0 deletions csharp/ql/src/semmlecode.csharp.dbscheme
Original file line number Diff line number Diff line change
Expand Up @@ -998,11 +998,17 @@ case @expr.kind of
| 119 = @assign_coalesce_expr
| 120 = @suppress_nullable_warning_expr
| 121 = @namespace_access_expr
/* C# 9.0 */
| 122 = @lt_pattern_expr
| 123 = @gt_pattern_expr
| 124 = @le_pattern_expr
| 125 = @ge_pattern_expr
;

@switch = @switch_stmt | @switch_expr;
@case = @case_stmt | @switch_case_expr;
@pattern_match = @case | @is_expr;
@relational_pattern_expr = @gt_pattern_expr | @lt_pattern_expr | @ge_pattern_expr | @le_pattern_expr;

@integer_literal_expr = @int_literal_expr | @long_literal_expr | @uint_literal_expr | @ulong_literal_expr;
@real_literal_expr = @float_literal_expr | @double_literal_expr | @decimal_literal_expr;
Expand Down
57 changes: 57 additions & 0 deletions csharp/ql/test/library-tests/csharp9/PrintAst.expected
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,63 @@ ParenthesizedPattern.cs:
# 27| 0: [TypeAccessPatternExpr] access to type String
# 27| 0: [TypeMention] string
# 27| 2: [IntLiteral] 5
RelationalPattern.cs:
# 3| [Class] RelationalPattern
# 5| 5: [Method] M1
# 5| -1: [TypeMention] bool
#-----| 2: (Parameters)
# 5| 0: [Parameter] c
# 5| -1: [TypeMention] char
# 6| 4: [IsExpr] ... is ...
# 6| 0: [ParameterAccess] access to parameter c
# 6| 1: [GEPattern] >= ...
# 6| 0: [CharLiteral] a
# 7| 6: [Method] M2
# 7| -1: [TypeMention] bool
#-----| 2: (Parameters)
# 7| 0: [Parameter] c
# 7| -1: [TypeMention] char
# 8| 4: [IsExpr] ... is ...
# 8| 0: [ParameterAccess] access to parameter c
# 8| 1: [GTPattern] > ...
# 8| 0: [CharLiteral] a
# 9| 7: [Method] M3
# 9| -1: [TypeMention] bool
#-----| 2: (Parameters)
# 9| 0: [Parameter] c
# 9| -1: [TypeMention] char
# 10| 4: [IsExpr] ... is ...
# 10| 0: [ParameterAccess] access to parameter c
# 10| 1: [LEPattern] <= ...
# 10| 0: [CharLiteral] a
# 11| 8: [Method] M4
# 11| -1: [TypeMention] bool
#-----| 2: (Parameters)
# 11| 0: [Parameter] c
# 11| -1: [TypeMention] char
# 12| 4: [IsExpr] ... is ...
# 12| 0: [ParameterAccess] access to parameter c
# 12| 1: [LTPattern] < ...
# 12| 0: [CharLiteral] a
# 14| 9: [Method] M5
# 14| -1: [TypeMention] string
#-----| 2: (Parameters)
# 14| 0: [Parameter] i
# 14| -1: [TypeMention] int
# 15| 4: [BlockStmt] {...}
# 16| 0: [ReturnStmt] return ...;
# 16| 0: [SwitchExpr] ... switch { ... }
# 16| -1: [ParameterAccess] access to parameter i
# 18| 0: [SwitchCaseExpr] ... => ...
# 18| 0: [ConstantPatternExpr,IntLiteral] 1
# 18| 2: [StringLiteral] "1"
# 19| 1: [SwitchCaseExpr] ... => ...
# 19| 0: [GTPattern] > ...
# 19| 0: [IntLiteral] 1
# 19| 2: [StringLiteral] ">1"
# 20| 2: [SwitchCaseExpr] ... => ...
# 20| 0: [DiscardPatternExpr] _
# 20| 2: [StringLiteral] "other"
TargetType.cs:
# 5| [Class] TargetType
# 7| 5: [Method] M2
Expand Down
23 changes: 23 additions & 0 deletions csharp/ql/test/library-tests/csharp9/RelationalPattern.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using System;

public class RelationalPattern
{
public static bool M1(char c) =>
c is >= 'a';
public static bool M2(char c) =>
c is > 'a';
public static bool M3(char c) =>
c is <= 'a';
public static bool M4(char c) =>
c is < 'a';

public static string M5(int i)
{
return i switch
{
1 => "1",
>1 => ">1",
_ => "other"
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
| RelationalPattern.cs:6:14:6:19 | >= ... | >= |
| RelationalPattern.cs:8:14:8:18 | > ... | > |
| RelationalPattern.cs:10:14:10:19 | <= ... | <= |
| RelationalPattern.cs:12:14:12:18 | < ... | < |
| RelationalPattern.cs:19:13:19:14 | > ... | > |
4 changes: 4 additions & 0 deletions csharp/ql/test/library-tests/csharp9/relationalPattern.ql
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import csharp

from RelationalPatternExpr p
select p, p.getOperator()
Loading