mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-25 08:46:06 +00:00
[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
This commit is contained in:
parent
f5768ec9b1
commit
dfdfd306cf
@ -12679,6 +12679,8 @@ public:
|
||||
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);
|
||||
|
@ -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) {
|
||||
|
@ -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}} */
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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@-1 {{use of logical '&&' with constant operand}}
|
||||
// expected-note@-2 {{use '&' for a bitwise operation}}
|
||||
// expected-note@-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@-1 {{use of logical '||' with constant operand}}
|
||||
// expected-note@-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}}
|
||||
|
@ -79,6 +79,11 @@ template<typename T> concept C16 = true && (0 && 0); // expected-error {{atomic
|
||||
// expected-warning@-1{{use of logical '&&' with constant operand}}
|
||||
// expected-note@-2{{use '&' for a bitwise operation}}
|
||||
// expected-note@-3{{remove constant to silence this warning}}
|
||||
// expected-warning@-4{{use of logical '&&' with constant operand}}
|
||||
// expected-note@-5{{use '&' for a bitwise operation}}
|
||||
// expected-note@-6{{remove constant to silence this warning}}
|
||||
|
||||
|
||||
template<typename T> concept C17 = T{};
|
||||
static_assert(!C17<bool>);
|
||||
template<typename T> concept C18 = (bool&&)true;
|
||||
|
@ -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}} \
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -76,15 +76,37 @@ void test() {
|
||||
|
||||
(xs[2] && (a = 0)) + a; // cxx11-warning {{unsequenced modification and access to 'a'}}
|
||||
// cxx17-warning@-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@-1 {{use '&' for a bitwise operation}}
|
||||
// cxx11-note@-2 {{remove constant to silence this warning}}
|
||||
// cxx17-warning@-3 {{use of logical '&&' with constant operand}}
|
||||
// cxx17-note@-4 {{use '&' for a bitwise operation}}
|
||||
// cxx17-note@-5 {{remove constant to silence this warning}}
|
||||
|
||||
(1 && (a = 0)) + a; // cxx11-warning {{unsequenced modification and access to 'a'}}
|
||||
// cxx17-warning@-1 {{unsequenced modification and access to 'a'}}
|
||||
// cxx11-warning@-2 {{use of logical '&&' with constant operand}}
|
||||
// cxx11-note@-3 {{use '&' for a bitwise operation}}
|
||||
// cxx11-note@-4 {{remove constant to silence this warning}}
|
||||
// cxx17-warning@-5 {{use of logical '&&' with constant operand}}
|
||||
// cxx17-note@-6 {{use '&' for a bitwise operation}}
|
||||
// cxx17-note@-7 {{remove constant to silence this warning}}
|
||||
|
||||
|
||||
(xs[3] || (a = 0)) + a; // cxx11-warning {{unsequenced modification and access to 'a'}}
|
||||
// cxx17-warning@-1 {{unsequenced modification and access to 'a'}}
|
||||
(0 || (a = 0)) + a; // cxx11-warning {{unsequenced modification and access to 'a'}}
|
||||
// cxx17-warning@-1 {{unsequenced modification and access to 'a'}}
|
||||
(1 || (a = 0)) + a; // ok
|
||||
// cxx11-warning@-2 {{use of logical '||' with constant operand}}
|
||||
// cxx11-note@-3 {{use '|' for a bitwise operation}}
|
||||
// cxx17-warning@-4 {{use of logical '||' with constant operand}}
|
||||
// cxx17-note@-5 {{use '|' for a bitwise operation}}
|
||||
(1 || (a = 0)) + a; // cxx11-warning {{use of logical '||' with constant operand}}
|
||||
// cxx11-note@-1 {{use '|' for a bitwise operation}}
|
||||
// cxx17-warning@-2 {{use of logical '||' with constant operand}}
|
||||
// cxx17-note@-3 {{use '|' for a bitwise operation}}
|
||||
|
||||
|
||||
(xs[4] ? a : ++a) + a; // cxx11-warning {{unsequenced modification and access to 'a'}}
|
||||
// cxx17-warning@-1 {{unsequenced modification and access to 'a'}}
|
||||
|
@ -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 {
|
||||
|
Loading…
x
Reference in New Issue
Block a user