Skip to content

Commit f62d18f

Browse files
[Clang] Extend -Wbool-operation to warn about bitwise and of bools with side effects
Motivation: https://arstechnica.com/gadgets/2021/07/google-pushed-a-one-character-typo-to-production-bricking-chrome-os-devices/ Warn for pattern boolA & boolB or boolA | boolB where boolA and boolB has possible side effects. Casting one operand to int is enough to silence this warning: for example (int)boolA & boolB or boolA| (int)boolB Fixes https://bugs.llvm.org/show_bug.cgi?id=51216 Differential Revision: https://reviews.llvm.org/D108003
1 parent cf284f6 commit f62d18f

File tree

6 files changed

+147
-1
lines changed

6 files changed

+147
-1
lines changed

clang/include/clang/Basic/DiagnosticGroups.td

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,8 @@ def StringConversion : DiagGroup<"string-conversion">;
6464
def SignConversion : DiagGroup<"sign-conversion">;
6565
def PointerBoolConversion : DiagGroup<"pointer-bool-conversion">;
6666
def UndefinedBoolConversion : DiagGroup<"undefined-bool-conversion">;
67-
def BoolOperation : DiagGroup<"bool-operation">;
67+
def BitwiseInsteadOfLogical : DiagGroup<"bitwise-instead-of-logical">;
68+
def BoolOperation : DiagGroup<"bool-operation", [BitwiseInsteadOfLogical]>;
6869
def BoolConversion : DiagGroup<"bool-conversion", [PointerBoolConversion,
6970
UndefinedBoolConversion]>;
7071
def IntConversion : DiagGroup<"int-conversion">;

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ def warn_infinite_recursive_function : Warning<
6666
def warn_comma_operator : Warning<"possible misuse of comma operator here">,
6767
InGroup<DiagGroup<"comma">>, DefaultIgnore;
6868
def note_cast_to_void : Note<"cast expression to void to silence warning">;
69+
def note_cast_operand_to_int : Note<"cast one or both operands to int to silence this warning">;
6970

7071
// Constant expressions
7172
def err_expr_not_ice : Error<
@@ -7423,6 +7424,9 @@ def note_member_declared_here : Note<
74237424
"member %0 declared here">;
74247425
def note_member_first_declared_here : Note<
74257426
"member %0 first declared here">;
7427+
def warn_bitwise_instead_of_logical : Warning<
7428+
"use of bitwise '%0' with boolean operands">,
7429+
InGroup<BitwiseInsteadOfLogical>, DefaultIgnore;
74267430
def warn_bitwise_negation_bool : Warning<
74277431
"bitwise negation of a boolean expression%select{;| always evaluates to 'true';}0 "
74287432
"did you mean logical negation?">,

