Skip to content

Commit 637af4c

Browse files
committed
Add -Wbitwise-conditional-parentheses to warn on mixing '|' and '&' with "?:"
Extend -Wparentheses to cover mixing bitwise-and and bitwise-or with the conditional operator. There's two main cases seen with this: unsigned bits1 = 0xf0 | cond ? 0x4 : 0x1; unsigned bits2 = cond1 ? 0xf0 : 0x10 | cond2 ? 0x5 : 0x2; // Intended order of evaluation: unsigned bits1 = 0xf0 | (cond ? 0x4 : 0x1); unsigned bits2 = (cond1 ? 0xf0 : 0x10) | (cond2 ? 0x5 : 0x2); // Actual order of evaluation: unsigned bits1 = (0xf0 | cond) ? 0x4 : 0x1; unsigned bits2 = cond1 ? 0xf0 : ((0x10 | cond2) ? 0x5 : 0x2); Differential Revision: https://reviews.llvm.org/D66043 llvm-svn: 375326
1 parent 7bbe711 commit 637af4c

File tree

5 files changed

+41
-2
lines changed

5 files changed

+41
-2
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,9 @@ Improvements to Clang's diagnostics
6161
operation and a constant. The group also has the new warning which diagnoses
6262
when a bitwise-or with a non-negative value is converted to a bool, since
6363
that bool will always be true.
64+
- -Wbitwise-conditional-parentheses will warn on operator precedence issues
65+
when mixing bitwise-and (&) and bitwise-or (|) operator with the
66+
conditional operator (?:).
6467
6568
Non-comprehensive list of changes in this release
6669
-------------------------------------------------

clang/include/clang/Basic/DiagnosticGroups.td

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,7 @@ def ExitTimeDestructors : DiagGroup<"exit-time-destructors">;
296296
def FlexibleArrayExtensions : DiagGroup<"flexible-array-extensions">;
297297
def FourByteMultiChar : DiagGroup<"four-char-constants">;
298298
def GlobalConstructors : DiagGroup<"global-constructors">;
299+
def BitwiseConditionalParentheses: DiagGroup<"bitwise-conditional-parentheses">;
299300
def BitwiseOpParentheses: DiagGroup<"bitwise-op-parentheses">;
300301
def LogicalOpParentheses: DiagGroup<"logical-op-parentheses">;
301302
def LogicalNotParentheses: DiagGroup<"logical-not-parentheses">;
@@ -737,6 +738,7 @@ def ParenthesesOnEquality : DiagGroup<"parentheses-equality">;
737738
def Parentheses : DiagGroup<"parentheses",
738739
[LogicalOpParentheses,
739740
LogicalNotParentheses,
741+
BitwiseConditionalParentheses,
740742
BitwiseOpParentheses,
741743
ShiftOpParentheses,
742744
OverloadedShiftOpParentheses,

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5745,6 +5745,9 @@ def note_precedence_silence : Note<
57455745
def warn_precedence_conditional : Warning<
57465746
"operator '?:' has lower precedence than '%0'; '%0' will be evaluated first">,
57475747
InGroup<Parentheses>;
5748+
def warn_precedence_bitwise_conditional : Warning<
5749+
"operator '?:' has lower precedence than '%0'; '%0' will be evaluated first">,
5750+
InGroup<BitwiseConditionalParentheses>;
57485751
def note_precedence_conditional_first : Note<
57495752
"place parentheses around the '?:' expression to evaluate it first">;
57505753

clang/lib/Sema/SemaExpr.cpp

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7620,7 +7620,12 @@ static void SuggestParentheses(Sema &Self, SourceLocation Loc,
76207620
static bool IsArithmeticOp(BinaryOperatorKind Opc) {
76217621
return BinaryOperator::isAdditiveOp(Opc) ||
76227622
BinaryOperator::isMultiplicativeOp(Opc) ||
7623-
BinaryOperator::isShiftOp(Opc);
7623+
BinaryOperator::isShiftOp(Opc) || Opc == BO_And || Opc == BO_Or;
7624+
// This only checks for bitwise-or and bitwise-and, but not bitwise-xor and
7625+
// not any of the logical operators. Bitwise-xor is commonly used as a
7626+
// logical-xor because there is no logical-xor operator. The logical
7627+
// operators, including uses of xor, have a high false positive rate for
7628+
// precedence warnings.
76247629
}
76257630

76267631
/// IsArithmeticBinaryExpr - Returns true if E is an arithmetic binary
@@ -7710,7 +7715,11 @@ static void DiagnoseConditionalPrecedence(Sema &Self,
77107715
// The condition is an arithmetic binary expression, with a right-
77117716
// hand side that looks boolean, so warn.
77127717

7713-
Self.Diag(OpLoc, diag::warn_precedence_conditional)
7718+
unsigned DiagID = BinaryOperator::isBitwiseOp(CondOpcode)
7719+
? diag::warn_precedence_bitwise_conditional
7720+
: diag::warn_precedence_conditional;
7721+
7722+
Self.Diag(OpLoc, DiagID)
77147723
<< Condition->getSourceRange()
77157724
<< BinaryOperator::getOpcodeStr(CondOpcode);
77167725

clang/test/Sema/parentheses.c

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,28 @@ void conditional_op(int x, int y, _Bool b, void* p) {
9393

9494
(void)(x + y > 0 ? 1 : 2); // no warning
9595
(void)(x + (y > 0) ? 1 : 2); // expected-warning {{operator '?:' has lower precedence than '+'}} expected-note 2{{place parentheses}}
96+
97+
(void)(b ? 0xf0 : 0x10 | b ? 0x5 : 0x2); // expected-warning {{operator '?:' has lower precedence than '|'}} expected-note 2{{place parentheses}}
98+
99+
(void)((b ? 0xf0 : 0x10) | (b ? 0x5 : 0x2)); // no warning, has parentheses
100+
(void)(b ? 0xf0 : (0x10 | b) ? 0x5 : 0x2); // no warning, has parentheses
101+
102+
(void)(x | b ? 1 : 2); // expected-warning {{operator '?:' has lower precedence than '|'}} expected-note 2{{place parentheses}}
103+
(void)(x & b ? 1 : 2); // expected-warning {{operator '?:' has lower precedence than '&'}} expected-note 2{{place parentheses}}
104+
105+
(void)((x | b) ? 1 : 2); // no warning, has parentheses
106+
(void)(x | (b ? 1 : 2)); // no warning, has parentheses
107+
(void)((x & b) ? 1 : 2); // no warning, has parentheses
108+
(void)(x & (b ? 1 : 2)); // no warning, has parentheses
109+
110+
// Only warn on uses of the bitwise operators, and not the logical operators.
111+
// The bitwise operators are more likely to be bugs while the logical
112+
// operators are more likely to be used correctly. Since there is no
113+
// explicit logical-xor operator, the bitwise-xor is commonly used instead.
114+
// For this warning, treat the bitwise-xor as if it were a logical operator.
115+
(void)(x ^ b ? 1 : 2); // no warning, ^ is often used as logical xor
116+
(void)(x || b ? 1 : 2); // no warning, logical operator
117+
(void)(x && b ? 1 : 2); // no warning, logical operator
96118
}
97119

98120
// RUN: not %clang_cc1 -fsyntax-only -Wparentheses -Werror -fdiagnostics-show-option %s 2>&1 | FileCheck %s -check-prefix=CHECK-FLAG

0 commit comments

Comments
 (0)