Skip to content

Commit 89844db

Browse files
authored
Merge pull request #22708 from michaelnebel/csharp/improvesimplifyboolexpr
C#: Remove some FPs for `cs/simplifiable-boolean-expression`.
2 parents 6647e5f + 8ebc37c commit 89844db

4 files changed

Lines changed: 109 additions & 40 deletions

File tree

‎csharp/ql/src/Language Abuse/SimplifyBoolExpr.ql‎

Lines changed: 80 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -13,56 +13,88 @@
1313
import csharp
1414

1515
/**
16-
* Holds if expression `expr` has Boolean `value` at child `child`.
17-
* No other child nodes are boolean literals.
16+
* Holds if the left operand of a binary operation is a Boolean literal with the specified value
17+
* and the right operand is not a Boolean literal.
1818
*/
19-
predicate literalChild(Expr expr, int child, boolean value) {
20-
value = expr.getChild(child).(BoolLiteral).getBoolValue() and
21-
forall(int c | c != child | not expr.getChild(c) instanceof BoolLiteral)
19+
predicate binaryLiteralLeft(BinaryOperation op, boolean value) {
20+
value = op.getLeftOperand().(BoolLiteral).getBoolValue() and
21+
not op.getRightOperand() instanceof BoolLiteral
2222
}
2323

2424
/**
25-
* Expression `expr` has Boolean `value1` at child `child1`, and boolean `value2` at `child2`.
26-
* No other child nodes are boolean literals.
25+
* Holds if the right operand of a binary operation is a Boolean literal with the specified value
26+
* and the left operand is not a Boolean literal.
2727
*/
28-
predicate literalChildren(Expr expr, int child1, boolean value1, int child2, boolean value2) {
29-
value1 = expr.getChild(child1).(BoolLiteral).getBoolValue() and
30-
value2 = expr.getChild(child2).(BoolLiteral).getBoolValue() and
31-
forall(int c | c != child1 and c != child2 | not expr.getChild(c) instanceof BoolLiteral)
28+
predicate binaryLiteralRight(BinaryOperation op, boolean value) {
29+
value = op.getRightOperand().(BoolLiteral).getBoolValue() and
30+
not op.getLeftOperand() instanceof BoolLiteral
3231
}
3332

34-
predicate rewriteBinaryExpr(BinaryOperation op, boolean value, string oldPattern) {
35-
literalChild(op, 0, value) and oldPattern = value + " " + op.getOperator() + " A"
36-
or
37-
literalChild(op, 1, value) and oldPattern = "A " + op.getOperator() + " " + value
33+
/**
34+
* Holds if the 'then' branch of a conditional expression is a Boolean literal with the specified value
35+
* and the 'condition' or 'else' branch are not Boolean literals.
36+
*/
37+
predicate conditionalThenLiteral(ConditionalExpr cond, boolean value) {
38+
value = cond.getThen().(BoolLiteral).getBoolValue() and
39+
not cond.getCondition() instanceof BoolLiteral and
40+
not cond.getElse() instanceof BoolLiteral
3841
}
3942

40-
bindingset[withFalseOperand, withTrueOperand]
41-
predicate rewriteBinaryExpr(
42-
BinaryOperation op, string oldPattern, string withFalseOperand, string withTrueOperand,
43-
string newPattern
44-
) {
45-
rewriteBinaryExpr(op, false, oldPattern) and newPattern = withFalseOperand
46-
or
47-
rewriteBinaryExpr(op, true, oldPattern) and newPattern = withTrueOperand
43+
/**
44+
* Holds if the 'else' branch of a conditional expression is a Boolean literal with the specified value
45+
* and the 'condition' or 'then' branch are not Boolean literals.
46+
*/
47+
predicate conditionalElseLiteral(ConditionalExpr cond, boolean value) {
48+
value = cond.getElse().(BoolLiteral).getBoolValue() and
49+
not cond.getCondition() instanceof BoolLiteral and
50+
not cond.getThen() instanceof BoolLiteral
51+
}
52+
53+
/**
54+
* Holds if both the 'then' and 'else' branches of a conditional expression are Boolean literals with the specified values
55+
* and the 'condition' branch is not a Boolean literal.
56+
*/
57+
predicate conditionalThenAndElseLiteral(ConditionalExpr cond, boolean thenValue, boolean elseValue) {
58+
thenValue = cond.getThen().(BoolLiteral).getBoolValue() and
59+
elseValue = cond.getElse().(BoolLiteral).getBoolValue() and
60+
not cond.getCondition() instanceof BoolLiteral
61+
}
62+
63+
predicate rewriteBinaryExpr(BinaryOperation op, boolean value, string oldPattern) {
64+
op.getLeftOperand().getType() instanceof BoolType and
65+
op.getRightOperand().getType() instanceof BoolType and
66+
(
67+
binaryLiteralLeft(op, value) and oldPattern = value + " " + op.getOperator() + " A"
68+
or
69+
binaryLiteralRight(op, value) and oldPattern = "A " + op.getOperator() + " " + value
70+
)
4871
}
4972

5073
predicate rewriteConditionalExpr(ConditionalExpr cond, string oldPattern, string newPattern) {
51-
literalChild(cond, 1, false) and oldPattern = "A ? false : B" and newPattern = "!A && B"
52-
or
53-
literalChild(cond, 1, true) and oldPattern = "A ? true : B" and newPattern = "A || B"
54-
or
55-
literalChild(cond, 2, false) and oldPattern = "A ? B : false" and newPattern = "A && B"
56-
or
57-
literalChild(cond, 2, true) and oldPattern = "A ? B : true" and newPattern = "!A || B"
58-
or
59-
exists(boolean b | literalChildren(cond, 1, b, 2, b) |
60-
oldPattern = "A ? " + b + " : " + b and newPattern = b.toString()
74+
cond.getCondition().getType() instanceof BoolType and
75+
cond.getThen().getType() instanceof BoolType and
76+
cond.getElse().getType() instanceof BoolType and
77+
(
78+
conditionalThenLiteral(cond, false) and oldPattern = "A ? false : B" and newPattern = "!A && B"
79+
or
80+
conditionalThenLiteral(cond, true) and oldPattern = "A ? true : B" and newPattern = "A || B"
81+
or
82+
conditionalElseLiteral(cond, false) and oldPattern = "A ? B : false" and newPattern = "A && B"
83+
or
84+
conditionalElseLiteral(cond, true) and oldPattern = "A ? B : true" and newPattern = "!A || B"
85+
or
86+
exists(boolean b | conditionalThenAndElseLiteral(cond, b, b) |
87+
oldPattern = "A ? " + b + " : " + b and newPattern = b.toString()
88+
)
89+
or
90+
conditionalThenAndElseLiteral(cond, true, false) and
91+
oldPattern = "A ? true : false" and
92+
newPattern = "A"
93+
or
94+
conditionalThenAndElseLiteral(cond, false, true) and
95+
oldPattern = "A ? false : true" and
96+
newPattern = "!A"
6197
)
62-
or
63-
literalChildren(cond, 1, true, 2, false) and oldPattern = "A ? true : false" and newPattern = "A"
64-
or
65-
literalChildren(cond, 1, false, 2, true) and oldPattern = "A ? false : true" and newPattern = "!A"
6698
}
6799

68100
predicate negatedOperators(string op, string negated) {
@@ -115,12 +147,20 @@ predicate pushNegation(LogicalNotExpr expr, string oldPattern, string newPattern
115147
)
116148
}
117149

118-
predicate rewrite(Expr expr, string oldPattern, string newPattern) {
150+
predicate rewriteBinaryOperation(BinaryOperation op, string oldPattern, string newPattern) {
119151
exists(string withFalseOperand, string withTrueOperand |
120-
simplifyBinaryExpr(expr.(BinaryOperation).getOperator(), withFalseOperand, withTrueOperand)
152+
simplifyBinaryExpr(op.getOperator(), withFalseOperand, withTrueOperand)
121153
|
122-
rewriteBinaryExpr(expr, oldPattern, withFalseOperand, withTrueOperand, newPattern)
154+
rewriteBinaryExpr(op, false, oldPattern) and
155+
newPattern = withFalseOperand
156+
or
157+
rewriteBinaryExpr(op, true, oldPattern) and
158+
newPattern = withTrueOperand
123159
)
160+
}
161+
162+
predicate rewrite(Expr expr, string oldPattern, string newPattern) {
163+
rewriteBinaryOperation(expr, oldPattern, newPattern)
124164
or
125165
rewriteConditionalExpr(expr, oldPattern, newPattern)
126166
or
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: minorAnalysis
3+
---
4+
* The `cs/simplifiable-boolean-expression` query now produces fewer false-positive results when type information is incomplete.

‎csharp/ql/test/query-tests/Language Abuse/SimplifyBoolExpr/SimplifyBoolExpr.cs‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,5 +44,8 @@ void Fn()
4444
if (true != false) ;
4545
if (true && true) ;
4646
if (true || false) ;
47+
48+
bool? boption = false;
49+
if (boption == true) ; // GOOD. Can't be simplified like a regular bool
4750
}
4851
}

‎csharp/ql/test/query-tests/standalone/Language Abuse/SimplifyBoolExpr/SimplifyBoolExpr.cs‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,3 +58,25 @@ class IncompleteNestedOperatorTest
5858
return lambda() || Local();
5959
}
6060
}
61+
62+
class Test
63+
{
64+
public class Container
65+
{
66+
public bool? Field;
67+
}
68+
69+
public void Fn()
70+
{
71+
bool? boption = false;
72+
if (boption == true) ; // GOOD. Can't be simplified like a regular bool
73+
74+
bool b = false;
75+
if (b ? boption : false) ; // GOOD. Can't be simplified like a regular bool
76+
77+
// Emulating incomplete type information by not declaring container explicitly.
78+
if (container.Field == true) ; // GOOD. Can't be simplified like a regular bool
79+
80+
if (b ? container.Field : false) ; // GOOD. Can't be simplified like a regular bool
81+
}
82+
}

0 commit comments

Comments
 (0)