clang/lib/Sema/SemaChecking.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13249,6 +13249,20 @@ static void AnalyzeImplicitConversions(
1324913249
<< OrigE->getSourceRange() << T->isBooleanType()
1325013250
<< FixItHint::CreateReplacement(UO->getBeginLoc(), "!");
1325113251

13252+
if (const auto *BO = dyn_cast<BinaryOperator>(SourceExpr))
13253+
if ((BO->getOpcode() == BO_And || BO->getOpcode() == BO_Or) &&
13254+
BO->getLHS()->isKnownToHaveBooleanValue() &&
13255+
BO->getRHS()->isKnownToHaveBooleanValue() &&
13256+
BO->getLHS()->HasSideEffects(S.Context) &&
13257+
BO->getRHS()->HasSideEffects(S.Context)) {
13258+
S.Diag(BO->getBeginLoc(), diag::warn_bitwise_instead_of_logical)
13259+
<< (BO->getOpcode() == BO_And ? "&" : "|") << OrigE->getSourceRange()
13260+
<< FixItHint::CreateReplacement(
13261+
BO->getOperatorLoc(),
13262+
(BO->getOpcode() == BO_And ? "&&" : "||"));
13263+
S.Diag(BO->getBeginLoc(), diag::note_cast_operand_to_int);
13264+
}
13265+
1325213266
// For conditional operators, we analyze the arguments as if they
1325313267
// were being fed directly into the output.
1325413268
if (auto *CO = dyn_cast<AbstractConditionalOperator>(SourceExpr)) {

clang/test/Misc/warning-wall.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ RUN: FileCheck --input-file=%t %s
44
CHECK:-Wall
55
CHECK-NEXT: -Wmost
66
CHECK-NEXT: -Wbool-operation
7+
CHECK-NEXT: -Wbitwise-instead-of-logical
78
CHECK-NEXT: -Wchar-subscripts
89
CHECK-NEXT: -Wcomment
910
CHECK-NEXT: -Wdelete-non-virtual-dtor
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// RUN: %clang_cc1 -x c -fsyntax-only -verify -Wbool-operation %s
2+
// RUN: %clang_cc1 -x c -fsyntax-only -verify -Wall %s
3+
// RUN: %clang_cc1 -x c -fsyntax-only -Wbitwise-instead-of-logical -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s
4+
// RUN: %clang_cc1 -x c -fsyntax-only -Wbool-operation -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s
5+
// RUN: %clang_cc1 -x c++ -fsyntax-only -verify -Wbool-operation %s
6+
// RUN: %clang_cc1 -x c++ -fsyntax-only -verify -Wall %s
7+
// RUN: %clang_cc1 -x c++ -fsyntax-only -Wbitwise-instead-of-logical -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s
8+
// RUN: %clang_cc1 -x c++ -fsyntax-only -Wbool-operation -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s
9+
10+
#ifdef __cplusplus
11+
typedef bool boolean;
12+
#else
13+
typedef _Bool boolean;
14+
#endif
15+
16+
boolean foo(void);
17+
boolean bar(void);
18+
boolean baz(void) __attribute__((const));
19+
void sink(boolean);
20+
21+
#define FOO foo()
22+
23+
void test(boolean a, boolean b, int *p, volatile int *q, int i) {
24+
b = a & b;
25+
b = foo() & a;
26+
b = (p != 0) & (*p == 42); // FIXME: also warn for a non-volatile pointer dereference
27+
b = foo() & (*q == 42); // expected-warning {{use of bitwise '&' with boolean operands}}
28+
// expected-note@-1 {{cast one or both operands to int to silence this warning}}
29+
b = foo() & (int)(*q == 42); // OK, no warning expected
30+
b = a & foo();
31+
b = (int)a & foo(); // OK, no warning expected
32+
b = foo() & bar(); // expected-warning {{use of bitwise '&' with boolean operands}}
33+
// expected-note@-1 {{cast one or both operands to int to silence this warning}}
34+
// CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:13-[[@LINE-2]]:14}:"&&"
35+
b = foo() & (int)bar(); // OK, no warning expected
36+
b = foo() & !bar(); // expected-warning {{use of bitwise '&' with boolean operands}}
37+
// expected-note@-1 {{cast one or both operands to int to silence this warning}}
38+
// CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:13-[[@LINE-2]]:14}:"&&"
39+
b = a & baz();
40+
b = bar() & FOO; // expected-warning {{use of bitwise '&' with boolean operands}}
41+
// expected-note@-1 {{cast one or both operands to int to silence this warning}}
42+
// CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:13-[[@LINE-2]]:14}:"&&"
43+
b = foo() & (int)FOO; // OK, no warning expected
44+
b = b & foo();
45+
b = bar() & (i > 4);
46+
b = (i == 7) & foo();
47+
#ifdef __cplusplus
48+
b = foo() bitand bar(); // expected-warning {{use of bitwise '&' with boolean operands}}
49+
// expected-note@-1 {{cast one or both operands to int to silence this warning}}
50+
#endif
51+
52+
if (foo() & bar()) // expected-warning {{use of bitwise '&' with boolean operands}}
53+
// expected-note@-1 {{cast one or both operands to int to silence this warning}}
54+
;
55+
56+
sink(a & b);
57+
sink(a & foo());
58+
sink(foo() & bar()); // expected-warning {{use of bitwise '&' with boolean operands}}
59+
// expected-note@-1 {{cast one or both operands to int to silence this warning}}
60+
61+
int n = i + 10;
62+
b = (n & (n - 1));
63+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// RUN: %clang_cc1 -x c -fsyntax-only -verify -Wbool-operation %s
2+
// RUN: %clang_cc1 -x c -fsyntax-only -verify -Wall %s
3+
// RUN: %clang_cc1 -x c -fsyntax-only -Wbitwise-instead-of-logical -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s
4+
// RUN: %clang_cc1 -x c -fsyntax-only -Wbool-operation -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s
5+
// RUN: %clang_cc1 -x c++ -fsyntax-only -verify -Wbool-operation %s
6+
// RUN: %clang_cc1 -x c++ -fsyntax-only -verify -Wall %s
7+
// RUN: %clang_cc1 -x c++ -fsyntax-only -Wbitwise-instead-of-logical -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s
8+
// RUN: %clang_cc1 -x c++ -fsyntax-only -Wbool-operation -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s
9+
10+
#ifdef __cplusplus
11+
typedef bool boolean;
12+
#else
13+
typedef _Bool boolean;
14+
#endif
15+
16+
boolean foo(void);
17+
boolean bar(void);
18+
boolean baz(void) __attribute__((const));
19+
void sink(boolean);
20+
21+
#define FOO foo()
22+
23+
void test(boolean a, boolean b, int *p, volatile int *q, int i) {
24+
b = a | b;
25+
b = foo() | a;
26+
b = (p != 0) | (*p == 42); // FIXME: also warn for a non-volatile pointer dereference
27+
b = foo() | (*q == 42); // expected-warning {{use of bitwise '|' with boolean operands}}
28+
// expected-note@-1 {{cast one or both operands to int to silence this warning}}
29+
b = foo() | (int)(*q == 42); // OK, no warning expected
30+
b = a | foo();
31+
b = (int)a | foo(); // OK, no warning expected
32+
b = foo() | bar(); // expected-warning {{use of bitwise '|' with boolean operands}}
33+
// expected-note@-1 {{cast one or both operands to int to silence this warning}}
34+
// CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:13-[[@LINE-2]]:14}:"||"
35+
b = foo() | !bar(); // expected-warning {{use of bitwise '|' with boolean operands}}
36+
// expected-note@-1 {{cast one or both operands to int to silence this warning}}
37+
// CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:13-[[@LINE-2]]:14}:"||"
38+
b = foo() | (int)bar(); // OK, no warning expected
39+
b = a | baz();
40+
b = bar() | FOO; // expected-warning {{use of bitwise '|' with boolean operands}}
41+
// expected-note@-1 {{cast one or both operands to int to silence this warning}}
42+
// CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:13-[[@LINE-2]]:14}:"||"
43+
b = foo() | (int)FOO; // OK, no warning expected
44+
b = b | foo();
45+
b = bar() | (i > 4);
46+
b = (i == 7) | foo();
47+
#ifdef __cplusplus
48+
b = foo() bitor bar(); // expected-warning {{use of bitwise '|' with boolean operands}}
49+
// expected-note@-1 {{cast one or both operands to int to silence this warning}}
50+
#endif
51+
52+
if (foo() | bar()) // expected-warning {{use of bitwise '|' with boolean operands}}
53+
// expected-note@-1 {{cast one or both operands to int to silence this warning}}
54+
;
55+
56+
sink(a | b);
57+
sink(a | foo());
58+
sink(foo() | bar()); // expected-warning {{use of bitwise '|' with boolean operands}}
59+
// expected-note@-1 {{cast one or both operands to int to silence this warning}}
60+
61+
int n = i + 10;
62+
b = (n | (n - 1));
63+
}

0 commit comments

Comments
 (0)