Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 1428d0b

Browse files
committed
C#: Implement recursive patterns
1 parent 318068b commit 1428d0b

26 files changed

Lines changed: 1493 additions & 56 deletions

csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Discard.cs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,23 @@
11
using Microsoft.CodeAnalysis.CSharp.Syntax;
22
using Semmle.Extraction.Kinds;
3+
using Semmle.Extraction.Entities;
4+
using Microsoft.CodeAnalysis;
35

46
namespace Semmle.Extraction.CSharp.Entities.Expressions
57
{
6-
class Discard : Expression<NameSyntax>
8+
class Discard : Expression
79
{
810
public Discard(ExpressionNodeInfo info) : base(info.SetKind(ExprKind.DISCARD))
911
{
1012
}
1113

12-
protected override void Populate()
14+
public Discard(Context cx, DiscardDesignationSyntax discard, IExpressionParentEntity parent, int child) :
15+
base(new ExpressionInfo(cx, Type.Create(cx, cx.Model(discard).GetTypeInfo(discard).Type), cx.Create(discard.GetLocation()), ExprKind.DISCARD, parent, child, false, null))
16+
{
17+
}
18+
19+
public Discard(Context cx, DiscardPatternSyntax pattern, IExpressionParentEntity parent, int child) :
20+
base(new ExpressionInfo(cx, Type.Create(cx, cx.Model(pattern).GetTypeInfo(pattern).Type), cx.Create(pattern.GetLocation()), ExprKind.DISCARD, parent, child, false, null))
1321
{
1422
}
1523
}

csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Factory.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,9 @@ internal static Expression Create(ExpressionNodeInfo info)
238238
case SyntaxKind.IndexExpression:
239239
return Unary.Create(info.SetKind(ExprKind.INDEX));
240240

241+
case SyntaxKind.SwitchExpression:
242+
return Switch.Create(info);
243+
241244
default:
242245
info.Context.ModelError(info.Node, $"Unhandled expression '{info.Node}' of kind '{info.Node.Kind()}'");
243246
return new Unknown(info);

csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/IsPattern.cs

Lines changed: 125 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,126 @@
66

77
namespace Semmle.Extraction.CSharp.Entities.Expressions
88
{
9+
static class PatternExtensions
10+
{
11+
public static Expression CreatePattern(this Context cx, PatternSyntax syntax, IExpressionParentEntity parent, int child)
12+
{
13+
switch (syntax)
14+
{
15+
case ConstantPatternSyntax constantPattern:
16+
return Expression.Create(cx, constantPattern.Expression, parent, child);
17+
18+
case DeclarationPatternSyntax declPattern:
19+
// Creates a single local variable declaration.
20+
{
21+
if (declPattern.Designation is VariableDesignationSyntax designation && cx.Model(syntax).GetDeclaredSymbol(designation) is ILocalSymbol symbol)
22+
{
23+
var type = Type.Create(cx, symbol.Type);
24+
25+
return VariableDeclaration.Create(cx, symbol, type, cx.Create(syntax.GetLocation()), cx.Create(designation.GetLocation()), false, parent, child);
26+
}
27+
throw new InternalError(syntax, "Is pattern not handled");
28+
}
29+
30+
case RecursivePatternSyntax recPattern:
31+
return new RecursivePattern(cx, recPattern, parent, child, false);
32+
33+
case VarPatternSyntax varPattern:
34+
switch(varPattern.Designation)
35+
{
36+
case ParenthesizedVariableDesignationSyntax parDesignation:
37+
return VariableDeclaration.CreateParenthesized(cx, varPattern, parDesignation, parent, child);
38+
case SingleVariableDesignationSyntax varDesignation:
39+
if (cx.Model(syntax).GetDeclaredSymbol(varDesignation) is ILocalSymbol symbol2)
40+
{
41+
var type = Type.Create(cx, symbol2.Type);
42+
43+
return VariableDeclaration.Create(cx, symbol2, type, cx.Create(syntax.GetLocation()), cx.Create(varDesignation.GetLocation()), false, parent, child);
44+
}
45+
else
46+
{
47+
throw new InternalError(varPattern, "Unable to get the declared symbol of the var pattern designation.");
48+
}
49+
default:
50+
throw new InternalError("var pattern designation is unhandled");
51+
}
52+
53+
case DiscardPatternSyntax dp:
54+
return new Discard(cx, dp, parent, child);
55+
56+
default:
57+
throw new InternalError(syntax, "Is pattern not handled");
58+
}
59+
60+
}
61+
}
62+
63+
class PropertyPattern : Expression
64+
{
65+
internal PropertyPattern(Context cx, PropertyPatternClauseSyntax pp, IExpressionParentEntity parent, int child) :
66+
base(new ExpressionInfo(cx, Type.Create(cx, null), cx.Create(pp.GetLocation()), ExprKind.PROPERTY_PATTERN, parent, child, false, null))
67+
{
68+
child = 0;
69+
foreach (var sub in pp.Subpatterns)
70+
{
71+
cx.CreatePattern(sub.Pattern, this, child++);
72+
}
73+
}
74+
}
75+
76+
class PositionalPattern : Expression
77+
{
78+
internal PositionalPattern(Context cx, PositionalPatternClauseSyntax posPc, IExpressionParentEntity parent, int child) :
79+
base(new ExpressionInfo(cx, Type.Create(cx, null), cx.Create(posPc.GetLocation()), ExprKind.POSITIONAL_PATTERN, parent, child, false, null))
80+
{
81+
child = 0;
82+
foreach (var sub in posPc.Subpatterns)
83+
{
84+
cx.CreatePattern(sub.Pattern, this, child++);
85+
}
86+
}
87+
}
88+
89+
class RecursivePattern : Expression
90+
{
91+
/// <summary>
92+
/// Creates and populates a recursive pattern.
93+
/// </summary>
94+
/// <param name="cx">The extraction context.</param>
95+
/// <param name="syntax">The syntax node of the recursive pattern.</param>
96+
/// <param name="parent">The parent pattern/expression.</param>
97+
/// <param name="child">The child index of this pattern.</param>
98+
/// <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>
99+
public RecursivePattern(Context cx, RecursivePatternSyntax syntax, IExpressionParentEntity parent, int child, bool isTopLevel) :
100+
base(new ExpressionInfo(cx, Type.Create(cx, null), cx.Create(syntax.GetLocation()), ExprKind.RECURSIVE_PATTERN, parent, child, false, null))
101+
{
102+
if(!isTopLevel)
103+
{
104+
// Extract the type access
105+
if(syntax.Type is TypeSyntax t)
106+
Expressions.TypeAccess.Create(cx, t, this, 1);
107+
108+
// Extract the local variable declaration
109+
if (syntax.Designation is VariableDesignationSyntax designation && cx.Model(syntax).GetDeclaredSymbol(designation) is ILocalSymbol symbol)
110+
{
111+
var type = Type.Create(cx, symbol.Type);
112+
113+
VariableDeclaration.Create(cx, symbol, type, cx.Create(syntax.GetLocation()), cx.Create(designation.GetLocation()), false, this, 0);
114+
}
115+
}
116+
117+
if (syntax.PositionalPatternClause is PositionalPatternClauseSyntax posPc)
118+
{
119+
new PositionalPattern(cx, posPc, this, 2);
120+
}
121+
122+
if (syntax.PropertyPatternClause is PropertyPatternClauseSyntax pc)
123+
{
124+
new PropertyPattern(cx, pc, this, 3);
125+
}
126+
}
127+
}
128+
9129
class IsPattern : Expression<IsPatternExpressionSyntax>
10130
{
11131
private IsPattern(ExpressionNodeInfo info) : base(info.SetKind(ExprKind.IS))
@@ -18,7 +138,7 @@ private void PopulatePattern(PatternSyntax pattern, TypeSyntax optionalType, Syn
18138
if (!isVar)
19139
Expressions.TypeAccess.Create(cx, optionalType, this, 1);
20140

21-
if (cx.Model(pattern).GetDeclaredSymbol(designation) is ILocalSymbol symbol)
141+
if (!(designation is null) && cx.Model(pattern).GetDeclaredSymbol(designation) is ILocalSymbol symbol)
22142
{
23143
var type = Type.Create(cx, symbol.Type);
24144

@@ -43,6 +163,10 @@ protected override void Populate()
43163
case DeclarationPatternSyntax declPattern:
44164
PopulatePattern(declPattern, declPattern.Type, default(SyntaxToken), declPattern.Designation);
45165
return;
166+
case RecursivePatternSyntax recPattern:
167+
PopulatePattern(recPattern, recPattern.Type, default(SyntaxToken), recPattern.Designation);
168+
new RecursivePattern(cx, recPattern, this, 4, true);
169+
return;
46170
default:
47171
throw new InternalError(Syntax, "Is pattern not handled");
48172
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
using Microsoft.CodeAnalysis.CSharp.Syntax;
2+
using Semmle.Extraction.Kinds;
3+
4+
namespace Semmle.Extraction.CSharp.Entities.Expressions
5+
{
6+
class RangeExpression : Expression<RangeExpressionSyntax>
7+
{
8+
private RangeExpression(ExpressionNodeInfo info) : base(info.SetKind(ExprKind.RANGE))
9+
{
10+
}
11+
12+
protected override void Populate()
13+
{
14+
if (!(Syntax.LeftOperand is null))
15+
Expression.Create(cx, Syntax.LeftOperand, this, 0);
16+
if (!(Syntax.RightOperand is null))
17+
Expression.Create(cx, Syntax.RightOperand, this, 1);
18+
}
19+
20+
public static Expression Create(ExpressionNodeInfo info) => new RangeExpression(info).TryPopulate();
21+
}
22+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
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+
class Switch : Expression<SwitchExpressionSyntax>
8+
{
9+
private Switch(ExpressionNodeInfo info) : base(info.SetKind(ExprKind.SWITCH))
10+
{
11+
}
12+
13+
public static Expression Create(ExpressionNodeInfo info) => new Switch(info).TryPopulate();
14+
15+
public Expression SwitchedExpr { get; private set; }
16+
17+
protected override void Populate()
18+
{
19+
SwitchedExpr = Expression.Create(cx, Syntax.GoverningExpression, this, -1);
20+
int child = 0;
21+
foreach(var arm in Syntax.Arms)
22+
{
23+
new SwitchCase(cx, arm, this, child++);
24+
}
25+
}
26+
}
27+
28+
class SwitchCase : Expression
29+
{
30+
internal SwitchCase(Context cx, SwitchExpressionArmSyntax arm, Switch parent, int child) :
31+
base(new ExpressionInfo(cx, parent.SwitchedExpr.Type, cx.Create(arm.GetLocation()), ExprKind.SWITCH_CASE, parent, child, false, null))
32+
{
33+
cx.CreatePattern(arm.Pattern, this, 0);
34+
if (arm.WhenClause is WhenClauseSyntax when)
35+
Expression.Create(cx, when.Condition, this, 1);
36+
Expression.Create(cx, arm.Expression, this, 2);
37+
}
38+
}
39+
}

csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/VariableDeclaration.cs

Lines changed: 60 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,13 @@ static VariableDeclaration CreateSingle(Context cx, DeclarationExpressionSyntax
2626
if (variableSymbol == null)
2727
{
2828
cx.ModelError(node, "Failed to determine local variable");
29-
return new VariableDeclaration(cx, node, null, isVar, parent, child);
29+
return Create(cx, node, null, isVar, parent, child);
3030
}
3131

3232
var type = Type.Create(cx, variableSymbol.Type);
3333
var location = cx.Create(designation.GetLocation());
3434

35-
var ret = new VariableDeclaration(cx, designation, type, isVar, parent, child);
35+
var ret = Create(cx, designation, type, isVar, parent, child);
3636
cx.Try(null, null, () => LocalVariable.Create(cx, variableSymbol, ret, isVar, location));
3737
return ret;
3838
}
@@ -41,7 +41,7 @@ static VariableDeclaration CreateSingle(Context cx, DeclarationExpressionSyntax
4141
/// Create a tuple expression representing a parenthesized variable declaration.
4242
/// That is, we consider `var (x, y) = ...` to be equivalent to `(var x, var y) = ...`.
4343
/// </summary>
44-
static Expression CreateParenthesized(Context cx, DeclarationExpressionSyntax node, ParenthesizedVariableDesignationSyntax designation, IExpressionParentEntity parent, int child)
44+
public static Expression CreateParenthesized(Context cx, DeclarationExpressionSyntax node, ParenthesizedVariableDesignationSyntax designation, IExpressionParentEntity parent, int child)
4545
{
4646
var type = Type.Create(cx, null); // Should ideally be a corresponding tuple type
4747
var tuple = new Expression(new ExpressionInfo(cx, type, cx.Create(node.GetLocation()), ExprKind.TUPLE, parent, child, false, null));
@@ -56,41 +56,73 @@ static Expression CreateParenthesized(Context cx, DeclarationExpressionSyntax no
5656
return tuple;
5757
}
5858

59-
static Expression Create(Context cx, DeclarationExpressionSyntax node, VariableDesignationSyntax designation, IExpressionParentEntity parent, int child)
59+
public static Expression CreateParenthesized(Context cx, VarPatternSyntax varPattern, ParenthesizedVariableDesignationSyntax designation, IExpressionParentEntity parent, int child)
6060
{
61-
var single = designation as SingleVariableDesignationSyntax;
62-
if (single != null)
63-
return CreateSingle(cx, node, single, parent, child);
64-
65-
var paren = designation as ParenthesizedVariableDesignationSyntax;
66-
if (paren != null)
67-
return CreateParenthesized(cx, node, paren, parent, child);
61+
var type = Type.Create(cx, null); // Should ideally be a corresponding tuple type
62+
var tuple = new Expression(new ExpressionInfo(cx, type, cx.Create(varPattern.GetLocation()), ExprKind.TUPLE, parent, child, false, null));
6863

69-
var discard = designation as DiscardDesignationSyntax;
70-
if (discard != null)
64+
cx.Try(null, null, () =>
7165
{
72-
var type = cx.Model(node).GetTypeInfo(node).Type;
73-
return new VariableDeclaration(cx, node, Type.Create(cx, type), node.Type.IsVar, parent, child);
74-
}
66+
var child0 = 0;
67+
foreach (var variable in designation.Variables)
68+
switch(variable)
69+
{
70+
case ParenthesizedVariableDesignationSyntax paren:
71+
CreateParenthesized(cx, varPattern, paren, tuple, child0++);
72+
break;
73+
case SingleVariableDesignationSyntax single:
74+
if (cx.Model(variable).GetDeclaredSymbol(single) is ILocalSymbol local)
75+
{
76+
var decl = Create(cx, variable, Type.Create(cx, local.Type), true, tuple, child0++);
77+
var id = single.Identifier;
78+
var declSymbol = cx.Model(single).GetDeclaredSymbol(single);
79+
var location = cx.Create(id.GetLocation());
80+
LocalVariable.Create(cx, local, decl, true, location);
81+
}
82+
else
83+
{
84+
throw new InternalError(single, "Failed to access local variable");
85+
}
86+
break;
87+
case DiscardDesignationSyntax discard:
88+
new Discard(cx, discard, tuple, child0++);
89+
break;
90+
default:
91+
throw new InternalError(variable, "Unhandled designation type");
92+
}
93+
});
7594

76-
cx.ModelError(node, "Failed to determine designation type");
77-
return new VariableDeclaration(cx, node, null, node.Type.IsVar, parent, child);
95+
return tuple;
7896
}
7997

80-
public static Expression Create(Context cx, DeclarationExpressionSyntax node, IExpressionParentEntity parent, int child)
81-
{
82-
return Create(cx, node, node.Designation, parent, child);
83-
}
8498

85-
VariableDeclaration(Context cx, CSharpSyntaxNode d, Type type, bool isVar, IExpressionParentEntity parent, int child)
86-
: base(new ExpressionInfo(cx, type, cx.Create(d.FixedLocation()), ExprKind.LOCAL_VAR_DECL, parent, child, false, null))
99+
static Expression Create(Context cx, DeclarationExpressionSyntax node, VariableDesignationSyntax designation, IExpressionParentEntity parent, int child)
87100
{
101+
switch(designation)
102+
{
103+
case SingleVariableDesignationSyntax single:
104+
return CreateSingle(cx, node, single, parent, child);
105+
case ParenthesizedVariableDesignationSyntax paren:
106+
return CreateParenthesized(cx, node, paren, parent, child);
107+
case DiscardDesignationSyntax discard:
108+
var type = cx.Model(discard).GetTypeInfo(discard).Type;
109+
return Create(cx, node, Type.Create(cx, type), node.Type.IsVar, parent, child);
110+
default:
111+
cx.ModelError(node, "Failed to determine designation type");
112+
return Create(cx, node, null, node.Type.IsVar, parent, child);
113+
}
88114
}
89115

116+
public static Expression Create(Context cx, DeclarationExpressionSyntax node, IExpressionParentEntity parent, int child) =>
117+
Create(cx, node, node.Designation, parent, child);
118+
119+
public static VariableDeclaration Create(Context cx, CSharpSyntaxNode c, Type type, bool isVar, IExpressionParentEntity parent, int child) =>
120+
new VariableDeclaration(new ExpressionInfo(cx, type, cx.Create(c.FixedLocation()), ExprKind.LOCAL_VAR_DECL, parent, child, false, null));
121+
90122
public static VariableDeclaration Create(Context cx, CatchDeclarationSyntax d, bool isVar, IExpressionParentEntity parent, int child)
91123
{
92124
var type = Type.Create(cx, cx.Model(d).GetDeclaredSymbol(d).Type);
93-
var ret = new VariableDeclaration(cx, d, type, isVar, parent, child);
125+
var ret = Create(cx, d, type, isVar, parent, child);
94126
cx.Try(d, null, () =>
95127
{
96128
var id = d.Identifier;
@@ -102,9 +134,9 @@ public static VariableDeclaration Create(Context cx, CatchDeclarationSyntax d, b
102134
return ret;
103135
}
104136

105-
public static VariableDeclaration Create(Context cx, VariableDeclaratorSyntax d, Type type, bool isVar, IExpressionParentEntity parent, int child)
137+
public static VariableDeclaration CreateDeclarator(Context cx, VariableDeclaratorSyntax d, Type type, bool isVar, IExpressionParentEntity parent, int child)
106138
{
107-
var ret = new VariableDeclaration(cx, d, type, isVar, parent, child);
139+
var ret = Create(cx, d, type, isVar, parent, child);
108140
cx.Try(d, null, () =>
109141
{
110142
var id = d.Identifier;
@@ -137,7 +169,7 @@ public static void Populate(Context cx, VariableDeclarationSyntax decl, IExpress
137169

138170
foreach (var v in decl.Variables)
139171
{
140-
VariableDeclaration.Create(cx, v, type, decl.Type.IsVar, parent, child);
172+
VariableDeclaration.CreateDeclarator(cx, v, type, decl.Type.IsVar, parent, child);
141173
child += childIncrement;
142174
}
143175
}

0 commit comments

Comments
 (0)