Skip to content

Commit 032a6b1

Browse files
authored
Merge pull request #1375 from hvitved/csharp/switch-expr-guard
C#: Switch expression guards
2 parents 79406f8 + c68dfb9 commit 032a6b1

File tree

8 files changed

+151
-9
lines changed

8 files changed

+151
-9
lines changed

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

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -70,13 +70,13 @@ module AbstractValues {
7070
}
7171

7272
/** An integer value. */
73-
class IntergerValue extends AbstractValue, TIntegerValue {
73+
class IntegerValue extends AbstractValue, TIntegerValue {
7474
/** Gets the underlying integer value. */
7575
int getValue() { this = TIntegerValue(result) }
7676

7777
override predicate branch(ControlFlowElement cfe, ConditionalSuccessor s, Expr e) { none() }
7878

79-
override BooleanValue getDualValue() { none() }
79+
override IntegerValue getDualValue() { none() }
8080

8181
override Expr getAnExpr() {
8282
result.getValue().toInt() = this.getValue() and
@@ -199,7 +199,9 @@ class DereferenceableExpr extends Expr {
199199
// incorrectly `int`, while it should have been `int?`. We apply
200200
// `getNullEquivParent()` as a workaround
201201
this = getNullEquivParent*(e) and
202-
t = e.getType()
202+
t = e.getType() and
203+
not this instanceof SwitchCaseExpr and
204+
not this instanceof PatternExpr
203205
|
204206
t instanceof NullableType and
205207
isNullableType = true
@@ -734,6 +736,8 @@ module Internal {
734736
Guard() {
735737
this.getType() instanceof BoolType and
736738
not this instanceof BoolLiteral and
739+
not this instanceof SwitchCaseExpr and
740+
not this instanceof PatternExpr and
737741
val = TBooleanValue(_)
738742
or
739743
this instanceof DereferenceableExpr and
@@ -1037,9 +1041,9 @@ module Internal {
10371041
ck.isInequality() and branch = false
10381042
)
10391043
or
1040-
result = any(PatternMatch pm |
1041-
pm.getExpr() = e1 and
1042-
e2 = pm.getPattern().(ConstantPatternExpr) and
1044+
result = any(IsExpr ie |
1045+
ie.getExpr() = e1 and
1046+
e2 = ie.getPattern().(ConstantPatternExpr) and
10431047
branch = true
10441048
)
10451049
)
@@ -1068,11 +1072,11 @@ module Internal {
10681072
* then `o` is guaranteed to be equal to `""`.
10691073
*/
10701074
private Expr getAMatchingEqualityCheck(Expr e1, MatchValue v, Expr e2) {
1071-
exists(Switch s, ConstCase case | case = v.getCase() |
1075+
exists(Switch s, Case case | case = v.getCase() |
10721076
e1 = s.getExpr() and
10731077
result = e1 and
10741078
case = s.getACase() and
1075-
e2 = case.getPattern() and
1079+
e2 = case.getPattern().(ConstantPatternExpr) and
10761080
v.isMatch()
10771081
)
10781082
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
// semmle-extractor-options: --cil
1+
// semmle-extractor-options: --cil /langversion:8.0

csharp/ql/test/library-tests/controlflow/guards/GuardedControlFlowNode.expected

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,22 @@
168168
| Guards.cs:269:11:269:12 | access to parameter o1 | Guards.cs:268:13:268:41 | call to operator == | Guards.cs:268:13:268:14 | access to parameter o1 | true |
169169
| Guards.cs:269:11:269:12 | access to parameter o1 | Guards.cs:268:16:268:25 | call to method GetType | Guards.cs:268:13:268:14 | access to parameter o1 | non-null |
170170
| Guards.cs:271:11:271:12 | access to parameter o1 | Guards.cs:270:13:270:42 | call to operator == | Guards.cs:270:13:270:14 | access to parameter o1 | true |
171+
| Guards.cs:279:17:279:17 | access to parameter o | Guards.cs:276:16:276:16 | access to parameter o | Guards.cs:276:16:276:16 | access to parameter o | match Action<Object> a |
172+
| Guards.cs:279:17:279:17 | access to parameter o | Guards.cs:276:16:276:16 | access to parameter o | Guards.cs:276:16:276:16 | access to parameter o | non-null |
173+
| Guards.cs:283:17:283:17 | access to parameter o | Guards.cs:276:16:276:16 | access to parameter o | Guards.cs:276:16:276:16 | access to parameter o | match "" |
174+
| Guards.cs:283:17:283:17 | access to parameter o | Guards.cs:276:16:276:16 | access to parameter o | Guards.cs:276:16:276:16 | access to parameter o | non-match Action<Object> a |
175+
| Guards.cs:283:17:283:17 | access to parameter o | Guards.cs:276:16:276:16 | access to parameter o | Guards.cs:276:16:276:16 | access to parameter o | non-match Action<String> a |
176+
| Guards.cs:283:17:283:17 | access to parameter o | Guards.cs:276:16:276:16 | access to parameter o | Guards.cs:276:16:276:16 | access to parameter o | non-null |
177+
| Guards.cs:285:17:285:17 | access to parameter o | Guards.cs:276:16:276:16 | access to parameter o | Guards.cs:276:16:276:16 | access to parameter o | match null |
178+
| Guards.cs:285:17:285:17 | access to parameter o | Guards.cs:276:16:276:16 | access to parameter o | Guards.cs:276:16:276:16 | access to parameter o | non-match "" |
179+
| Guards.cs:285:17:285:17 | access to parameter o | Guards.cs:276:16:276:16 | access to parameter o | Guards.cs:276:16:276:16 | access to parameter o | non-match Action<Object> a |
180+
| Guards.cs:285:17:285:17 | access to parameter o | Guards.cs:276:16:276:16 | access to parameter o | Guards.cs:276:16:276:16 | access to parameter o | non-match Action<String> a |
181+
| Guards.cs:285:17:285:17 | access to parameter o | Guards.cs:276:16:276:16 | access to parameter o | Guards.cs:276:16:276:16 | access to parameter o | null |
182+
| Guards.cs:287:17:287:17 | access to parameter o | Guards.cs:276:16:276:16 | access to parameter o | Guards.cs:276:16:276:16 | access to parameter o | non-match "" |
183+
| Guards.cs:287:17:287:17 | access to parameter o | Guards.cs:276:16:276:16 | access to parameter o | Guards.cs:276:16:276:16 | access to parameter o | non-match Action<Object> a |
184+
| Guards.cs:287:17:287:17 | access to parameter o | Guards.cs:276:16:276:16 | access to parameter o | Guards.cs:276:16:276:16 | access to parameter o | non-match Action<String> a |
185+
| Guards.cs:287:17:287:17 | access to parameter o | Guards.cs:276:16:276:16 | access to parameter o | Guards.cs:276:16:276:16 | access to parameter o | non-match null |
186+
| Guards.cs:287:17:287:17 | access to parameter o | Guards.cs:276:16:276:16 | access to parameter o | Guards.cs:276:16:276:16 | access to parameter o | non-null |
171187
| Splitting.cs:13:17:13:17 | [b (line 9): true] access to parameter o | Splitting.cs:12:17:12:17 | access to parameter o | Splitting.cs:12:17:12:17 | access to parameter o | non-null |
172188
| Splitting.cs:13:17:13:17 | [b (line 9): true] access to parameter o | Splitting.cs:12:17:12:25 | ... != ... | Splitting.cs:12:17:12:17 | access to parameter o | true |
173189
| Splitting.cs:14:13:14:13 | [b (line 9): false] access to parameter b | Splitting.cs:11:13:11:13 | access to parameter b | Splitting.cs:11:13:11:13 | access to parameter b | false |

csharp/ql/test/library-tests/controlflow/guards/GuardedExpr.expected

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,22 @@
168168
| Guards.cs:269:11:269:12 | access to parameter o1 | Guards.cs:268:13:268:41 | call to operator == | Guards.cs:268:13:268:14 | access to parameter o1 | true |
169169
| Guards.cs:269:11:269:12 | access to parameter o1 | Guards.cs:268:16:268:25 | call to method GetType | Guards.cs:268:13:268:14 | access to parameter o1 | non-null |
170170
| Guards.cs:271:11:271:12 | access to parameter o1 | Guards.cs:270:13:270:42 | call to operator == | Guards.cs:270:13:270:14 | access to parameter o1 | true |
171+
| Guards.cs:279:17:279:17 | access to parameter o | Guards.cs:276:16:276:16 | access to parameter o | Guards.cs:276:16:276:16 | access to parameter o | match Action<Object> a |
172+
| Guards.cs:279:17:279:17 | access to parameter o | Guards.cs:276:16:276:16 | access to parameter o | Guards.cs:276:16:276:16 | access to parameter o | non-null |
173+
| Guards.cs:283:17:283:17 | access to parameter o | Guards.cs:276:16:276:16 | access to parameter o | Guards.cs:276:16:276:16 | access to parameter o | match "" |
174+
| Guards.cs:283:17:283:17 | access to parameter o | Guards.cs:276:16:276:16 | access to parameter o | Guards.cs:276:16:276:16 | access to parameter o | non-match Action<Object> a |
175+
| Guards.cs:283:17:283:17 | access to parameter o | Guards.cs:276:16:276:16 | access to parameter o | Guards.cs:276:16:276:16 | access to parameter o | non-match Action<String> a |
176+
| Guards.cs:283:17:283:17 | access to parameter o | Guards.cs:276:16:276:16 | access to parameter o | Guards.cs:276:16:276:16 | access to parameter o | non-null |
177+
| Guards.cs:285:17:285:17 | access to parameter o | Guards.cs:276:16:276:16 | access to parameter o | Guards.cs:276:16:276:16 | access to parameter o | match null |
178+
| Guards.cs:285:17:285:17 | access to parameter o | Guards.cs:276:16:276:16 | access to parameter o | Guards.cs:276:16:276:16 | access to parameter o | non-match "" |
179+
| Guards.cs:285:17:285:17 | access to parameter o | Guards.cs:276:16:276:16 | access to parameter o | Guards.cs:276:16:276:16 | access to parameter o | non-match Action<Object> a |
180+
| Guards.cs:285:17:285:17 | access to parameter o | Guards.cs:276:16:276:16 | access to parameter o | Guards.cs:276:16:276:16 | access to parameter o | non-match Action<String> a |
181+
| Guards.cs:285:17:285:17 | access to parameter o | Guards.cs:276:16:276:16 | access to parameter o | Guards.cs:276:16:276:16 | access to parameter o | null |
182+
| Guards.cs:287:17:287:17 | access to parameter o | Guards.cs:276:16:276:16 | access to parameter o | Guards.cs:276:16:276:16 | access to parameter o | non-match "" |
183+
| Guards.cs:287:17:287:17 | access to parameter o | Guards.cs:276:16:276:16 | access to parameter o | Guards.cs:276:16:276:16 | access to parameter o | non-match Action<Object> a |
184+
| Guards.cs:287:17:287:17 | access to parameter o | Guards.cs:276:16:276:16 | access to parameter o | Guards.cs:276:16:276:16 | access to parameter o | non-match Action<String> a |
185+
| Guards.cs:287:17:287:17 | access to parameter o | Guards.cs:276:16:276:16 | access to parameter o | Guards.cs:276:16:276:16 | access to parameter o | non-match null |
186+
| Guards.cs:287:17:287:17 | access to parameter o | Guards.cs:276:16:276:16 | access to parameter o | Guards.cs:276:16:276:16 | access to parameter o | non-null |
171187
| Splitting.cs:13:17:13:17 | access to parameter o | Splitting.cs:12:17:12:17 | access to parameter o | Splitting.cs:12:17:12:17 | access to parameter o | non-null |
172188
| Splitting.cs:13:17:13:17 | access to parameter o | Splitting.cs:12:17:12:25 | ... != ... | Splitting.cs:12:17:12:17 | access to parameter o | true |
173189
| Splitting.cs:23:24:23:24 | access to parameter o | Splitting.cs:22:17:22:17 | access to parameter o | Splitting.cs:22:17:22:17 | access to parameter o | non-null |

csharp/ql/test/library-tests/controlflow/guards/Guards.cs

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,4 +270,69 @@ void M22(object o1, object o2)
270270
if (o1?.GetType() == o2?.GetType())
271271
o1.ToString(); // not null guarded
272272
}
273+
274+
string M23(object o)
275+
{
276+
return o switch
277+
{
278+
Action<object> a =>
279+
o.ToString(), // null guarded
280+
Action<string> a =>
281+
a.ToString(), // not null (but not a guard)
282+
"" =>
283+
o.ToString(), // null guarded
284+
null =>
285+
o.ToString(), // not null guarded
286+
_ =>
287+
o.ToString() // null guarded
288+
};
289+
}
290+
291+
int M24(bool b1)
292+
{
293+
var b2 = true;
294+
if (b1)
295+
b2 = false;
296+
return b2 switch
297+
{
298+
true => 0,
299+
_ => 1
300+
};
301+
}
302+
303+
int M25(bool b1)
304+
{
305+
var b2 = false;
306+
if (b1)
307+
b2 = true;
308+
return b2 switch
309+
{
310+
true => 0,
311+
_ => 1
312+
};
313+
}
314+
315+
int M26(bool b)
316+
{
317+
var i = 0;
318+
if (b)
319+
i = 1;
320+
return i switch
321+
{
322+
1 => 0,
323+
_ => 1
324+
};
325+
}
326+
327+
int M27(bool b)
328+
{
329+
var e = E.A;
330+
if (b)
331+
e = E.B;
332+
return e switch
333+
{
334+
E.B => 0,
335+
_ => 1
336+
};
337+
}
273338
}

csharp/ql/test/library-tests/controlflow/guards/Implications.expected

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,32 @@
349349
| Guards.cs:268:16:268:25 | call to method GetType | non-null | Guards.cs:268:13:268:14 | access to parameter o1 | non-null |
350350
| Guards.cs:270:16:270:25 | call to method GetType | non-null | Guards.cs:270:13:270:14 | access to parameter o1 | non-null |
351351
| Guards.cs:270:33:270:42 | call to method GetType | non-null | Guards.cs:270:30:270:31 | access to parameter o2 | non-null |
352+
| Guards.cs:276:16:276:16 | access to parameter o | match "" | Guards.cs:276:16:276:16 | access to parameter o | non-null |
353+
| Guards.cs:276:16:276:16 | access to parameter o | match Action<Object> a | Guards.cs:276:16:276:16 | access to parameter o | non-null |
354+
| Guards.cs:276:16:276:16 | access to parameter o | match Action<String> a | Guards.cs:276:16:276:16 | access to parameter o | non-null |
355+
| Guards.cs:276:16:276:16 | access to parameter o | match null | Guards.cs:276:16:276:16 | access to parameter o | null |
356+
| Guards.cs:276:16:276:16 | access to parameter o | non-match null | Guards.cs:276:16:276:16 | access to parameter o | non-null |
357+
| Guards.cs:281:17:281:17 | access to local variable a | non-null | Guards.cs:276:16:276:16 | access to parameter o | non-null |
358+
| Guards.cs:281:17:281:17 | access to local variable a | null | Guards.cs:276:16:276:16 | access to parameter o | null |
359+
| Guards.cs:293:13:293:21 | Boolean b2 = ... | false | Guards.cs:293:13:293:14 | access to local variable b2 | false |
360+
| Guards.cs:293:13:293:21 | Boolean b2 = ... | true | Guards.cs:293:13:293:14 | access to local variable b2 | true |
361+
| Guards.cs:295:13:295:22 | ... = ... | false | Guards.cs:295:13:295:14 | access to local variable b2 | false |
362+
| Guards.cs:295:13:295:22 | ... = ... | true | Guards.cs:295:13:295:14 | access to local variable b2 | true |
363+
| Guards.cs:296:16:296:17 | access to local variable b2 | match true | Guards.cs:294:13:294:14 | access to parameter b1 | false |
364+
| Guards.cs:296:16:296:17 | access to local variable b2 | match true | Guards.cs:296:16:296:17 | access to local variable b2 | true |
365+
| Guards.cs:305:13:305:22 | Boolean b2 = ... | false | Guards.cs:305:13:305:14 | access to local variable b2 | false |
366+
| Guards.cs:305:13:305:22 | Boolean b2 = ... | true | Guards.cs:305:13:305:14 | access to local variable b2 | true |
367+
| Guards.cs:307:13:307:21 | ... = ... | false | Guards.cs:307:13:307:14 | access to local variable b2 | false |
368+
| Guards.cs:307:13:307:21 | ... = ... | true | Guards.cs:307:13:307:14 | access to local variable b2 | true |
369+
| Guards.cs:308:16:308:17 | access to local variable b2 | match true | Guards.cs:306:13:306:14 | access to parameter b1 | true |
370+
| Guards.cs:308:16:308:17 | access to local variable b2 | match true | Guards.cs:308:16:308:17 | access to local variable b2 | true |
371+
| Guards.cs:308:16:308:17 | access to local variable b2 | non-match true | Guards.cs:306:13:306:14 | access to parameter b1 | false |
372+
| Guards.cs:320:16:320:16 | access to local variable i | match 1 | Guards.cs:318:13:318:13 | access to parameter b | true |
373+
| Guards.cs:320:16:320:16 | access to local variable i | match 1 | Guards.cs:320:16:320:16 | access to local variable i | 1 |
374+
| Guards.cs:320:16:320:16 | access to local variable i | non-match 1 | Guards.cs:318:13:318:13 | access to parameter b | false |
375+
| Guards.cs:332:16:332:16 | access to local variable e | match access to constant B | Guards.cs:330:13:330:13 | access to parameter b | true |
376+
| Guards.cs:332:16:332:16 | access to local variable e | match access to constant B | Guards.cs:332:16:332:16 | access to local variable e | 1 |
377+
| Guards.cs:332:16:332:16 | access to local variable e | non-match access to constant B | Guards.cs:330:13:330:13 | access to parameter b | false |
352378
| Splitting.cs:12:17:12:25 | ... != ... | false | Splitting.cs:12:17:12:17 | access to parameter o | null |
353379
| Splitting.cs:12:17:12:25 | ... != ... | true | Splitting.cs:12:17:12:17 | access to parameter o | non-null |
354380
| Splitting.cs:22:17:22:25 | ... != ... | false | Splitting.cs:22:17:22:17 | access to parameter o | null |

csharp/ql/test/library-tests/controlflow/guards/MatchingGuardedExpr.expected

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,15 @@
1010
| Guards.cs:162:24:162:24 | access to parameter o | Guards.cs:151:17:151:17 | access to parameter o | Guards.cs:155:13:155:34 | case ...: | non-match Action<String> a | false |
1111
| Guards.cs:162:24:162:24 | access to parameter o | Guards.cs:151:17:151:17 | access to parameter o | Guards.cs:157:13:157:20 | case ...: | non-match "" | false |
1212
| Guards.cs:162:24:162:24 | access to parameter o | Guards.cs:151:17:151:17 | access to parameter o | Guards.cs:159:13:159:22 | case ...: | non-match null | false |
13+
| Guards.cs:279:17:279:17 | access to parameter o | Guards.cs:276:16:276:16 | access to parameter o | Guards.cs:278:13:279:28 | ... => ... | match Action<Object> a | true |
14+
| Guards.cs:283:17:283:17 | access to parameter o | Guards.cs:276:16:276:16 | access to parameter o | Guards.cs:278:13:279:28 | ... => ... | non-match Action<Object> a | false |
15+
| Guards.cs:283:17:283:17 | access to parameter o | Guards.cs:276:16:276:16 | access to parameter o | Guards.cs:280:13:281:28 | ... => ... | non-match Action<String> a | false |
16+
| Guards.cs:283:17:283:17 | access to parameter o | Guards.cs:276:16:276:16 | access to parameter o | Guards.cs:282:13:283:28 | ... => ... | match "" | true |
17+
| Guards.cs:285:17:285:17 | access to parameter o | Guards.cs:276:16:276:16 | access to parameter o | Guards.cs:278:13:279:28 | ... => ... | non-match Action<Object> a | false |
18+
| Guards.cs:285:17:285:17 | access to parameter o | Guards.cs:276:16:276:16 | access to parameter o | Guards.cs:280:13:281:28 | ... => ... | non-match Action<String> a | false |
19+
| Guards.cs:285:17:285:17 | access to parameter o | Guards.cs:276:16:276:16 | access to parameter o | Guards.cs:282:13:283:28 | ... => ... | non-match "" | false |
20+
| Guards.cs:285:17:285:17 | access to parameter o | Guards.cs:276:16:276:16 | access to parameter o | Guards.cs:284:13:285:28 | ... => ... | match null | true |
21+
| Guards.cs:287:17:287:17 | access to parameter o | Guards.cs:276:16:276:16 | access to parameter o | Guards.cs:278:13:279:28 | ... => ... | non-match Action<Object> a | false |
22+
| Guards.cs:287:17:287:17 | access to parameter o | Guards.cs:276:16:276:16 | access to parameter o | Guards.cs:280:13:281:28 | ... => ... | non-match Action<String> a | false |
23+
| Guards.cs:287:17:287:17 | access to parameter o | Guards.cs:276:16:276:16 | access to parameter o | Guards.cs:282:13:283:28 | ... => ... | non-match "" | false |
24+
| Guards.cs:287:17:287:17 | access to parameter o | Guards.cs:276:16:276:16 | access to parameter o | Guards.cs:284:13:285:28 | ... => ... | non-match null | false |

csharp/ql/test/library-tests/controlflow/guards/NullGuardedExpr.expected

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@
4242
| Guards.cs:205:13:205:13 | access to parameter o |
4343
| Guards.cs:208:17:208:17 | access to parameter o |
4444
| Guards.cs:269:11:269:12 | access to parameter o1 |
45+
| Guards.cs:279:17:279:17 | access to parameter o |
46+
| Guards.cs:283:17:283:17 | access to parameter o |
47+
| Guards.cs:287:17:287:17 | access to parameter o |
4548
| Splitting.cs:13:17:13:17 | access to parameter o |
4649
| Splitting.cs:23:24:23:24 | access to parameter o |
4750
| Splitting.cs:35:13:35:13 | access to parameter o |

0 commit comments

Comments
 (0)