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

Skip to content

Commit 641126a

Browse files
authored
Merge pull request #1339 from calumgrant/cs/cs8/ranges
C#: Implement C#8 features
2 parents 87cbc7e + d2aea63 commit 641126a

79 files changed

Lines changed: 6140 additions & 901 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

change-notes/1.21/analysis-csharp.md

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,24 @@
99

1010
## Changes to code extraction
1111

12-
* Named attribute arguments are now extracted.
12+
* The following C# 8 features are now extracted:
13+
- Range expressions
14+
- Recursive patterns
1315

1416
## Changes to QL libraries
1517

1618
* The class `Attribute` has two new predicates: `getConstructorArgument()` and `getNamedArgument()`. The first predicate returns arguments to the underlying constructor call and the latter returns named arguments for initializing fields and properties.
19+
* The following QL classes have been added to model C# 8 features:
20+
- Class `IndexExpr` models from-end index expressions, for example `^1`
21+
- Class `PatternExpr` is an `Expr` that appears in a pattern. It has the new subclasses `DiscardPatternExpr`, `LabeledPatternExpr`, `RecursivePatternExpr`, `TypeAccessPatternExpr`, `TypePatternExpr`, and `VariablePatternExpr`.
22+
- Class `PatternMatch` models a pattern being matched. It has the subclasses `Case` and `IsExpr`.
23+
- Class `PositionalPatternExpr` models position patterns, for example `(int x, int y)`
24+
- Class `PropertyPatternExpr` models property patterns, for example `Length: int len`
25+
- Class `RangeExpr` models range expressions, for example `1..^1`
26+
- Class `SwitchCaseExpr` models the arm of a switch expression, for example `(false, false) => true`
27+
- Class `SwitchExpr` models `switch` expressions, for example `(a, b) switch { ... }`
28+
- Classes `IsConstantExpr`, `IsTypeExpr` and `IsPatternExpr` are deprecated in favour of `IsExpr`
29+
- Class `Switch` models both `SwitchExpr` and `SwitchStmt`
30+
- Class `Case` models both `CaseStmt` and `SwitchCaseExpr`
1731

1832
## Changes to autobuilder

csharp/extractor/Semmle.Extraction.CSharp/Entities/Expression.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,10 @@ public static CallType GetCallType(Context cx, ExpressionSyntax node)
163163
if (method.ContainingType != null && method.ContainingType.TypeKind == Microsoft.CodeAnalysis.TypeKind.Delegate)
164164
return CallType.UserOperator;
165165
return CallType.BuiltInOperator;
166+
case MethodKind.Constructor:
167+
// The index operator ^... generates a constructor call to System.Index.
168+
// Instead, treat this as a regular operator.
169+
return CallType.None;
166170
default:
167171
return CallType.UserOperator;
168172
}

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

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

