Merge pull request #11594 from michaelnebel/csharp/listpattern

C#: Support List and Slice patterns.
This commit is contained in:
Michael Nebel
2022-12-16 08:21:07 +01:00
committed by GitHub
18 changed files with 8450 additions and 1 deletions

View File

@@ -0,0 +1,13 @@
class Expression extends @expr {
string toString() { none() }
}
class TypeOrRef extends @type_or_ref {
string toString() { none() }
}
from Expression e, int k, int kind, TypeOrRef t
where
expressions(e, k, t) and
if k = [131, 132] then kind = 106 else kind = k
select e, kind, t

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,3 @@
description: Remove list- and slice pattern expression kinds.
compatibility: backwards
expressions.rel: run expressions.qlo

View File

@@ -0,0 +1,15 @@
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Semmle.Extraction.Kinds;
using Semmle.Util;
namespace Semmle.Extraction.CSharp.Entities.Expressions
{
internal class ListPattern : Expression
{
internal ListPattern(Context cx, ListPatternSyntax syntax, IExpressionParentEntity parent, int child) :
base(new ExpressionInfo(cx, null, cx.CreateLocation(syntax.GetLocation()), ExprKind.LIST_PATTERN, parent, child, false, null))
{
syntax.Patterns.ForEach((p, i) => Pattern.Create(cx, p, this, i));
}
}
}

View File

@@ -74,6 +74,12 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions
case DiscardPatternSyntax dp:
return new Discard(cx, dp, parent, child);
case ListPatternSyntax listPattern:
return new ListPattern(cx, listPattern, parent, child);
case SlicePatternSyntax slicePattern:
return new SlicePattern(cx, slicePattern, parent, child);
default:
throw new InternalError(syntax, "Pattern not handled");
}

View File

@@ -0,0 +1,17 @@
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Semmle.Extraction.Kinds;
namespace Semmle.Extraction.CSharp.Entities.Expressions
{
internal class SlicePattern : Expression
{
public SlicePattern(Context cx, SlicePatternSyntax syntax, IExpressionParentEntity parent, int child) :
base(new ExpressionInfo(cx, null, cx.CreateLocation(syntax.GetLocation()), ExprKind.SLICE_PATTERN, parent, child, false, null))
{
if (syntax.Pattern is not null)
{
Pattern.Create(cx, syntax.Pattern, this, 0);
}
}
}
}

View File

@@ -125,6 +125,8 @@ namespace Semmle.Extraction.Kinds
OR_PATTERN = 128,
FUNCTION_POINTER_INVOCATION = 129,
WITH = 130,
DEFINE_SYMBOL = 999
LIST_PATTERN = 131,
SLICE_PATTERN = 132,
DEFINE_SYMBOL = 999,
}
}

View File

@@ -79,6 +79,19 @@ namespace Semmle.Util
a(item);
}
/// <summary>
/// Applies the action <paramref name="a"/> to each item and its index in this collection.
/// </summary>
public static void ForEach<T>(this IEnumerable<T> items, Action<T, int> a)
{
var i = 0;
foreach (var item in items)
{
a(item, i);
i++;
}
}
/// <summary>
/// Forces enumeration of this collection and discards the result.
/// </summary>

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* C# 11: Added support for list- and slice patterns in the extractor.

View File

