Skip to content

Commit 51d6858

Browse files
authored
Merge pull request #1392 from calumgrant/cs/cs8/static-using-null
C#: More C# 8 features
2 parents a6da499 + c88359b commit 51d6858

39 files changed

+4087
-51
lines changed

change-notes/1.21/analysis-csharp.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,18 @@
1313
* The following C# 8 features are now extracted:
1414
- Range expressions
1515
- Recursive patterns
16+
- Using declaration statements
17+
- `static` modifiers on local functions
18+
- Null-coalescing assignment expressions
19+
1620
* The `unmanaged` type parameter constraint is now extracted.
1721

1822
## Changes to QL libraries
1923

2024
* 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.
2125
* The class `TypeParameterConstraints` has a new predicate `hasUnmanagedTypeConstraint()`, indicating that the type parameter has the `unmanaged` constraint.
2226
* The following QL classes have been added to model C# 8 features:
27+
- Class `AssignCoalesceExpr` models null-coalescing assignment, for example `x ??= y`
2328
- Class `IndexExpr` models from-end index expressions, for example `^1`
2429
- Class `PatternExpr` is an `Expr` that appears in a pattern. It has the new subclasses `DiscardPatternExpr`, `LabeledPatternExpr`, `RecursivePatternExpr`, `TypeAccessPatternExpr`, `TypePatternExpr`, and `VariablePatternExpr`.
2530
- Class `PatternMatch` models a pattern being matched. It has the subclasses `Case` and `IsExpr`.
@@ -31,5 +36,6 @@
3136
- Classes `IsConstantExpr`, `IsTypeExpr` and `IsPatternExpr` are deprecated in favour of `IsExpr`
3237
- Class `Switch` models both `SwitchExpr` and `SwitchStmt`
3338
- Class `Case` models both `CaseStmt` and `SwitchCaseExpr`
39+
- Class `UsingStmt` models both `UsingBlockStmt` and `UsingDeclStmt`
3440

