[clang] dfdfd30 - [Clang] Fix -Wconstant-logical-operand when LHS is a constant
Shivam Gupta via cfe-commits
cfe-commits at lists.llvm.org
Tue Jul 18 23:42:49 PDT 2023
Author: Shivam Gupta
Date: 2023-07-19T12:12:11+05:30
New Revision: dfdfd306cfaf54fbc43e2d5eb36489dac3eb9976
URL: https://github.com/llvm/llvm-project/commit/dfdfd306cfaf54fbc43e2d5eb36489dac3eb9976
DIFF: https://github.com/llvm/llvm-project/commit/dfdfd306cfaf54fbc43e2d5eb36489dac3eb9976.diff
LOG: [Clang] Fix -Wconstant-logical-operand when LHS is a constant
This fix PR37919
The below code produces -Wconstant-logical-operand for the first statement,
but not the second.
void foo(int x) {
if (x && 5) {}
if (5 && x) {}
}
Reviewed By: nickdesaulniers
Differential Revision: https://reviews.llvm.org/D142609
Added:
Modified:
clang/include/clang/Sema/Sema.h
clang/lib/Sema/SemaExpr.cpp
clang/test/C/drs/dr4xx.c
clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p5.cpp
clang/test/Parser/cxx2a-concept-declaration.cpp
clang/test/Sema/exprs.c
clang/test/SemaCXX/expressions.cpp
clang/test/SemaCXX/warn-unsequenced.cpp
clang/test/SemaTemplate/dependent-expr.cpp
Removed:
################################################################################
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 2ffe3783c2f3e7..400b6f71133236 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -12679,6 +12679,8 @@ class Sema final {
QualType CheckBitwiseOperands( // C99 6.5.[10...12]
ExprResult &LHS, ExprResult &RHS, SourceLocation Loc,
BinaryOperatorKind Opc);
+ void diagnoseLogicalInsteadOfBitwise(Expr *Op1, Expr *Op2, SourceLocation Loc,
+ BinaryOperatorKind Opc);
QualType CheckLogicalOperands( // C99 6.5.[13,14]
ExprResult &LHS, ExprResult &RHS, SourceLocation Loc,
BinaryOperatorKind Opc);
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 56e9c4ca133278..87e0939d56ceb2 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -13893,6 +13893,56 @@ inline QualType Sema::CheckBitwiseOperands(ExprResult &LHS, ExprResult &RHS,
return InvalidOperands(Loc, LHS, RHS);
}
+// Diagnose cases where the user write a logical and/or but probably meant a
+// bitwise one. We do this when one of the operands is a non-bool integer and
+// the other is a constant.
+void Sema::diagnoseLogicalInsteadOfBitwise(Expr *Op1, Expr *Op2,
+ SourceLocation Loc,
+ BinaryOperatorKind Opc) {
+ if (Op1->getType()->isIntegerType() && !Op1->getType()->isBooleanType() &&
+ Op2->getType()->isIntegerType() && !Op2->isValueDependent() &&
+ // Don't warn in macros or template instantiations.
+ !Loc.isMacroID() && !inTemplateInstantiation() &&
+ !Op2->getExprLoc().isMacroID() &&
+ !Op1->getExprLoc().isMacroID()) {
+ bool IsOp1InMacro = Op1->getExprLoc().isMacroID();
+ bool IsOp2InMacro = Op2->getExprLoc().isMacroID();
+
+ // Exclude the specific expression from triggering the warning.
+ if (!(IsOp1InMacro && IsOp2InMacro && Op1->getSourceRange() == Op2->getSourceRange())) {
+ // If the RHS can be constant folded, and if it constant folds to something
+ // that isn't 0 or 1 (which indicate a potential logical operation that
+ // happened to fold to true/false) then warn.
+ // Parens on the RHS are ignored.
+ // If the RHS can be constant folded, and if it constant folds to something
+ // that isn't 0 or 1 (which indicate a potential logical operation that
+ // happened to fold to true/false) then warn.
+ // Parens on the RHS are ignored.
+ Expr::EvalResult EVResult;
+ if (Op2->EvaluateAsInt(EVResult, Context)) {
+ llvm::APSInt Result = EVResult.Val.getInt();
+ if ((getLangOpts().Bool && !Op2->getType()->isBooleanType() &&
+ !Op2->getExprLoc().isMacroID()) ||
+ (Result != 0 && Result != 1)) {
+ Diag(Loc, diag::warn_logical_instead_of_bitwise)
+ << Op2->getSourceRange() << (Opc == BO_LAnd ? "&&" : "||");
+ // Suggest replacing the logical operator with the bitwise version
+ Diag(Loc, diag::note_logical_instead_of_bitwise_change_operator)
+ << (Opc == BO_LAnd ? "&" : "|")
+ << FixItHint::CreateReplacement(
+ SourceRange(Loc, getLocForEndOfToken(Loc)),
+ Opc == BO_LAnd ? "&" : "|");
+ if (Opc == BO_LAnd)
+ // Suggest replacing "Foo() && kNonZero" with "Foo()"
+ Diag(Loc, diag::note_logical_instead_of_bitwise_remove_constant)
+ << FixItHint::CreateRemoval(SourceRange(
+ getLocForEndOfToken(Op1->getEndLoc()), Op2->getEndLoc()));
+ }
+ }
+ }
+ }
+}
+
// C99 6.5.[13,14]
inline QualType Sema::CheckLogicalOperands(ExprResult &LHS, ExprResult &RHS,
SourceLocation Loc,
@@ -13911,9 +13961,6 @@ inline QualType Sema::CheckLogicalOperands(ExprResult &LHS, ExprResult &RHS,
}
}
- if (EnumConstantInBoolContext)
- Diag(Loc, diag::warn_enum_constant_in_bool_context);
-
// WebAssembly tables can't be used with logical operators.
QualType LHSTy = LHS.get()->getType();
QualType RHSTy = RHS.get()->getType();
@@ -13924,40 +13971,14 @@ inline QualType Sema::CheckLogicalOperands(ExprResult &LHS, ExprResult &RHS,
return InvalidOperands(Loc, LHS, RHS);
}
- // Diagnose cases where the user write a logical and/or but probably meant a
- // bitwise one. We do this when the LHS is a non-bool integer and the RHS
- // is a constant.
- if (!EnumConstantInBoolContext && LHS.get()->getType()->isIntegerType() &&
- !LHS.get()->getType()->isBooleanType() &&
- RHS.get()->getType()->isIntegerType() && !RHS.get()->isValueDependent() &&
- // Don't warn in macros or template instantiations.
- !Loc.isMacroID() && !inTemplateInstantiation()) {
- // If the RHS can be constant folded, and if it constant folds to something
- // that isn't 0 or 1 (which indicate a potential logical operation that
- // happened to fold to true/false) then warn.
- // Parens on the RHS are ignored.
- Expr::EvalResult EVResult;
- if (RHS.get()->EvaluateAsInt(EVResult, Context)) {
- llvm::APSInt Result = EVResult.Val.getInt();
- if ((getLangOpts().Bool && !RHS.get()->getType()->isBooleanType() &&
- !RHS.get()->getExprLoc().isMacroID()) ||
- (Result != 0 && Result != 1)) {
- Diag(Loc, diag::warn_logical_instead_of_bitwise)
- << RHS.get()->getSourceRange() << (Opc == BO_LAnd ? "&&" : "||");
- // Suggest replacing the logical operator with the bitwise version
- Diag(Loc, diag::note_logical_instead_of_bitwise_change_operator)
- << (Opc == BO_LAnd ? "&" : "|")
- << FixItHint::CreateReplacement(
- SourceRange(Loc, getLocForEndOfToken(Loc)),
- Opc == BO_LAnd ? "&" : "|");
- if (Opc == BO_LAnd)
- // Suggest replacing "Foo() && kNonZero" with "Foo()"
- Diag(Loc, diag::note_logical_instead_of_bitwise_remove_constant)
- << FixItHint::CreateRemoval(
- SourceRange(getLocForEndOfToken(LHS.get()->getEndLoc()),
- RHS.get()->getEndLoc()));
- }
- }
+ if (EnumConstantInBoolContext) {
+ // Warn when converting the enum constant to a boolean
+ Diag(Loc, diag::warn_enum_constant_in_bool_context);
+ } else {
+ // Diagnose cases where the user write a logical and/or but probably meant a
+ // bitwise one.
+ diagnoseLogicalInsteadOfBitwise(LHS.get(), RHS.get(), Loc, Opc);
+ diagnoseLogicalInsteadOfBitwise(RHS.get(), LHS.get(), Loc, Opc);
}
if (!Context.getLangOpts().CPlusPlus) {
diff --git a/clang/test/C/drs/dr4xx.c b/clang/test/C/drs/dr4xx.c
index 5c5349aa7c1409..24fa2670955478 100644
--- a/clang/test/C/drs/dr4xx.c
+++ b/clang/test/C/drs/dr4xx.c
@@ -308,7 +308,9 @@ void dr489(void) {
case (int){ 2 }: break; /* expected-warning {{expression is not an integer constant expression; folding it to a constant is a GNU extension}}
c89only-warning {{compound literals are a C99-specific feature}}
*/
- case 12 || main(): break; /* expected-warning {{expression is not an integer constant expression; folding it to a constant is a GNU extension}} */
+ case 12 || main(): break; /* expected-warning {{expression is not an integer constant expression; folding it to a constant is a GNU extension}}
+ expected-warning {{use of logical '||' with constant operand}} \
+ expected-note {{use '|' for a bitwise operation}} */
}
}
diff --git a/clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p5.cpp b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p5.cpp
index 18b2c6b1d6d15b..a5b57b76625df2 100644
--- a/clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p5.cpp
+++ b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p5.cpp
@@ -72,9 +72,15 @@ constexpr S InitList3(int a) { return a ? S{ a, a } : S{ a, ng }; }; // ok
constexpr int LogicalAnd1(int n) { return n && (throw, 0); } // ok
constexpr int LogicalAnd2(int n) { return 1 && (throw, 0); } // expected-error {{never produces}} expected-note {{subexpression}}
+ // expected-warning at -1 {{use of logical '&&' with constant operand}}
+ // expected-note at -2 {{use '&' for a bitwise operation}}
+ // expected-note at -3 {{remove constant to silence this warning}}
constexpr int LogicalOr1(int n) { return n || (throw, 0); } // ok
constexpr int LogicalOr2(int n) { return 0 || (throw, 0); } // expected-error {{never produces}} expected-note {{subexpression}}
+ // expected-warning at -1 {{use of logical '||' with constant operand}}
+ // expected-note at -2 {{use '|' for a bitwise operation}}
+
constexpr int Conditional1(bool b, int n) { return b ? n : ng; } // ok
constexpr int Conditional2(bool b, int n) { return b ? n * ng : n + ng; } // expected-error {{never produces}} expected-note {{both arms of conditional operator are unable to produce a constant expression}}
diff --git a/clang/test/Parser/cxx2a-concept-declaration.cpp b/clang/test/Parser/cxx2a-concept-declaration.cpp
index 0a7af847112de1..fa30703bf8df76 100644
--- a/clang/test/Parser/cxx2a-concept-declaration.cpp
+++ b/clang/test/Parser/cxx2a-concept-declaration.cpp
@@ -79,6 +79,11 @@ template<typename T> concept C16 = true && (0 && 0); // expected-error {{atomic
// expected-warning at -1{{use of logical '&&' with constant operand}}
// expected-note at -2{{use '&' for a bitwise operation}}
// expected-note at -3{{remove constant to silence this warning}}
+// expected-warning at -4{{use of logical '&&' with constant operand}}
+// expected-note at -5{{use '&' for a bitwise operation}}
+// expected-note at -6{{remove constant to silence this warning}}
+
+
template<typename T> concept C17 = T{};
static_assert(!C17<bool>);
template<typename T> concept C18 = (bool&&)true;
diff --git a/clang/test/Sema/exprs.c b/clang/test/Sema/exprs.c
index 31c6d1e01491a7..20a4c8fcc8b982 100644
--- a/clang/test/Sema/exprs.c
+++ b/clang/test/Sema/exprs.c
@@ -212,6 +212,10 @@ int test20(int x) {
// expected-note {{use '&' for a bitwise operation}} \
// expected-note {{remove constant to silence this warning}}
+ return 4 && x; // expected-warning {{use of logical '&&' with constant operand}} \
+ // expected-note {{use '&' for a bitwise operation}} \
+ // expected-note {{remove constant to silence this warning}}
+
return x && sizeof(int) == 4; // no warning, RHS is logical op.
// no warning, this is an idiom for "true" in old C style.
@@ -223,6 +227,9 @@ int test20(int x) {
// expected-note {{use '|' for a bitwise operation}}
return x || 5; // expected-warning {{use of logical '||' with constant operand}} \
// expected-note {{use '|' for a bitwise operation}}
+ return 5 || x; // expected-warning {{use of logical '||' with constant operand}} \
+ // expected-note {{use '|' for a bitwise operation}}
+
return x && 0;
return x && 1;
return x && -1; // expected-warning {{use of logical '&&' with constant operand}} \
diff --git a/clang/test/SemaCXX/expressions.cpp b/clang/test/SemaCXX/expressions.cpp
index 641cfc8af7ce99..bde7adb756974b 100644
--- a/clang/test/SemaCXX/expressions.cpp
+++ b/clang/test/SemaCXX/expressions.cpp
@@ -44,6 +44,9 @@ int test2(int x) {
return x && 4; // expected-warning {{use of logical '&&' with constant operand}} \
// expected-note {{use '&' for a bitwise operation}} \
// expected-note {{remove constant to silence this warning}}
+ return 4 && x; // expected-warning {{use of logical '&&' with constant operand}} \
+ // expected-note {{use '&' for a bitwise operation}} \
+ // expected-note {{remove constant to silence this warning}}
return x && sizeof(int) == 4; // no warning, RHS is logical op.
return x && true;
@@ -66,6 +69,8 @@ int test2(int x) {
// expected-note {{use '|' for a bitwise operation}}
return x || 5; // expected-warning {{use of logical '||' with constant operand}} \
// expected-note {{use '|' for a bitwise operation}}
+ return 5 || x; // expected-warning {{use of logical '||' with constant operand}} \
+ // expected-note {{use '|' for a bitwise operation}}
return x && 0; // expected-warning {{use of logical '&&' with constant operand}} \
// expected-note {{use '&' for a bitwise operation}} \
// expected-note {{remove constant to silence this warning}}
@@ -139,6 +144,5 @@ void test4() {
bool r1 = X || Y;
#define Y2 2
- bool r2 = X || Y2; // expected-warning {{use of logical '||' with constant operand}} \
- // expected-note {{use '|' for a bitwise operation}}
+ bool r2 = X || Y2;
}
diff --git a/clang/test/SemaCXX/warn-unsequenced.cpp b/clang/test/SemaCXX/warn-unsequenced.cpp
index 50dde8f3a57891..5076a2dfcedd7a 100644
--- a/clang/test/SemaCXX/warn-unsequenced.cpp
+++ b/clang/test/SemaCXX/warn-unsequenced.cpp
@@ -76,15 +76,37 @@ void test() {
(xs[2] && (a = 0)) + a; // cxx11-warning {{unsequenced modification and access to 'a'}}
// cxx17-warning at -1 {{unsequenced modification and access to 'a'}}
- (0 && (a = 0)) + a; // ok
+
+ (0 && (a = 0)) + a; // cxx11-warning {{use of logical '&&' with constant operand}}
+ // cxx11-note at -1 {{use '&' for a bitwise operation}}
+ // cxx11-note at -2 {{remove constant to silence this warning}}
+ // cxx17-warning at -3 {{use of logical '&&' with constant operand}}
+ // cxx17-note at -4 {{use '&' for a bitwise operation}}
+ // cxx17-note at -5 {{remove constant to silence this warning}}
+
(1 && (a = 0)) + a; // cxx11-warning {{unsequenced modification and access to 'a'}}
// cxx17-warning at -1 {{unsequenced modification and access to 'a'}}
+ // cxx11-warning at -2 {{use of logical '&&' with constant operand}}
+ // cxx11-note at -3 {{use '&' for a bitwise operation}}
+ // cxx11-note at -4 {{remove constant to silence this warning}}
+ // cxx17-warning at -5 {{use of logical '&&' with constant operand}}
+ // cxx17-note at -6 {{use '&' for a bitwise operation}}
+ // cxx17-note at -7 {{remove constant to silence this warning}}
+
(xs[3] || (a = 0)) + a; // cxx11-warning {{unsequenced modification and access to 'a'}}
// cxx17-warning at -1 {{unsequenced modification and access to 'a'}}
(0 || (a = 0)) + a; // cxx11-warning {{unsequenced modification and access to 'a'}}
// cxx17-warning at -1 {{unsequenced modification and access to 'a'}}
- (1 || (a = 0)) + a; // ok
+ // cxx11-warning at -2 {{use of logical '||' with constant operand}}
+ // cxx11-note at -3 {{use '|' for a bitwise operation}}
+ // cxx17-warning at -4 {{use of logical '||' with constant operand}}
+ // cxx17-note at -5 {{use '|' for a bitwise operation}}
+ (1 || (a = 0)) + a; // cxx11-warning {{use of logical '||' with constant operand}}
+ // cxx11-note at -1 {{use '|' for a bitwise operation}}
+ // cxx17-warning at -2 {{use of logical '||' with constant operand}}
+ // cxx17-note at -3 {{use '|' for a bitwise operation}}
+
(xs[4] ? a : ++a) + a; // cxx11-warning {{unsequenced modification and access to 'a'}}
// cxx17-warning at -1 {{unsequenced modification and access to 'a'}}
diff --git a/clang/test/SemaTemplate/dependent-expr.cpp b/clang/test/SemaTemplate/dependent-expr.cpp
index 51bd375d7920ee..f2d74ffbcfdd52 100644
--- a/clang/test/SemaTemplate/dependent-expr.cpp
+++ b/clang/test/SemaTemplate/dependent-expr.cpp
@@ -43,7 +43,9 @@ namespace PR7198 {
namespace PR7724 {
template<typename OT> int myMethod()
- { return 2 && sizeof(OT); }
+ { return 2 && sizeof(OT); } // expected-warning {{use of logical '&&' with constant operand}} \
+ // expected-note {{use '&' for a bitwise operation}} \
+ // expected-note {{remove constant to silence this warning}}
}
namespace test4 {
More information about the cfe-commits
mailing list