Skip to content

Commit 3676b09

Browse files
authored
[clang-tidy] readability-simplify-boolean-expr avoid to warn expression expand from macro when IgnoreMacro option is enabled. (#91757)
Fixes: #91487
1 parent 65c9b84 commit 3676b09

File tree

4 files changed

+53
-29
lines changed

4 files changed

+53
-29
lines changed

clang-tools-extra/clang-tidy/readability/SimplifyBooleanExprCheck.cpp

Lines changed: 33 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
//===----------------------------------------------------------------------===//
88

99
#include "SimplifyBooleanExprCheck.h"
10+
#include "clang/AST/Expr.h"
1011
#include "clang/AST/RecursiveASTVisitor.h"
1112
#include "clang/Lex/Lexer.h"
1213
#include "llvm/Support/SaveAndRestore.h"
@@ -280,9 +281,8 @@ class SimplifyBooleanExprCheck::Visitor : public RecursiveASTVisitor<Visitor> {
280281
if (!S) {
281282
return true;
282283
}
283-
if (Check->IgnoreMacros && S->getBeginLoc().isMacroID()) {
284+
if (Check->canBeBypassed(S))
284285
return false;
285-
}
286286
if (!shouldIgnore(S))
287287
StmtStack.push_back(S);
288288
return true;
@@ -513,17 +513,23 @@ class SimplifyBooleanExprCheck::Visitor : public RecursiveASTVisitor<Visitor> {
513513
return true;
514514
}
515515

516-
static bool isUnaryLNot(const Expr *E) {
517-
return isa<UnaryOperator>(E) &&
516+
bool isExpectedUnaryLNot(const Expr *E) {
517+
return !Check->canBeBypassed(E) && isa<UnaryOperator>(E) &&
518518
cast<UnaryOperator>(E)->getOpcode() == UO_LNot;
519519
}
520520

521+
bool isExpectedBinaryOp(const Expr *E) {
522+
const auto *BinaryOp = dyn_cast<BinaryOperator>(E);
523+
return !Check->canBeBypassed(E) && BinaryOp && BinaryOp->isLogicalOp() &&
524+
BinaryOp->getType()->isBooleanType();
525+
}
526+
521527
template <typename Functor>
522528
static bool checkEitherSide(const BinaryOperator *BO, Functor Func) {
523529
return Func(BO->getLHS()) || Func(BO->getRHS());
524530
}
525531

526-
static bool nestedDemorgan(const Expr *E, unsigned NestingLevel) {
532+
bool nestedDemorgan(const Expr *E, unsigned NestingLevel) {
527533
const auto *BO = dyn_cast<BinaryOperator>(E->IgnoreUnlessSpelledInSource());
528534
if (!BO)
529535
return false;
@@ -539,15 +545,13 @@ class SimplifyBooleanExprCheck::Visitor : public RecursiveASTVisitor<Visitor> {
539545
return true;
540546
case BO_LAnd:
541547
case BO_LOr:
542-
if (checkEitherSide(BO, isUnaryLNot))
543-
return true;
544-
if (NestingLevel) {
545-
if (checkEitherSide(BO, [NestingLevel](const Expr *E) {
546-
return nestedDemorgan(E, NestingLevel - 1);
547-
}))
548-
return true;
549-
}
550-
return false;
548+
return checkEitherSide(
549+
BO,
550+
[this](const Expr *E) { return isExpectedUnaryLNot(E); }) ||
551+
(NestingLevel &&
552+
checkEitherSide(BO, [this, NestingLevel](const Expr *E) {
553+
return nestedDemorgan(E, NestingLevel - 1);
554+
}));
551555
default:
552556
return false;
553557
}
@@ -556,19 +560,19 @@ class SimplifyBooleanExprCheck::Visitor : public RecursiveASTVisitor<Visitor> {
556560
bool TraverseUnaryOperator(UnaryOperator *Op) {
557561
if (!Check->SimplifyDeMorgan || Op->getOpcode() != UO_LNot)
558562
return Base::TraverseUnaryOperator(Op);
559-
Expr *SubImp = Op->getSubExpr()->IgnoreImplicit();
560-
auto *Parens = dyn_cast<ParenExpr>(SubImp);
561-
auto *BinaryOp =
562-
Parens
563-
? dyn_cast<BinaryOperator>(Parens->getSubExpr()->IgnoreImplicit())
564-
: dyn_cast<BinaryOperator>(SubImp);
565-
if (!BinaryOp || !BinaryOp->isLogicalOp() ||
566-
!BinaryOp->getType()->isBooleanType())
563+
const Expr *SubImp = Op->getSubExpr()->IgnoreImplicit();
564+
const auto *Parens = dyn_cast<ParenExpr>(SubImp);
565+
const Expr *SubExpr =
566+
Parens ? Parens->getSubExpr()->IgnoreImplicit() : SubImp;
567+
if (!isExpectedBinaryOp(SubExpr))
567568
return Base::TraverseUnaryOperator(Op);
569+
const auto *BinaryOp = cast<BinaryOperator>(SubExpr);
568570
if (Check->SimplifyDeMorganRelaxed ||
569-
checkEitherSide(BinaryOp, isUnaryLNot) ||
570-
checkEitherSide(BinaryOp,
571-
[](const Expr *E) { return nestedDemorgan(E, 1); })) {
571+
checkEitherSide(
572+
BinaryOp,
573+
[this](const Expr *E) { return isExpectedUnaryLNot(E); }) ||
574+
checkEitherSide(
575+
BinaryOp, [this](const Expr *E) { return nestedDemorgan(E, 1); })) {
572576
if (Check->reportDeMorgan(Context, Op, BinaryOp, !IsProcessing, parent(),
573577
Parens) &&
574578
!Check->areDiagsSelfContained()) {
@@ -694,6 +698,10 @@ void SimplifyBooleanExprCheck::check(const MatchFinder::MatchResult &Result) {
694698
Visitor(this, *Result.Context).traverse();
695699
}
696700

701+
bool SimplifyBooleanExprCheck::canBeBypassed(const Stmt *S) const {
702+
return IgnoreMacros && S->getBeginLoc().isMacroID();
703+
}
704+
697705
void SimplifyBooleanExprCheck::issueDiag(const ASTContext &Context,
698706
SourceLocation Loc,
699707
StringRef Description,

clang-tools-extra/clang-tidy/readability/SimplifyBooleanExprCheck.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ class SimplifyBooleanExprCheck : public ClangTidyCheck {
6464
StringRef Description, SourceRange ReplacementRange,
6565
StringRef Replacement);
6666

67+
bool canBeBypassed(const Stmt *S) const;
68+
6769
const bool IgnoreMacros;
6870
const bool ChainedConditionalReturn;
6971
const bool ChainedConditionalAssignment;

clang-tools-extra/docs/ReleaseNotes.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,10 @@ Changes in existing checks
357357
support calls to overloaded operators as base expression and provide fixes to
358358
expressions with side-effects.
359359

360+
- Improved :doc:`readability-simplify-boolean-expr
361+
<clang-tidy/checks/readability/simplify-boolean-expr>` check to avoid to emit
362+
warning for macro when IgnoreMacro option is enabled.
363+
360364
- Improved :doc:`readability-static-definition-in-anonymous-namespace
361365
<clang-tidy/checks/readability/static-definition-in-anonymous-namespace>`
362366
check by resolving fix-it overlaps in template code by disregarding implicit

clang-tools-extra/test/clang-tidy/checkers/readability/simplify-boolean-expr-macros.cpp

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,25 @@
66
// RUN: --
77

88
#define NEGATE(expr) !(expr)
9+
#define NOT_AND_NOT(a, b) (!a && !b)
910

1011
bool without_macro(bool a, bool b) {
1112
return !(!a && b);
1213
// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: boolean expression can be simplified by DeMorgan's theorem
1314
// CHECK-FIXES: return a || !b;
1415
}
1516

16-
bool macro(bool a, bool b) {
17-
return NEGATE(!a && b);
18-
// CHECK-MESSAGES-MACROS: :[[@LINE-1]]:12: warning: boolean expression can be simplified by DeMorgan's theorem
19-
// CHECK-FIXES: return NEGATE(!a && b);
17+
void macro(bool a, bool b) {
18+
NEGATE(!a && b);
19+
// CHECK-MESSAGES-MACROS: :[[@LINE-1]]:5: warning: boolean expression can be simplified by DeMorgan's theorem
20+
// CHECK-FIXES: NEGATE(!a && b);
21+
!NOT_AND_NOT(a, b);
22+
// CHECK-MESSAGES-MACROS: :[[@LINE-1]]:5: warning: boolean expression can be simplified by DeMorgan's theorem
23+
// CHECK-FIXES: !NOT_AND_NOT(a, b);
24+
!(NEGATE(a) && b);
25+
// CHECK-MESSAGES-MACROS: :[[@LINE-1]]:5: warning: boolean expression can be simplified by DeMorgan's theorem
26+
// CHECK-FIXES: !(NEGATE(a) && b);
27+
!(a && NEGATE(b));
28+
// CHECK-MESSAGES-MACROS: :[[@LINE-1]]:5: warning: boolean expression can be simplified by DeMorgan's theorem
29+
// CHECK-FIXES: !(a && NEGATE(b));
2030
}

0 commit comments

Comments
 (0)