3541
## Changes to autobuilder

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@ static ExprKind GetAssignmentOperation(Context cx, AssignmentExpressionSyntax sy
7171
return ExprKind.ASSIGN_LSHIFT;
7272
case SyntaxKind.GreaterThanGreaterThanEqualsToken:
7373
return ExprKind.ASSIGN_RSHIFT;
74+
case SyntaxKind.QuestionQuestionEqualsToken:
75+
return ExprKind.ASSIGN_COALESCE;
7476
default:
7577
cx.ModelError(syntax, "Unrecognised assignment type " + GetKind(cx, syntax));
7678
return ExprKind.UNKNOWN;
@@ -142,6 +144,8 @@ static ExprKind GetKind(Context cx, AssignmentExpressionSyntax syntax)
142144
return ExprKind.SUB;
143145
case ExprKind.ASSIGN_XOR:
144146
return ExprKind.BIT_XOR;
147+
case ExprKind.ASSIGN_COALESCE:
148+
return ExprKind.NULL_COALESCING;
145149
default:
146150
cx.ModelError(Syntax, "Couldn't unfold assignment of type " + kind);
147151
return ExprKind.UNKNOWN;

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ internal static Expression Create(ExpressionNodeInfo info)
8080
case SyntaxKind.RightShiftAssignmentExpression:
8181
case SyntaxKind.DivideAssignmentExpression:
8282
case SyntaxKind.ModuloAssignmentExpression:
83+
case SyntaxKind.CoalesceAssignmentExpression:
8384
return Assignment.Create(info);
8485

8586
case SyntaxKind.ObjectCreationExpression:

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
using Microsoft.CodeAnalysis;
2+
using Microsoft.CodeAnalysis.CSharp.Syntax;
3+
using System.Linq;
24

35
namespace Semmle.Extraction.CSharp.Entities
46
{
@@ -39,6 +41,18 @@ class LocalFunctionFactory : ICachedEntityFactory<IMethodSymbol, LocalFunction>
3941
public override void Populate()
4042
{
4143
PopulateMethod();
44+
45+
// There is a "bug" in Roslyn whereby the IMethodSymbol associated with the local function symbol
46+
// is always static, so we need to go to the syntax reference of the local function to see whether
47+
// the "static" modifier is present.
48+
if (symbol.DeclaringSyntaxReferences.SingleOrDefault().GetSyntax() is LocalFunctionStatementSyntax fn)
49+
{
50+
foreach(var modifier in fn.Modifiers)
51+
{
52+
Modifier.HasModifier(Context, this, modifier.Text);
53+
}
54+
}
55+
4256
var originalDefinition = IsSourceDeclaration ? this : Create(Context, symbol.OriginalDefinition);
4357
var returnType = Type.Create(Context, symbol.ReturnType);
4458
Context.Emit(Tuples.local_functions(this, symbol.Name, returnType, originalDefinition));

csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/LocalDeclaration.cs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,19 @@ namespace Semmle.Extraction.CSharp.Entities.Statements
66
{
77
class LocalDeclaration : Statement<LocalDeclarationStatementSyntax>
88
{
9+
static StmtKind GetKind(LocalDeclarationStatementSyntax declStmt)
10+
{
11+
if (declStmt.UsingKeyword.RawKind != 0)
12+
return StmtKind.USING_DECL;
13+
14+
if (declStmt.IsConst)
15+
return StmtKind.CONST_DECL;
16+
17+
return StmtKind.VAR_DECL;
18+
}
19+
920
LocalDeclaration(Context cx, LocalDeclarationStatementSyntax declStmt, IStatementParentEntity parent, int child)
10-
: base(cx, declStmt, declStmt.IsConst ? StmtKind.CONST_DECL : StmtKind.VAR_DECL, parent, child) { }
21+
: base(cx, declStmt, GetKind(declStmt), parent, child) { }
1122

1223
public static LocalDeclaration Create(Context cx, LocalDeclarationStatementSyntax node, IStatementParentEntity parent, int child)
1324
{

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ public enum ExprKind
112112
RECURSIVE_PATTERN = 115,
113113
PROPERTY_PATTERN = 116,
114114
POSITIONAL_PATTERN = 117,
115-
SWITCH_CASE = 118
115+
SWITCH_CASE = 118,
116+
ASSIGN_COALESCE = 119
116117
}
117118
}

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ public enum StmtKind
3434
LABEL = 27,
3535
CATCH = 28,
3636
CASE = 29,
37-
LOCAL_FUNCTION = 30
37+
LOCAL_FUNCTION = 30,
38+
USING_DECL = 31
3839
}
3940
}

csharp/ql/src/semmle/code/csharp/Callable.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -903,7 +903,7 @@ class ExplicitConversionOperator extends ConversionOperator {
903903
* }
904904
* ```
905905
*/
906-
class LocalFunction extends Callable, @local_function {
906+
class LocalFunction extends Callable, Modifiable, @local_function {
907907
override string getName() { local_functions(this, result, _, _) }
908908

909909
override LocalFunction getSourceDeclaration() { local_functions(this, _, _, result) }

csharp/ql/src/semmle/code/csharp/Stmt.qll

Lines changed: 83 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1115,40 +1115,16 @@ class LockStmt extends Stmt, @lock_stmt {
11151115
}
11161116

11171117
/**
1118-
* A `using` statement, for example
1119-
*
1120-
* ```
1121-
* using (FileStream f = File.Open("settings.xml")) {
1122-
* ...
1123-
* }
1124-
* ```
1118+
* A using block or declaration. Either a using declaration (`UsingDeclStmt`) or
1119+
* a using block (`UsingBlockStmt`).
11251120
*/
11261121
class UsingStmt extends Stmt, @using_stmt {
1127-
/** Gets the `i`th local variable of this `using` statement. */
1128-
LocalVariable getVariable(int i) { result = this.getVariableDeclExpr(i).getVariable() }
1129-
1130-
/** Gets a local variable of this `using` statement. */
1131-
LocalVariable getAVariable() { result = this.getVariable(_) }
1132-
11331122
/** Gets the `i`th local variable declaration of this `using` statement. */
1134-
LocalVariableDeclExpr getVariableDeclExpr(int i) { result = this.getChild(-i - 1) }
1123+
LocalVariableDeclExpr getVariableDeclExpr(int i) { none() }
11351124

11361125
/** Gets a local variable declaration of this `using` statement. */
11371126
LocalVariableDeclExpr getAVariableDeclExpr() { result = this.getVariableDeclExpr(_) }
11381127

1139-
/**
1140-
* Gets the expression directly used by this `using` statement, if any. For
1141-
* example, `f` on line 2 in
1142-
*
1143-
* ```
1144-
* var f = File.Open("settings.xml");
1145-
* using (f) {
1146-
* ...
1147-
* }
1148-
* ```
1149-
*/
1150-
Expr getExpr() { result = this.getChild(0) }
1151-
11521128
/**
11531129
* Gets an expression that is used in this `using` statement. Either an
11541130
* expression assigned to a variable, for example `File.Open("settings.xml")`
@@ -1169,14 +1145,69 @@ class UsingStmt extends Stmt, @using_stmt {
11691145
* }
11701146
* ```
11711147
*/
1172-
Expr getAnExpr() {
1148+
Expr getAnExpr() { none() }
1149+
1150+
/**
1151+
* DEPRECATED: Use UsingBlockStmt.getExpr() instead.
1152+
* Gets the expression directly used by this `using` statement, if any. For
1153+
* example, `f` on line 2 in
1154+
*
1155+
* ```
1156+
* var f = File.Open("settings.xml");
1157+
* using (f) {
1158+
* ...
1159+
* }
1160+
* ```
1161+
*/
1162+
deprecated Expr getExpr() { none() }
1163+
1164+
/**
1165+
* DEPRECATED: Use UsingBlockStmt.getBody() instead.
1166+
* Gets the body of this `using` statement.
1167+
*/
1168+
deprecated Stmt getBody() { none() }
1169+
}
1170+
1171+
/**
1172+
* A `using` block statement, for example
1173+
*
1174+
* ```
1175+
* using (FileStream f = File.Open("settings.xml")) {
1176+
* ...
1177+
* }
1178+
* ```
1179+
*/
1180+
class UsingBlockStmt extends UsingStmt, @using_block_stmt {
1181+
/** Gets the `i`th local variable of this `using` statement. */
1182+
LocalVariable getVariable(int i) { result = this.getVariableDeclExpr(i).getVariable() }
1183+
1184+
/** Gets a local variable of this `using` statement. */
1185+
LocalVariable getAVariable() { result = this.getVariable(_) }
1186+
1187+
/** Gets the `i`th local variable declaration of this `using` statement. */
1188+
override LocalVariableDeclExpr getVariableDeclExpr(int i) { result = this.getChild(-i - 1) }
1189+
1190+
/**
1191+
* Gets the expression directly used by this `using` statement, if any. For
1192+
* example, `f` on line 2 in
1193+
*
1194+
* ```
1195+
* var f = File.Open("settings.xml");
1196+
* using (f) {
1197+
* ...
1198+
* }
1199+
* ```
1200+
*/
1201+
override Expr getExpr() { result = this.getChild(0) }
1202+
1203+
override Expr getAnExpr() {
11731204
result = this.getAVariableDeclExpr().getInitializer()
11741205
or
11751206
result = this.getExpr()
11761207
}
11771208

11781209
/** Gets the body of this `using` statement. */
1179-
Stmt getBody() { result.getParent() = this }
1210+
override Stmt getBody() { result.getParent() = this }
11801211

11811212
override string toString() { result = "using (...) {...}" }
11821213
}
@@ -1254,6 +1285,29 @@ class LocalConstantDeclStmt extends LocalVariableDeclStmt, @const_decl_stmt {
12541285
override string toString() { result = "const ... ...;" }
12551286
}
12561287

1288+
/**
1289+
* A `using` declaration statement, for example
1290+
*
1291+
* ```
1292+
* using FileStream f = File.Open("settings.xml");
1293+
* ```
1294+
*/
1295+
class UsingDeclStmt extends LocalVariableDeclStmt, UsingStmt, @using_decl_stmt {
1296+
override string toString() { result = "using ... ...;" }
1297+
1298+
override LocalVariableDeclExpr getAVariableDeclExpr() {
1299+
result = LocalVariableDeclStmt.super.getAVariableDeclExpr()
1300+
}
1301+
1302+
override LocalVariableDeclExpr getVariableDeclExpr(int n) {
1303+
result = LocalVariableDeclStmt.super.getVariableDeclExpr(n)
1304+
}
1305+
1306+
override Expr getAnExpr() {
1307+
result = this.getAVariableDeclExpr().getInitializer()
1308+
}
1309+
}
1310+
12571311
/**
12581312
* An empty statement, for example line 2 in
12591313
*

csharp/ql/src/semmle/code/csharp/controlflow/ControlFlowGraph.qll

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -468,7 +468,7 @@ module ControlFlow {
468468
override ControlFlowElement getChildElement(int i) {
469469
not this instanceof GeneralCatchClause and
470470
not this instanceof FixedStmt and
471-
not this instanceof UsingStmt and
471+
not this instanceof UsingBlockStmt and
472472
result = this.getChild(i)
473473
or
474474
this = any(GeneralCatchClause gcc | i = 0 and result = gcc.getBlock())
@@ -480,7 +480,7 @@ module ControlFlow {
480480
i = max(int j | exists(fs.getVariableDeclExpr(j))) + 1
481481
)
482482
or
483-
this = any(UsingStmt us |
483+
this = any(UsingBlockStmt us |
484484
if exists(us.getExpr())
485485
then (
486486
result = us.getExpr() and

0 commit comments

Comments
 (0)