10#include "clang/AST/Expr.h"
11#include "clang/AST/RecursiveASTVisitor.h"
12#include "clang/Basic/DiagnosticIDs.h"
13#include "clang/Lex/Lexer.h"
14#include "llvm/Support/SaveAndRestore.h"
24static StringRef
getText(
const ASTContext &Context, SourceRange Range) {
25 return Lexer::getSourceText(CharSourceRange::getTokenRange(Range),
26 Context.getSourceManager(),
27 Context.getLangOpts());
31static StringRef
getText(
const ASTContext &Context, T &Node) {
32 return getText(Context, Node.getSourceRange());
36 "redundant boolean literal supplied to boolean operator";
38 "redundant boolean literal in if statement condition";
40 "redundant boolean literal in conditional return statement";
43 E = E->IgnoreImpCasts();
44 if (isa<BinaryOperator>(E) || isa<ConditionalOperator>(E))
47 if (
const auto *Op = dyn_cast<CXXOperatorCallExpr>(E))
48 return Op->getNumArgs() == 2 && Op->getOperator() != OO_Call &&
49 Op->getOperator() != OO_Subscript;
54static std::pair<BinaryOperatorKind, BinaryOperatorKind>
Opposites[] = {
55 {BO_LT, BO_GE}, {BO_GT, BO_LE}, {BO_EQ, BO_NE}};
58 const BinaryOperatorKind Opcode = BinOp->getOpcode();
59 for (
const auto NegatableOp :
Opposites) {
60 if (Opcode == NegatableOp.first)
61 return BinaryOperator::getOpcodeStr(NegatableOp.second);
62 if (Opcode == NegatableOp.second)
63 return BinaryOperator::getOpcodeStr(NegatableOp.first);
69 {OO_EqualEqual,
"=="}, {OO_ExclaimEqual,
"!="}, {OO_Less,
"<"},
70 {OO_GreaterEqual,
">="}, {OO_Greater,
">"}, {OO_LessEqual,
"<="}};
74 if (Name.first == OpKind)
80static std::pair<OverloadedOperatorKind, OverloadedOperatorKind>
82 {OO_Less, OO_GreaterEqual},
83 {OO_Greater, OO_LessEqual}};
86 const OverloadedOperatorKind Opcode = OpCall->getOperator();
88 if (Opcode == NegatableOp.first)
90 if (Opcode == NegatableOp.second)
96static std::string
asBool(StringRef Text,
bool NeedsStaticCast) {
98 return (
"static_cast<bool>(" + Text +
")").str();
100 return std::string(Text);
104 if (
const auto *ImpCast = dyn_cast<ImplicitCastExpr>(E))
105 return ImpCast->getCastKind() == CK_PointerToBoolean ||
106 ImpCast->getCastKind() == CK_MemberPointerToBoolean;
112 if (
const auto *ImpCast = dyn_cast<ImplicitCastExpr>(E))
113 return ImpCast->getCastKind() == CK_IntegralToBoolean;
119 if (
const auto *ImpCast = dyn_cast<ImplicitCastExpr>(E);
120 ImpCast && ImpCast->getCastKind() == CK_UserDefinedConversion &&
121 ImpCast->getSubExpr()->getType()->isBooleanType()) {
122 if (
const auto *MemCall =
123 dyn_cast<CXXMemberCallExpr>(ImpCast->getSubExpr())) {
124 if (
const auto *MemDecl =
125 dyn_cast<CXXConversionDecl>(MemCall->getMethodDecl());
126 MemDecl && MemDecl->isExplicit())
131 E = E->IgnoreImpCasts();
132 return !E->getType()->isBooleanType();
136 const Expr *E,
bool Negated,
137 const char *Constant) {
138 E = E->IgnoreImpCasts();
139 const std::string ExprText =
140 (isa<BinaryOperator>(E) ? (
"(" +
getText(Context, *E) +
")")
143 return ExprText +
" " + (Negated ?
"!=" :
"==") +
" " + Constant;
147 const Expr *E,
bool Negated) {
148 const char *NullPtr = Context.getLangOpts().CPlusPlus11 ?
"nullptr" :
"NULL";
153 const Expr *E,
bool Negated) {
158 bool Negated,
const Expr *E) {
159 E = E->IgnoreParenBaseCasts();
160 if (
const auto *EC = dyn_cast<ExprWithCleanups>(E))
161 E = EC->getSubExpr();
163 const bool NeedsStaticCast =
166 if (
const auto *UnOp = dyn_cast<UnaryOperator>(E);
167 UnOp && UnOp->getOpcode() == UO_LNot) {
183 StringRef NegatedOperator;
184 const Expr *LHS =
nullptr;
185 const Expr *RHS =
nullptr;
186 if (
const auto *BinOp = dyn_cast<BinaryOperator>(E)) {
188 LHS = BinOp->getLHS();
189 RHS = BinOp->getRHS();
190 }
else if (
const auto *OpExpr = dyn_cast<CXXOperatorCallExpr>(E);
191 OpExpr && OpExpr->getNumArgs() == 2) {
193 LHS = OpExpr->getArg(0);
194 RHS = OpExpr->getArg(1);
197 if (!NegatedOperator.empty() && LHS && RHS)
198 return (
asBool((
getText(Context, *LHS) +
" " + NegatedOperator +
" " +
203 const StringRef Text =
getText(Context, *E);
205 return (
"!(" + Text +
")").str();
213 return (
"!" +
asBool(Text, NeedsStaticCast));
216 if (
const auto *UnOp = dyn_cast<UnaryOperator>(E);
217 UnOp && UnOp->getOpcode() == UO_LNot) {
235 CharSourceRange CharRange) {
236 std::string ReplacementText =
237 Lexer::getSourceText(CharRange, Context.getSourceManager(),
238 Context.getLangOpts())
240 Lexer Lex(CharRange.getBegin(), Context.getLangOpts(), ReplacementText.data(),
241 ReplacementText.data(),
242 ReplacementText.data() + ReplacementText.size());
243 Lex.SetCommentRetentionState(
true);
246 while (!Lex.LexFromRawLexer(Tok))
247 if (Tok.is(tok::TokenKind::comment) || Tok.is(tok::TokenKind::hash))
253static std::optional<bool>
255 if (
const auto *OpCall = dyn_cast<CXXOperatorCallExpr>(E)) {
257 if (!NegatedOperator.empty()) {
258 if (OpCall->getOperatorLoc().isMacroID())
260 Fixes.push_back(FixItHint::CreateReplacement(OpCall->getOperatorLoc(),
269 using Base = RecursiveASTVisitor<Visitor>;
273 : Check(Check), Context(Context) {}
278 switch (S->getStmtClass()) {
279 case Stmt::ImplicitCastExprClass:
280 case Stmt::MaterializeTemporaryExprClass:
281 case Stmt::CXXBindTemporaryExprClass:
291 if (Check->canBeBypassed(S))
294 StmtStack.push_back(S);
300 assert(StmtStack.back() == S);
301 StmtStack.pop_back();
307 Check->reportBinOp(Context, Op);
313 if (
const auto *Bool = dyn_cast<CXXBoolLiteralExpr>(E)) {
314 if (FilterMacro && Bool->getBeginLoc().isMacroID())
316 return Bool->getValue();
318 if (
const auto *UnaryOp = dyn_cast<UnaryOperator>(E)) {
319 if (FilterMacro && UnaryOp->getBeginLoc().isMacroID())
321 if (UnaryOp->getOpcode() == UO_LNot)
323 UnaryOp->getSubExpr()->IgnoreImplicit(), FilterMacro))
333 operator bool()
const {
return Item !=
nullptr; }
341 const auto *RS = dyn_cast<ReturnStmt>(S);
342 if (!RS || !RS->getRetValue())
344 if (std::optional<bool> Ret =
346 return {RS->getRetValue(), *Ret};
354 template <
typename Functor>
356 if (
auto *CS = dyn_cast<CompoundStmt>(S)) {
358 return F(CS->body_front());
365 return StmtStack.size() < 2 ? nullptr : StmtStack[StmtStack.size() - 2];
371 if (If->hasInitStorage() || If->hasVarStorage() || If->isConsteval())
378 const Expr *Cond = If->getCond()->IgnoreImplicit();
381 Check->replaceWithThenStatement(Context, If, Cond);
383 Check->replaceWithElseStatement(Context, If, Cond);
395 if (ElseReturnBool && ThenReturnBool.
Bool != ElseReturnBool.
Bool) {
396 if (Check->ChainedConditionalReturn ||
397 !isa_and_nonnull<IfStmt>(
parent())) {
398 Check->replaceWithReturnCondition(Context, If, ThenReturnBool.Item,
399 ElseReturnBool.
Bool);
409 const auto VarBoolAssignmentMatcher =
411 const auto *BO = dyn_cast<BinaryOperator>(S);
412 if (!BO || BO->getOpcode() != BO_Assign)
414 std::optional<bool> RightasBool =
418 Expr *IgnImp = BO->getLHS()->IgnoreImplicit();
421 Loc = BO->getRHS()->getBeginLoc();
424 if (
auto *DRE = dyn_cast<DeclRefExpr>(IgnImp))
425 return {DRE->getDecl(), *RightasBool};
426 if (
const auto *ME = dyn_cast<MemberExpr>(IgnImp))
427 return {ME->getMemberDecl(), *RightasBool};
434 if (ElseAssignment.
Item == ThenAssignment.Item &&
435 ElseAssignment.
Bool != ThenAssignment.Bool &&
436 (Check->ChainedConditionalAssignment ||
437 !isa_and_nonnull<IfStmt>(
parent()))) {
438 Check->replaceWithAssignment(Context, If, Var, Loc,
439 ElseAssignment.
Bool);
452 if (std::optional<bool> Then =
454 if (std::optional<bool> Else =
457 Check->replaceWithCondition(Context, Cond, *Else);
466 bool CurIf =
false, PrevIf =
false;
467 for (
auto First = CS->body_begin(), Second = std::next(First),
468 End = CS->body_end();
469 Second != End; ++Second, ++First) {
471 CurIf = isa<IfStmt>(*First);
473 if (!TrailingReturnBool)
481 auto *If = cast<IfStmt>(*First);
482 if (!If->hasInitStorage() && !If->hasVarStorage() &&
483 !If->isConsteval()) {
486 if (ThenReturnBool &&
487 ThenReturnBool.
Bool != TrailingReturnBool.
Bool) {
488 if ((Check->ChainedConditionalReturn || !PrevIf) &&
489 If->getElse() ==
nullptr) {
490 Check->replaceCompoundReturnWithCondition(
491 Context, cast<ReturnStmt>(*Second), TrailingReturnBool.
Bool,
492 If, ThenReturnBool.
Item);
496 }
else if (isa<LabelStmt, CaseStmt, DefaultStmt>(*First)) {
502 isa<LabelStmt>(*First) ? cast<LabelStmt>(*First)->getSubStmt()
503 : isa<CaseStmt>(*First) ? cast<CaseStmt>(*First)->getSubStmt()
504 : cast<DefaultStmt>(*First)->getSubStmt();
505 auto *SubIf = dyn_cast<IfStmt>(SubStmt);
506 if (SubIf && !SubIf->getElse() && !SubIf->hasInitStorage() &&
507 !SubIf->hasVarStorage() && !SubIf->isConsteval()) {
510 if (ThenReturnBool &&
511 ThenReturnBool.
Bool != TrailingReturnBool.
Bool) {
512 Check->replaceCompoundReturnWithCondition(
513 Context, cast<ReturnStmt>(*Second), TrailingReturnBool.
Bool,
514 SubIf, ThenReturnBool.
Item);
523 return !Check->canBeBypassed(E) && isa<UnaryOperator>(E) &&
524 cast<UnaryOperator>(E)->getOpcode() == UO_LNot;
528 const auto *BinaryOp = dyn_cast<BinaryOperator>(E);
529 return !Check->canBeBypassed(E) && BinaryOp && BinaryOp->isLogicalOp() &&
530 BinaryOp->getType()->isBooleanType();
533 template <
typename Functor>
535 return Func(BO->getLHS()) || Func(BO->getRHS());
539 const auto *BO = dyn_cast<BinaryOperator>(E->IgnoreUnlessSpelledInSource());
542 if (!BO->getType()->isBooleanType())
544 switch (BO->getOpcode()) {
567 if (!Check->SimplifyDeMorgan || Op->getOpcode() != UO_LNot)
568 return Base::TraverseUnaryOperator(Op);
569 const Expr *SubImp = Op->getSubExpr()->IgnoreImplicit();
570 const auto *Parens = dyn_cast<ParenExpr>(SubImp);
571 const Expr *SubExpr =
572 Parens ? Parens->getSubExpr()->IgnoreImplicit() : SubImp;
574 return Base::TraverseUnaryOperator(Op);
575 const auto *BinaryOp = cast<BinaryOperator>(SubExpr);
576 if ((Check->SimplifyDeMorganRelaxed ||
583 Check->reportDeMorgan(Context, Op, BinaryOp, !IsProcessing,
parent(),
585 !Check->areDiagsSelfContained()) {
586 const llvm::SaveAndRestore RAII(IsProcessing,
true);
587 return Base::TraverseUnaryOperator(Op);
590 return Base::TraverseUnaryOperator(Op);
594 bool IsProcessing =
false;
603 IgnoreMacros(Options.get(
"IgnoreMacros", false)),
604 ChainedConditionalReturn(Options.get(
"ChainedConditionalReturn", false)),
605 ChainedConditionalAssignment(
606 Options.get(
"ChainedConditionalAssignment", false)),
607 SimplifyDeMorgan(Options.get(
"SimplifyDeMorgan", true)),
608 SimplifyDeMorganRelaxed(Options.get(
"SimplifyDeMorganRelaxed", false)) {
609 if (SimplifyDeMorganRelaxed && !SimplifyDeMorgan)
610 configurationDiag(
"%0: 'SimplifyDeMorganRelaxed' cannot be enabled "
611 "without 'SimplifyDeMorgan' enabled")
618 E = E->IgnoreParenImpCasts();
619 if (isa<CXXBoolLiteralExpr>(E))
621 if (
const auto *BinOp = dyn_cast<BinaryOperator>(E))
624 if (
const auto *UnaryOp = dyn_cast<UnaryOperator>(E))
629void SimplifyBooleanExprCheck::reportBinOp(
const ASTContext &Context,
630 const BinaryOperator *Op) {
631 const auto *LHS = Op->getLHS()->IgnoreParenImpCasts();
632 const auto *RHS = Op->getRHS()->IgnoreParenImpCasts();
634 const CXXBoolLiteralExpr *Bool =
nullptr;
635 const Expr *Other =
nullptr;
636 if ((Bool = dyn_cast<CXXBoolLiteralExpr>(LHS)) !=
nullptr)
638 else if ((Bool = dyn_cast<CXXBoolLiteralExpr>(RHS)) !=
nullptr)
643 if (Bool->getBeginLoc().isMacroID())
650 const bool BoolValue = Bool->getValue();
652 const auto ReplaceWithExpression = [
this, &Context, LHS, RHS,
653 Bool](
const Expr *ReplaceWith,
655 const std::string Replacement =
657 const SourceRange Range(LHS->getBeginLoc(), RHS->getEndLoc());
662 switch (Op->getOpcode()) {
666 ReplaceWithExpression(Other,
false);
669 ReplaceWithExpression(Bool,
false);
674 ReplaceWithExpression(Bool,
false);
677 ReplaceWithExpression(Other,
false);
681 ReplaceWithExpression(Other, !BoolValue);
685 ReplaceWithExpression(Other, BoolValue);
693 Options.store(Opts,
"IgnoreMacros", IgnoreMacros);
694 Options.store(Opts,
"ChainedConditionalReturn", ChainedConditionalReturn);
695 Options.store(Opts,
"ChainedConditionalAssignment",
696 ChainedConditionalAssignment);
697 Options.store(Opts,
"SimplifyDeMorgan", SimplifyDeMorgan);
698 Options.store(Opts,
"SimplifyDeMorganRelaxed", SimplifyDeMorganRelaxed);
702 Finder->addMatcher(translationUnitDecl(),
this);
709bool SimplifyBooleanExprCheck::canBeBypassed(
const Stmt *S)
const {
710 return IgnoreMacros && S->getBeginLoc().isMacroID();
714bool SimplifyBooleanExprCheck::issueDiag(
const ASTContext &Context,
716 StringRef Description,
717 SourceRange ReplacementRange,
718 StringRef Replacement) {
719 const CharSourceRange CharRange =
720 Lexer::makeFileCharRange(CharSourceRange::getTokenRange(ReplacementRange),
721 Context.getSourceManager(), getLangOpts());
723 const DiagnosticBuilder Diag = diag(Loc, Description);
726 Diag << FixItHint::CreateReplacement(CharRange, Replacement);
727 return HasReplacement;
730void SimplifyBooleanExprCheck::replaceWithThenStatement(
731 const ASTContext &Context,
const IfStmt *IfStatement,
732 const Expr *BoolLiteral) {
734 IfStatement->getSourceRange(),
735 getText(Context, *IfStatement->getThen()));
738void SimplifyBooleanExprCheck::replaceWithElseStatement(
739 const ASTContext &Context,
const IfStmt *IfStatement,
740 const Expr *BoolLiteral) {
741 const Stmt *ElseStatement = IfStatement->getElse();
743 IfStatement->getSourceRange(),
744 ElseStatement ?
getText(Context, *ElseStatement) :
"");
747void SimplifyBooleanExprCheck::replaceWithCondition(
748 const ASTContext &Context,
const ConditionalOperator *Ternary,
750 const std::string Replacement =
752 issueDiag(Context, Ternary->getTrueExpr()->getBeginLoc(),
753 "redundant boolean literal in ternary expression result",
754 Ternary->getSourceRange(), Replacement);
757void SimplifyBooleanExprCheck::replaceWithReturnCondition(
758 const ASTContext &Context,
const IfStmt *If,
const Expr *BoolLiteral,
760 const StringRef Terminator = isa<CompoundStmt>(If->getElse()) ?
";" :
"";
761 const std::string Condition =
763 const std::string Replacement = (
"return " + Condition + Terminator).str();
764 const SourceLocation Start = BoolLiteral->getBeginLoc();
766 const bool HasReplacement =
768 If->getSourceRange(), Replacement);
770 if (!HasReplacement) {
771 const SourceRange ConditionRange = If->getCond()->getSourceRange();
772 if (ConditionRange.isValid())
773 diag(ConditionRange.getBegin(),
"conditions that can be simplified",
779void SimplifyBooleanExprCheck::replaceCompoundReturnWithCondition(
780 const ASTContext &Context,
const ReturnStmt *Ret,
bool Negated,
781 const IfStmt *If,
const Expr *ThenReturn) {
782 const std::string Replacement =
785 const bool HasReplacement = issueDiag(
787 SourceRange(If->getBeginLoc(), Ret->getEndLoc()), Replacement);
789 if (!HasReplacement) {
790 const SourceRange ConditionRange = If->getCond()->getSourceRange();
791 if (ConditionRange.isValid())
792 diag(ConditionRange.getBegin(),
"conditions that can be simplified",
795 const SourceRange ReturnRange = Ret->getSourceRange();
796 if (ReturnRange.isValid())
797 diag(ReturnRange.getBegin(),
"return statement that can be simplified",
803void SimplifyBooleanExprCheck::replaceWithAssignment(
const ASTContext &Context,
804 const IfStmt *IfAssign,
808 const SourceRange Range = IfAssign->getSourceRange();
809 const StringRef VariableName =
getText(Context, *Var);
810 const StringRef Terminator =
811 isa<CompoundStmt>(IfAssign->getElse()) ?
";" :
"";
812 const std::string Condition =
814 const std::string Replacement =
815 (VariableName +
" = " + Condition + Terminator).str();
816 issueDiag(Context, Loc,
"redundant boolean literal in conditional assignment",
822 const BinaryOperator *BO) {
823 assert(BO->isLogicalOp());
824 if (BO->getOperatorLoc().isMacroID())
826 Output.push_back(FixItHint::CreateReplacement(
827 BO->getOperatorLoc(), BO->getOpcode() == BO_LAnd ?
"||" :
"&&"));
832 assert(BinaryOperator::isLogicalOp(BO));
833 return BO == BO_LAnd ? BO_LOr : BO_LAnd;
837 const ASTContext &Ctx,
const Expr *E,
838 std::optional<BinaryOperatorKind> OuterBO);
845 const ASTContext &Ctx,
const BinaryOperator *BinOp,
846 std::optional<BinaryOperatorKind> OuterBO,
847 const ParenExpr *Parens =
nullptr) {
848 switch (BinOp->getOpcode()) {
862 constexpr bool LogicalOpParentheses =
true;
863 if (((*OuterBO == NewOp) || (!LogicalOpParentheses &&
864 (*OuterBO == BO_LOr && NewOp == BO_LAnd))) &&
865 Parens && !Parens->getLParen().isMacroID() &&
866 !Parens->getRParen().isMacroID()) {
867 Fixes.push_back(FixItHint::CreateRemoval(Parens->getLParen()));
868 Fixes.push_back(FixItHint::CreateRemoval(Parens->getRParen()));
871 if (*OuterBO == BO_LAnd && NewOp == BO_LOr && !Parens) {
872 Fixes.push_back(FixItHint::CreateInsertion(BinOp->getBeginLoc(),
"("));
873 Fixes.push_back(FixItHint::CreateInsertion(
874 Lexer::getLocForEndOfToken(BinOp->getEndLoc(), 0,
875 Ctx.getSourceManager(),
892 if (BinOp->getOperatorLoc().isMacroID())
894 Fixes.push_back(FixItHint::CreateReplacement(
895 BinOp->getOperatorLoc(),
896 BinaryOperator::getOpcodeStr(
897 BinaryOperator::negateComparisonOp(BinOp->getOpcode()))));
903 if (Parens->getBeginLoc().isMacroID())
905 Fixes.push_back(FixItHint::CreateInsertion(Parens->getBeginLoc(),
"!"));
907 if (BinOp->getBeginLoc().isMacroID() || BinOp->getEndLoc().isMacroID())
909 Fixes.append({FixItHint::CreateInsertion(BinOp->getBeginLoc(),
"!("),
910 FixItHint::CreateInsertion(
911 Lexer::getLocForEndOfToken(BinOp->getEndLoc(), 0,
912 Ctx.getSourceManager(),
922 const ASTContext &Ctx,
const Expr *E,
923 std::optional<BinaryOperatorKind> OuterBO) {
924 if (isa<UnaryOperator>(E) && cast<UnaryOperator>(E)->getOpcode() == UO_LNot) {
926 if (cast<UnaryOperator>(E)->getOperatorLoc().
isMacroID())
929 FixItHint::CreateRemoval(cast<UnaryOperator>(E)->getOperatorLoc()));
932 if (
const auto *BinOp = dyn_cast<BinaryOperator>(E))
939 if (
const auto *Paren = dyn_cast<ParenExpr>(E)) {
940 if (
const auto *BinOp = dyn_cast<BinaryOperator>(Paren->getSubExpr()))
947 if (E->getBeginLoc().isMacroID())
949 Fixes.push_back(FixItHint::CreateInsertion(E->getBeginLoc(),
"!"));
954 BinaryOperatorKind NewOuterBinary,
955 const ParenExpr *Parens) {
960 switch (Parent->getStmtClass()) {
961 case Stmt::BinaryOperatorClass: {
962 const auto *BO = cast<BinaryOperator>(Parent);
963 if (BO->isAssignmentOp())
967 if (BO->getOpcode() == NewOuterBinary)
971 case Stmt::UnaryOperatorClass:
972 case Stmt::CXXRewrittenBinaryOperatorClass:
979bool SimplifyBooleanExprCheck::reportDeMorgan(
const ASTContext &Context,
980 const UnaryOperator *Outer,
981 const BinaryOperator *Inner,
984 const ParenExpr *Parens) {
987 assert(Inner->isLogicalOp());
990 diag(Outer->getBeginLoc(),
991 "boolean expression can be simplified by DeMorgan's theorem");
992 Diag << Outer->getSourceRange();
996 if (Outer->getOperatorLoc().isMacroID())
1001 Fixes.push_back(FixItHint::CreateRemoval(
1002 SourceRange(Outer->getOperatorLoc(), Parens->getLParen())));
1003 Fixes.push_back(FixItHint::CreateRemoval(Parens->getRParen()));
1005 Fixes.push_back(FixItHint::CreateRemoval(Outer->getOperatorLoc()));
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
NodeAndBool< Decl > DeclAndBool
bool TraverseUnaryOperator(UnaryOperator *Op)
Visitor(SimplifyBooleanExprCheck *Check, ASTContext &Context)
bool isExpectedBinaryOp(const Expr *E)
bool VisitConditionalOperator(ConditionalOperator *Cond)
static bool shouldIgnore(Stmt *S)
bool isExpectedUnaryLNot(const Expr *E)
static std::optional< bool > getAsBoolLiteral(const Expr *E, bool FilterMacro)
bool dataTraverseStmtPost(Stmt *S)
bool VisitCompoundStmt(CompoundStmt *CS)
bool dataTraverseStmtPre(Stmt *S)
static bool checkEitherSide(const BinaryOperator *BO, Functor Func)
static auto checkSingleStatement(Stmt *S, Functor F) -> decltype(F(S))
If S is not a CompoundStmt, applies F on S, otherwise if there is only 1 statement in the CompoundStm...
NodeAndBool< Expr > ExprAndBool
bool nestedDemorgan(const Expr *E, unsigned NestingLevel)
bool VisitBinaryOperator(const BinaryOperator *Op) const
static ExprAndBool parseReturnLiteralBool(const Stmt *S)
Detect's return (true|false|!true|!false);.
bool VisitIfStmt(IfStmt *If)
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
void registerMatchers(ast_matchers::MatchFinder *Finder) override
SimplifyBooleanExprCheck(StringRef Name, ClangTidyContext *Context)
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
static std::string replacementExpression(const ASTContext &Context, bool Negated, const Expr *E)
static bool needsZeroComparison(const Expr *E)
static bool containsBoolLiteral(const Expr *E)
static StringRef negatedOperator(const BinaryOperator *BinOp)
static bool shouldRemoveParens(const Stmt *Parent, BinaryOperatorKind NewOuterBinary, const ParenExpr *Parens)
static std::string compareExpressionToConstant(const ASTContext &Context, const Expr *E, bool Negated, const char *Constant)
static std::pair< BinaryOperatorKind, BinaryOperatorKind > Opposites[]
static bool needsParensAfterUnaryNegation(const Expr *E)
static bool containsDiscardedTokens(const ASTContext &Context, CharSourceRange CharRange)
static StringRef getOperatorName(OverloadedOperatorKind OpKind)
static constexpr char SimplifyConditionDiagnostic[]
static bool needsNullPtrComparison(const Expr *E)
static constexpr char SimplifyConditionalReturnDiagnostic[]
static bool flipDemorganOperator(SmallVectorImpl< FixItHint > &Output, const BinaryOperator *BO)
Swaps a BinaryOperator opcode from && to || or vice-versa.
static bool flipDemorganBinaryOperator(SmallVectorImpl< FixItHint > &Fixes, const ASTContext &Ctx, const BinaryOperator *BinOp, std::optional< BinaryOperatorKind > OuterBO, const ParenExpr *Parens=nullptr)
Inverts BinOp, Removing Parens if they exist and are safe to remove.
static constexpr char SimplifyOperatorDiagnostic[]
static bool flipDemorganSide(SmallVectorImpl< FixItHint > &Fixes, const ASTContext &Ctx, const Expr *E, std::optional< BinaryOperatorKind > OuterBO)
static StringRef getText(const ASTContext &Context, SourceRange Range)
static BinaryOperatorKind getDemorganFlippedOperator(BinaryOperatorKind BO)
static std::string asBool(StringRef Text, bool NeedsStaticCast)
static std::pair< OverloadedOperatorKind, OverloadedOperatorKind > OppositeOverloads[]
static std::string compareExpressionToZero(const ASTContext &Context, const Expr *E, bool Negated)
static bool isMacroID(SourceRange R)
static std::string compareExpressionToNullPtr(const ASTContext &Context, const Expr *E, bool Negated)
static std::optional< bool > tryFixCXXOperator(const Expr *E, SmallVectorImpl< FixItHint > &Fixes)
static std::pair< OverloadedOperatorKind, StringRef > OperatorNames[]
static bool needsStaticCast(const Expr *E)
llvm::StringMap< ClangTidyValue > OptionMap