@@ -321,6 +321,18 @@ private predicate hasChildPattern(ControlFlowElement pm, Expr child) {
mid instanceof @tuple_expr and
child = mid.getAChildExpr()
)
or
exists(Expr mid |
hasChildPattern(pm, mid) and
mid instanceof @list_pattern_expr and
child = mid.getAChildExpr()
)
or
exists(Expr mid |
hasChildPattern(pm, mid) and
mid instanceof @slice_pattern_expr and
child = mid.getAChildExpr()
)
}
/**
@@ -506,6 +518,26 @@ class PositionalPatternExpr extends PatternExpr, @positional_pattern_expr {
override string getAPrimaryQlClass() { result = "PositionalPatternExpr" }
}
/** 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 = "[ ... ]" }
/** Gets the `n`th pattern. */
PatternExpr getPattern(int n) { result = this.getChild(n) }
override string getAPrimaryQlClass() { result = "ListPatternExpr" }
}
/** A slice pattern. For example `..` in `x is [1, .., 2]. */
class SlicePatternExpr extends PatternExpr, @slice_pattern_expr {
override string toString() { result = ".." }
/** Gets the subpattern, if any. */
PatternExpr getPattern() { result = this.getChild(0) }
override string getAPrimaryQlClass() { result = "SlicePatternExpr" }
}
/** A unary pattern. For example, `not 1`. */
class UnaryPatternExpr extends PatternExpr, @unary_pattern_expr {
/** Gets the underlying pattern. */

View File

@@ -1134,6 +1134,9 @@ case @expr.kind of
| 128 = @or_pattern_expr
| 129 = @function_pointer_invocation_expr
| 130 = @with_expr
/* C# 11.0 */
| 131 = @list_pattern_expr
| 132 = @slice_pattern_expr
/* Preprocessor */
| 999 = @define_symbol_expr
;

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,2 @@
description: Add list- and slice pattern expressions kinds.
compatibility: backwards

View File

@@ -0,0 +1,38 @@
using System.Collections.Generic;
class ListPattern
{
public void M1(int[] x)
{
if (x is []) { }
if (x is [1]) { }
if (x is [_, 2]) { }
if (x is [var y, 3, 4]) { }
if (x is [5 or 6, _, 7]) { }
if (x is [var a, .., 2]) { }
if (x is [var b, .. { Length: 2 or 5 }, 2]) { }
}
public void M2(string[] x)
{
switch (x)
{
case []:
break;
case ["A"]:
break;
case [_, "B"]:
break;
case [var y, "C", "D"]:
break;
case ["E" or "F", _, "G"]:
break;
case [var a, .., "H"]:
break;
case [var b, .. var c, "I"]:
break;
default:
break;
}
}
}

View File

@@ -0,0 +1,20 @@
listPattern
| ListPattern.cs:7:18:7:19 | [ ... ] | |
| ListPattern.cs:8:18:8:20 | [ ... ] | 1 |
| ListPattern.cs:9:18:9:23 | [ ... ] | _, 2 |
| ListPattern.cs:10:18:10:30 | [ ... ] | Int32 y, 3, 4 |
| ListPattern.cs:11:18:11:31 | [ ... ] | ... or ..., _, 7 |
| ListPattern.cs:12:18:12:31 | [ ... ] | Int32 a, .., 2 |
| ListPattern.cs:13:18:13:50 | [ ... ] | Int32 b, .., 2 |
| ListPattern.cs:20:18:20:19 | [ ... ] | |
| ListPattern.cs:22:18:22:22 | [ ... ] | "A" |
| ListPattern.cs:24:18:24:25 | [ ... ] | _, "B" |
| ListPattern.cs:26:18:26:34 | [ ... ] | String y, "C", "D" |
| ListPattern.cs:28:18:28:37 | [ ... ] | ... or ..., _, "G" |
| ListPattern.cs:30:18:30:33 | [ ... ] | String a, .., "H" |
| ListPattern.cs:32:18:32:39 | [ ... ] | String b, .., "I" |
slicePattern
| ListPattern.cs:12:26:12:27 | .. | ..: |
| ListPattern.cs:13:26:13:46 | .. | ..:{ ... } |
| ListPattern.cs:30:26:30:27 | .. | ..: |
| ListPattern.cs:32:26:32:33 | .. | ..:String[] c |

View File

@@ -0,0 +1,19 @@
import csharp
private string childPatterns(ListPatternExpr e) {
result = concat(string child, int n | child = e.getPattern(n).toString() | child, ", " order by n)
}
query predicate listPattern(ListPatternExpr pattern, string children) {
pattern.getFile().getStem() = "ListPattern" and
children = childPatterns(pattern)
}
query predicate slicePattern(SlicePatternExpr pattern, string s) {
pattern.getFile().getStem() = "ListPattern" and
exists(string child |
if exists(pattern.getPattern()) then child = pattern.getPattern().toString() else child = ""
|
s = pattern.toString() + ":" + child
)
}