Skip to content

Commit e23675e

Browse files
committed
[clang-tidy] avoid false postive when ignore macro
Fixes: #91487
1 parent 023cdfc commit e23675e

File tree

4 files changed

+55
-29
lines changed

4 files changed

+55
-29
lines changed

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

Lines changed: 36 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,25 @@ class SimplifyBooleanExprCheck::Visitor : public RecursiveASTVisitor<Visitor> {
513513
return true;
514514
}
515515

516-
static bool isUnaryLNot(const Expr *E) {
517-
return isa<UnaryOperator>(E) &&
516+
static bool isExpectedUnaryLNot(SimplifyBooleanExprCheck *Check,
517+
const Expr *E) {
518+
return !Check->canBeBypassed(E) && isa<UnaryOperator>(E) &&
518519
cast<UnaryOperator>(E)->getOpcode() == UO_LNot;
519520
}
520521

522+
static bool isExpectedBinaryOp(SimplifyBooleanExprCheck *Check,
523+
const Expr *E) {
524+
const auto *BinaryOp = dyn_cast<BinaryOperator>(E);
525+
return !Check->canBeBypassed(E) && BinaryOp && BinaryOp->isLogicalOp() &&
526+
BinaryOp->getType()->isBooleanType();
527+
}
528+
521529
template <typename Functor>
522530
static bool checkEitherSide(const BinaryOperator *BO, Functor Func) {
523531
return Func(BO->getLHS()) || Func(BO->getRHS());
524532
}
525533

526-
static bool nestedDemorgan(const Expr *E, unsigned NestingLevel) {
534+
bool nestedDemorgan(const Expr *E, unsigned NestingLevel) {
527535
const auto *BO = dyn_cast<BinaryOperator>(E->IgnoreUnlessSpelledInSource());
528536
if (!BO)
529537
return false;
@@ -539,15 +547,14 @@ class SimplifyBooleanExprCheck::Visitor : public RecursiveASTVisitor<Visitor> {
539547
return true;
540548
case BO_LAnd:
541549
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;
550+
return checkEitherSide(BO,
551+
[this](const Expr *E) {
552+
return isExpectedUnaryLNot(Check, E);
553+
}) ||
554+
(NestingLevel &&
555+
checkEitherSide(BO, [this, NestingLevel](const Expr *E) {
556+
return nestedDemorgan(E, NestingLevel - 1);
557+
}));
551558
default:
552559
return false;
553560
}
@@ -556,19 +563,19 @@ class SimplifyBooleanExprCheck::Visitor : public RecursiveASTVisitor<Visitor> {
556563
bool TraverseUnaryOperator(UnaryOperator *Op) {
557564
if (!Check->SimplifyDeMorgan || Op->getOpcode() != UO_LNot)
558565
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())
566+
const Expr *SubImp = Op->getSubExpr()->IgnoreImplicit();
567+
const auto *Parens = dyn_cast<ParenExpr>(SubImp);
568+
const Expr *SubExpr =
569+
Parens ? Parens->getSubExpr()->IgnoreImplicit() : SubImp;
570+
if (!isExpectedBinaryOp(Check, SubExpr))
567571
return Base::TraverseUnaryOperator(Op);
572+
const auto *BinaryOp = cast<BinaryOperator>(SubExpr);
568573
if (Check->SimplifyDeMorganRelaxed ||
569-
checkEitherSide(BinaryOp, isUnaryLNot) ||
570-
checkEitherSide(BinaryOp,
571-
[](const Expr *E) { return nestedDemorgan(E, 1); })) {
574+
checkEitherSide(
575+
BinaryOp,
576+
[this](const Expr *E) { return isExpectedUnaryLNot(Check, E); }) ||
577+
checkEitherSide(
578+
BinaryOp, [this](const Expr *E) { return nestedDemorgan(E, 1); })) {
572579
if (Check->reportDeMorgan(Context, Op, BinaryOp, !IsProcessing, parent(),
573580
Parens) &&
574581
!Check->areDiagsSelfContained()) {
@@ -694,6 +701,10 @@ void SimplifyBooleanExprCheck::check(const MatchFinder::MatchResult &Result) {
694701
Visitor(this, *Result.Context).traverse();
695702
}
696703

704+
bool SimplifyBooleanExprCheck::canBeBypassed(const Stmt *S) const {
705+
return IgnoreMacros && S->getBeginLoc().isMacroID();
706+
}
707+
697708
void SimplifyBooleanExprCheck::issueDiag(const ASTContext &Context,
698709
SourceLocation Loc,
699710
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: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,9 @@ 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<clang-tidy/checks/readability/simplify-boolean-expr>`
361+
check to avoid to emit warning for macro when IgnoreMacro option is enabled.
362+
360363
- Improved :doc:`readability-static-definition-in-anonymous-namespace
361364
<clang-tidy/checks/readability/static-definition-in-anonymous-namespace>`
362365
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)