Skip to content

Commit 323709b

Browse files
committed
C#: Generalize cs/constant-condition
1 parent f7a515c commit 323709b

File tree

9 files changed

+171
-119
lines changed

9 files changed

+171
-119
lines changed

csharp/ql/src/Bad Practices/Control-Flow/ConstantCondition.ql

Lines changed: 98 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -14,39 +14,111 @@
1414
import csharp
1515
import semmle.code.csharp.commons.Assertions
1616
import semmle.code.csharp.commons.Constants
17+
import ControlFlowGraph
1718

18-
/** A condition of an `if` statement or a conditional expression. */
19-
private class IfCondition extends Expr {
20-
IfCondition() {
21-
this = any(IfStmt is).getCondition() or
22-
this = any(ConditionalExpr ce).getCondition()
19+
/** A constant condition. */
20+
abstract class ConstantCondition extends Expr {
21+
/** Gets the alert message for this constant condition. */
22+
abstract string getMessage();
23+
24+
/** Holds if this constant condition is white-listed. */
25+
predicate isWhiteListed() { none() }
26+
}
27+
28+
/** A constant Boolean condition. */
29+
class ConstantBooleanCondition extends ConstantCondition {
30+
boolean b;
31+
32+
ConstantBooleanCondition() {
33+
isConstantCondition(this, b)
34+
}
35+
36+
override string getMessage() {
37+
result = "Condition always evaluates to '" + b + "'."
38+
}
39+
40+
override predicate isWhiteListed() {
41+
// E.g. `x ?? false`
42+
this.(BoolLiteral) = any(NullCoalescingExpr nce).getRightOperand()
43+
}
44+
}
45+
46+
/** A constant condition in an `if` statement or a conditional expression. */
47+
class ConstantIfCondition extends ConstantBooleanCondition {
48+
ConstantIfCondition() {
49+
this = any(IfStmt is).getCondition().getAChildExpr*() or
50+
this = any(ConditionalExpr ce).getCondition().getAChildExpr*()
51+
}
52+
53+
override predicate isWhiteListed() {
54+
ConstantBooleanCondition.super.isWhiteListed()
55+
or
56+
// It is a common pattern to use a local constant/constant field to control
57+
// whether code parts must be executed or not
58+
this instanceof AssignableRead
2359
}
2460
}
2561

26-
/** A loop condition */
27-
private class LoopCondition extends Expr {
28-
LoopCondition() {
62+
/** A constant loop condition. */
63+
class ConstantLoopCondition extends ConstantBooleanCondition {
64+
ConstantLoopCondition() {
2965
this = any(LoopStmt ls).getCondition()
3066
}
67+
68+
override predicate isWhiteListed() {
69+
// Clearly intentional infinite loops are allowed
70+
this.(BoolLiteral).getBoolValue() = true
71+
}
72+
}
73+
74+
/** A constant nullness condition. */
75+
class ConstantNullnessCondition extends ConstantCondition {
76+
boolean b;
77+
78+
ConstantNullnessCondition() {
79+
forex(ControlFlowNode cfn |
80+
cfn = this.getAControlFlowNode() |
81+
exists(ControlFlowEdgeNullness t |
82+
exists(cfn.getASuccessorByType(t)) |
83+
if t.isNull() then b = true else b = false
84+
) and
85+
strictcount(ControlFlowEdgeType t | exists(cfn.getASuccessorByType(t))) = 1
86+
)
87+
}
88+
89+
override string getMessage() {
90+
if b = true then
91+
result = "Expression is always 'null'."
92+
else
93+
result = "Expression is never 'null'."
94+
}
3195
}
3296

33-
/** Holds if `e` is a conditional expression that is allowed to be constant. */
34-
predicate isWhiteListed(Expr e) {
35-
// It is a common pattern to use a local constant/constant field to control
36-
// whether code parts must be executed or not
37-
e = any(IfCondition ic).getAChildExpr*() and
38-
e instanceof AssignableRead
39-
or
40-
// Clearly intentional infinite loops are allowed
41-
e instanceof LoopCondition and
42-
e.(BoolLiteral).getBoolValue() = true
43-
or
44-
// E.g. `x ?? false`
45-
e.(BoolLiteral) = any(NullCoalescingExpr nce).getRightOperand()
97+
/** A constant matching condition. */
98+
class ConstantMatchingCondition extends ConstantCondition {
99+
boolean b;
100+
101+
ConstantMatchingCondition() {
102+
forex(ControlFlowNode cfn |
103+
cfn = this.getAControlFlowNode() |
104+
exists(ControlFlowEdgeMatching t |
105+
exists(cfn.getASuccessorByType(t)) |
106+
if t.isMatch() then b = true else b = false
107+
) and
108+
strictcount(ControlFlowEdgeType t | exists(cfn.getASuccessorByType(t))) = 1
109+
)
110+
}
111+
112+
override string getMessage() {
113+
if b = true then
114+
result = "Pattern always matches."
115+
else
116+
result = "Pattern never matches."
117+
}
46118
}
47119

48-
from Expr e, boolean b
49-
where isConstantCondition(e, b)
50-
and not isWhiteListed(e)
51-
and not isExprInAssertion(e)
52-
select e, "Condition always evaluates to '" + b + "'."
120+
from ConstantCondition c, string msg
121+
where msg = c.getMessage()
122+
and not c.isWhiteListed()
123+
and not isExprInAssertion(c)
124+
select c, msg

csharp/ql/src/Bad Practices/Control-Flow/ConstantNullCoalescingLeftHandOperand.qhelp

Lines changed: 0 additions & 15 deletions
This file was deleted.

csharp/ql/src/Bad Practices/Control-Flow/ConstantNullCoalescingLeftHandOperand.ql

Lines changed: 0 additions & 16 deletions
This file was deleted.

csharp/ql/src/Bad Practices/Control-Flow/ConstantSwitchSelector.cs

Lines changed: 0 additions & 32 deletions
This file was deleted.

csharp/ql/src/Bad Practices/Control-Flow/ConstantSwitchSelector.qhelp

Lines changed: 0 additions & 14 deletions
This file was deleted.

csharp/ql/src/Bad Practices/Control-Flow/ConstantSwitchSelector.ql

Lines changed: 0 additions & 16 deletions
This file was deleted.

csharp/ql/test/query-tests/Bad Practices/Control-Flow/ConstantCondition/ConstantCondition.cs

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// semmle-extractor-options: /r:System.Threading.Thread.dll /r:System.Diagnostics.Debug.dll
22

33
using System;
4+
using System.Collections;
45
using System.Diagnostics;
56

67
class ConstantCondition
@@ -32,10 +33,71 @@ void M1(int x)
3233
bool M3(double d) => d == d; // BAD: but flagged by cs/constant-comparison
3334
}
3435

36+
class ConstantNullness
37+
{
38+
void M1(int i)
39+
{
40+
var j = ((string)null)?.Length; // BAD
41+
var s = ((int?)i)?.ToString(); // BAD
42+
var k = s?.Length; // GOOD
43+
k = s?.ToLower()?.Length; // GOOD
44+
}
45+
46+
void M2(int i)
47+
{
48+
var j = (int?)null ?? 0; // BAD
49+
var s = "" ?? "a"; // BAD
50+
j = (int?)i ?? 1; // BAD
51+
s = ""?.CommaJoinWith(s); // BAD
52+
s = s ?? ""; // GOOD
53+
}
54+
}
55+
56+
class ConstantMatching
57+
{
58+
void M1()
59+
{
60+
switch (1 + 2)
61+
{
62+
case 2 : // BAD
63+
break;
64+
case 3 : // BAD
65+
break;
66+
case int _ : // GOOD
67+
break;
68+
}
69+
}
70+
71+
void M2(string s)
72+
{
73+
switch ((object)s)
74+
{
75+
case int _ : // BAD
76+
break;
77+
case "" : // GOOD
78+
break;
79+
}
80+
}
81+
82+
void M3(object o)
83+
{
84+
switch (o)
85+
{
86+
case IList _ : // GOOD
87+
break;
88+
}
89+
}
90+
}
91+
3592
class Assertions
3693
{
3794
void F()
3895
{
3996
Debug.Assert(false ? false : true); // GOOD
4097
}
4198
}
99+
100+
static class Ext
101+
{
102+
public static string CommaJoinWith(this string s1, string s2) => s1 + ", " + s2;
103+
}

csharp/ql/test/query-tests/Bad Practices/Control-Flow/ConstantCondition/ConstantCondition.expected

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
1+
| ConstantCondition.cs:40:18:40:29 | (...) ... | Expression is always 'null'. |
2+
| ConstantCondition.cs:41:18:41:24 | (...) ... | Expression is never 'null'. |
3+
| ConstantCondition.cs:48:17:48:26 | (...) ... | Expression is always 'null'. |
4+
| ConstantCondition.cs:49:17:49:18 | "" | Expression is never 'null'. |
5+
| ConstantCondition.cs:50:13:50:19 | (...) ... | Expression is never 'null'. |
6+
| ConstantCondition.cs:51:13:51:14 | "" | Expression is never 'null'. |
7+
| ConstantCondition.cs:62:18:62:18 | 2 | Pattern never matches. |
8+
| ConstantCondition.cs:64:18:64:18 | 3 | Pattern always matches. |
9+
| ConstantCondition.cs:75:18:75:20 | access to type Int32 | Pattern never matches. |
110
| ConstantConditionBad.cs:5:16:5:20 | ... > ... | Condition always evaluates to 'false'. |
211
| ConstantConditionalExpressionCondition.cs:11:22:11:34 | ... == ... | Condition always evaluates to 'true'. |
312
| ConstantConditionalExpressionCondition.cs:12:21:12:25 | false | Condition always evaluates to 'false'. |
@@ -7,6 +16,8 @@
716
| ConstantIfCondition.cs:11:17:11:29 | ... == ... | Condition always evaluates to 'true'. |
817
| ConstantIfCondition.cs:14:17:14:21 | false | Condition always evaluates to 'false'. |
918
| ConstantIfCondition.cs:17:17:17:26 | ... == ... | Condition always evaluates to 'true'. |
19+
| ConstantNullCoalescingLeftHandOperand.cs:11:24:11:34 | access to constant NULL_OBJECT | Expression is never 'null'. |
20+
| ConstantNullCoalescingLeftHandOperand.cs:12:24:12:27 | null | Expression is always 'null'. |
1021
| ConstantWhileCondition.cs:12:20:12:32 | ... == ... | Condition always evaluates to 'true'. |
1122
| ConstantWhileCondition.cs:16:20:16:24 | false | Condition always evaluates to 'false'. |
1223
| ConstantWhileCondition.cs:24:20:24:29 | ... == ... | Condition always evaluates to 'true'. |

csharp/ql/src/Bad Practices/Control-Flow/ConstantNullCoalescingLeftHandOperand.cs renamed to csharp/ql/test/query-tests/Bad Practices/Control-Flow/ConstantCondition/ConstantNullCoalescingLeftHandOperand.cs

File renamed without changes.

0 commit comments

Comments
 (0)