47
namespace Semmle.Extraction.CSharp.Entities.Expressions
58
{
6-
class Discard : Expression<NameSyntax>
9+
class Discard : Expression
710
{
811
public Discard(ExpressionNodeInfo info) : base(info.SetKind(ExprKind.DISCARD))
912
{
1013
}
1114

12-
protected override void Populate()
15+
Discard(Context cx, CSharpSyntaxNode syntax, IExpressionParentEntity parent, int child) :
16+
base(new ExpressionInfo(cx, Type.Create(cx, cx.Model(syntax).GetTypeInfo(syntax).Type), cx.Create(syntax.GetLocation()), ExprKind.DISCARD, parent, child, false, null))
17+
{
18+
}
19+
20+
public Discard(Context cx, DiscardDesignationSyntax discard, IExpressionParentEntity parent, int child) : this(cx, (CSharpSyntaxNode)discard, parent, child)
21+
{
22+
}
23+
24+
public Discard(Context cx, DiscardPatternSyntax pattern, IExpressionParentEntity parent, int child) : this(cx, (CSharpSyntaxNode)pattern, parent, child)
1325
{
1426
}
1527
}

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,15 @@ internal static Expression Create(ExpressionNodeInfo info)
232232
case SyntaxKind.IsPatternExpression:
233233
return IsPattern.Create(info);
234234

235+
case SyntaxKind.RangeExpression:
236+
return RangeExpression.Create(info);
237+
238+
case SyntaxKind.IndexExpression:
239+
return Unary.Create(info.SetKind(ExprKind.INDEX));
240+
241+
case SyntaxKind.SwitchExpression:
242+
return Switch.Create(info);
243+
235244
default:
236245
info.Context.ModelError(info.Node, $"Unhandled expression '{info.Node}' of kind '{info.Node.Kind()}'");
237246
return new Unknown(info);

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

Lines changed: 121 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,123 @@
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);
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 symbol)
40+
{
41+
var type = Type.Create(cx, symbol.Type);
42+
43+
return VariableDeclaration.Create(cx, symbol, 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+
class PropertyPattern : Expression
63+
{
64+
internal PropertyPattern(Context cx, PropertyPatternClauseSyntax pp, IExpressionParentEntity parent, int child) :
65+
base(new ExpressionInfo(cx, Type.Create(cx, null), cx.Create(pp.GetLocation()), ExprKind.PROPERTY_PATTERN, parent, child, false, null))
66+
{
67+
child = 0;
68+
foreach (var sub in pp.Subpatterns)
69+
{
70+
var p = cx.CreatePattern(sub.Pattern, this, child++);
71+
cx.Emit(Tuples.exprorstmt_name(p, sub.NameColon.Name.ToString()));
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) :
100+
base(new ExpressionInfo(cx, Type.Create(cx, null), cx.Create(syntax.GetLocation()), ExprKind.RECURSIVE_PATTERN, parent, child, false, null))
101+
{
102+
// Extract the type access
103+
if (syntax.Type is TypeSyntax t)
104+
Expressions.TypeAccess.Create(cx, t, this, 1);
105+
106+
// Extract the local variable declaration
107+
if (syntax.Designation is VariableDesignationSyntax designation && cx.Model(syntax).GetDeclaredSymbol(designation) is ILocalSymbol symbol)
108+
{
109+
var type = Type.Create(cx, symbol.Type);
110+
111+
VariableDeclaration.Create(cx, symbol, type, cx.Create(syntax.GetLocation()), cx.Create(designation.GetLocation()), false, this, 0);
112+
}
113+
114+
if (syntax.PositionalPatternClause is PositionalPatternClauseSyntax posPc)
115+
{
116+
new PositionalPattern(cx, posPc, this, 2);
117+
}
118+
119+
if (syntax.PropertyPatternClause is PropertyPatternClauseSyntax pc)
120+
{
121+
new PropertyPattern(cx, pc, this, 3);
122+
}
123+
}
124+
}
125+
9126
class IsPattern : Expression<IsPatternExpressionSyntax>
10127
{
11128
private IsPattern(ExpressionNodeInfo info) : base(info.SetKind(ExprKind.IS))
@@ -18,7 +135,7 @@ private void PopulatePattern(PatternSyntax pattern, TypeSyntax optionalType, Syn
18135
if (!isVar)
19136
Expressions.TypeAccess.Create(cx, optionalType, this, 1);
20137

21-
if (cx.Model(pattern).GetDeclaredSymbol(designation) is ILocalSymbol symbol)
138+
if (!(designation is null) && cx.Model(pattern).GetDeclaredSymbol(designation) is ILocalSymbol symbol)
22139
{
23140
var type = Type.Create(cx, symbol.Type);
24141

@@ -43,6 +160,9 @@ protected override void Populate()
43160
case DeclarationPatternSyntax declPattern:
44161
PopulatePattern(declPattern, declPattern.Type, default(SyntaxToken), declPattern.Designation);
45162
return;
163+
case RecursivePatternSyntax recPattern:
164+
new RecursivePattern(cx, recPattern, this, 3);
165+
return;
46166
default:
47167
throw new InternalError(Syntax, "Is pattern not handled");
48168
}
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+
}

0 commit comments

Comments
 (0)