10#include "clang/AST/RecursiveASTVisitor.h"
11#include "clang/Lex/Lexer.h"
12#include "llvm/Support/SaveAndRestore.h"
24StringRef getText(
const ASTContext &Context, SourceRange Range) {
25 return Lexer::getSourceText(CharSourceRange::getTokenRange(Range),
26 Context.getSourceManager(),
27 Context.getLangOpts());
30template <
typename T> StringRef getText(
const ASTContext &Context, T &Node) {
31 return getText(Context,
Node.getSourceRange());
37 "redundant boolean literal supplied to boolean operator";
39 "redundant boolean literal in if statement condition";
41 "redundant boolean literal in conditional return statement";
44 E =
E->IgnoreImpCasts();
45 if (isa<BinaryOperator>(
E) || isa<ConditionalOperator>(
E))
48 if (
const auto *Op = dyn_cast<CXXOperatorCallExpr>(
E))
49 return Op->getNumArgs() == 2 && Op->getOperator() != OO_Call &&
50 Op->getOperator() != OO_Subscript;
55static std::pair<BinaryOperatorKind, BinaryOperatorKind>
Opposites[] = {
56 {BO_LT, BO_GE}, {BO_GT, BO_LE}, {BO_EQ, BO_NE}};
59 const BinaryOperatorKind Opcode = BinOp->getOpcode();
61 if (Opcode == NegatableOp.first)
62 return BinaryOperator::getOpcodeStr(NegatableOp.second);
63 if (Opcode == NegatableOp.second)
64 return BinaryOperator::getOpcodeStr(NegatableOp.first);
70 {OO_EqualEqual,
"=="}, {OO_ExclaimEqual,
"!="}, {OO_Less,
"<"},
71 {OO_GreaterEqual,
">="}, {OO_Greater,
">"}, {OO_LessEqual,
"<="}};
75 if (
Name.first == OpKind)
82static std::pair<OverloadedOperatorKind, OverloadedOperatorKind>
84 {OO_Less, OO_GreaterEqual},
85 {OO_Greater, OO_LessEqual}};
88 const OverloadedOperatorKind Opcode = OpCall->getOperator();
90 if (Opcode == NegatableOp.first)
92 if (Opcode == NegatableOp.second)
98static std::string
asBool(StringRef
Text,
bool NeedsStaticCast) {
100 return (
"static_cast<bool>(" +
Text +
")").str();
102 return std::string(
Text);
106 if (
const auto *ImpCast = dyn_cast<ImplicitCastExpr>(
E))
107 return ImpCast->getCastKind() == CK_PointerToBoolean ||
108 ImpCast->getCastKind() == CK_MemberPointerToBoolean;
114 if (
const auto *ImpCast = dyn_cast<ImplicitCastExpr>(
E))
115 return ImpCast->getCastKind() == CK_IntegralToBoolean;
121 if (
const auto *ImpCast = dyn_cast<ImplicitCastExpr>(
E)) {
122 if (ImpCast->getCastKind() == CK_UserDefinedConversion &&
123 ImpCast->getSubExpr()->getType()->isBooleanType()) {
124 if (
const auto *MemCall =
125 dyn_cast<CXXMemberCallExpr>(ImpCast->getSubExpr())) {
126 if (
const auto *MemDecl =
127 dyn_cast<CXXConversionDecl>(MemCall->getMethodDecl())) {
128 if (MemDecl->isExplicit())
135 E =
E->IgnoreImpCasts();
136 return !
E->getType()->isBooleanType();
140 const Expr *
E,
bool Negated,
141 const char *Constant) {
142 E =
E->IgnoreImpCasts();
143 const std::string ExprText =
144 (isa<BinaryOperator>(
E) ? (
"(" + getText(Context, *
E) +
")")
145 : getText(Context, *
E))
147 return ExprText +
" " + (Negated ?
"!=" :
"==") +
" " + Constant;
151 const Expr *
E,
bool Negated) {
152 const char *NullPtr = Context.getLangOpts().CPlusPlus11 ?
"nullptr" :
"NULL";
157 const Expr *
E,
bool Negated) {
162 bool Negated,
const Expr *
E) {
163 E =
E->IgnoreParenBaseCasts();
164 if (
const auto *EC = dyn_cast<ExprWithCleanups>(
E))
165 E = EC->getSubExpr();
169 if (
const auto *UnOp = dyn_cast<UnaryOperator>(
E)) {
170 if (UnOp->getOpcode() == UO_LNot) {
187 StringRef NegatedOperator;
188 const Expr *LHS =
nullptr;
189 const Expr *RHS =
nullptr;
190 if (
const auto *BinOp = dyn_cast<BinaryOperator>(
E)) {
192 LHS = BinOp->getLHS();
193 RHS = BinOp->getRHS();
194 }
else if (
const auto *OpExpr = dyn_cast<CXXOperatorCallExpr>(
E)) {
195 if (OpExpr->getNumArgs() == 2) {
197 LHS = OpExpr->getArg(0);
198 RHS = OpExpr->getArg(1);
201 if (!NegatedOperator.empty() && LHS && RHS)
202 return (
asBool((getText(Context, *LHS) +
" " + NegatedOperator +
" " +
203 getText(Context, *RHS))
207 StringRef
Text = getText(Context, *
E);
209 return (
"!(" +
Text +
")").str();
220 if (
const auto *UnOp = dyn_cast<UnaryOperator>(
E)) {
221 if (UnOp->getOpcode() == UO_LNot) {
236 return asBool(getText(Context, *
E), NeedsStaticCast);
240 CharSourceRange CharRange) {
241 std::string ReplacementText =
242 Lexer::getSourceText(CharRange, Context.getSourceManager(),
243 Context.getLangOpts())
245 Lexer Lex(CharRange.getBegin(), Context.getLangOpts(), ReplacementText.data(),
246 ReplacementText.data(),
247 ReplacementText.data() + ReplacementText.size());
248 Lex.SetCommentRetentionState(
true);
251 while (!Lex.LexFromRawLexer(Tok)) {
252 if (Tok.is(tok::TokenKind::comment) || Tok.is(tok::TokenKind::hash))
260 using Base = RecursiveASTVisitor<Visitor>;
264 : Check(Check), Context(Context) {}
269 switch (S->getStmtClass()) {
270 case Stmt::ImplicitCastExprClass:
271 case Stmt::MaterializeTemporaryExprClass:
272 case Stmt::CXXBindTemporaryExprClass:
283 if (Check->IgnoreMacros && S->getBeginLoc().isMacroID()) {
287 StmtStack.push_back(S);
293 assert(StmtStack.back() == S);
294 StmtStack.pop_back();
300 Check->reportBinOp(Context, Op);
306 if (
const auto *Bool = dyn_cast<CXXBoolLiteralExpr>(
E)) {
307 if (FilterMacro && Bool->getBeginLoc().isMacroID())
309 return Bool->getValue();
311 if (
const auto *UnaryOp = dyn_cast<UnaryOperator>(
E)) {
312 if (FilterMacro && UnaryOp->getBeginLoc().isMacroID())
314 if (UnaryOp->getOpcode() == UO_LNot)
316 UnaryOp->getSubExpr()->IgnoreImplicit(), FilterMacro))
326 operator bool()
const {
return Item !=
nullptr; }
334 const auto *RS = dyn_cast<ReturnStmt>(S);
335 if (!RS || !RS->getRetValue())
337 if (std::optional<bool> Ret =
339 return {RS->getRetValue(), *Ret};
347 template <
typename Functor>
349 if (
auto *CS = dyn_cast<CompoundStmt>(S)) {
351 return F(CS->body_front());
358 return StmtStack.size() < 2 ? nullptr : StmtStack[StmtStack.size() - 2];
364 if (If->hasInitStorage() || If->hasVarStorage() || If->isConsteval())
371 Expr *Cond = If->getCond()->IgnoreImplicit();
374 Check->replaceWithThenStatement(Context, If, Cond);
376 Check->replaceWithElseStatement(Context, If, Cond);
388 if (ElseReturnBool && ThenReturnBool.
Bool != ElseReturnBool.
Bool) {
389 if (Check->ChainedConditionalReturn ||
390 !isa_and_nonnull<IfStmt>(
parent())) {
391 Check->replaceWithReturnCondition(Context, If, ThenReturnBool.Item,
392 ElseReturnBool.
Bool);
402 auto VarBoolAssignmentMatcher = [&Var,
404 const auto *BO = dyn_cast<BinaryOperator>(S);
405 if (!BO || BO->getOpcode() != BO_Assign)
407 std::optional<bool> RightasBool =
411 Expr *IgnImp = BO->getLHS()->IgnoreImplicit();
414 Loc = BO->getRHS()->getBeginLoc();
417 if (
auto *DRE = dyn_cast<DeclRefExpr>(IgnImp))
418 return {DRE->getDecl(), *RightasBool};
419 if (
auto *ME = dyn_cast<MemberExpr>(IgnImp))
420 return {ME->getMemberDecl(), *RightasBool};
427 if (ElseAssignment.
Item == ThenAssignment.Item &&
428 ElseAssignment.
Bool != ThenAssignment.Bool) {
429 if (Check->ChainedConditionalAssignment ||
430 !isa_and_nonnull<IfStmt>(
parent())) {
431 Check->replaceWithAssignment(Context, If, Var,
Loc,
432 ElseAssignment.
Bool);
446 if (std::optional<bool> Then =
448 if (std::optional<bool> Else =
451 Check->replaceWithCondition(Context, Cond, *Else);
460 bool CurIf =
false, PrevIf =
false;
461 for (
auto First = CS->body_begin(), Second = std::next(First),
462 End = CS->body_end();
463 Second != End; ++Second, ++First) {
465 CurIf = isa<IfStmt>(*First);
467 if (!TrailingReturnBool)
475 auto *If = cast<IfStmt>(*First);
476 if (!If->hasInitStorage() && !If->hasVarStorage() &&
477 !If->isConsteval()) {
480 if (ThenReturnBool &&
481 ThenReturnBool.
Bool != TrailingReturnBool.
Bool) {
482 if ((Check->ChainedConditionalReturn || !PrevIf) &&
483 If->getElse() ==
nullptr) {
484 Check->replaceCompoundReturnWithCondition(
485 Context, cast<ReturnStmt>(*Second), TrailingReturnBool.
Bool,
486 If, ThenReturnBool.
Item);
490 }
else if (isa<LabelStmt, CaseStmt, DefaultStmt>(*First)) {
496 isa<LabelStmt>(*First) ? cast<LabelStmt>(*First)->getSubStmt()
497 : isa<CaseStmt>(*First) ? cast<CaseStmt>(*First)->getSubStmt()
498 : cast<DefaultStmt>(*First)->getSubStmt();
499 auto *SubIf = dyn_cast<IfStmt>(SubStmt);
500 if (SubIf && !SubIf->getElse() && !SubIf->hasInitStorage() &&
501 !SubIf->hasVarStorage() && !SubIf->isConsteval()) {
504 if (ThenReturnBool &&
505 ThenReturnBool.
Bool != TrailingReturnBool.
Bool) {
506 Check->replaceCompoundReturnWithCondition(
507 Context, cast<ReturnStmt>(*Second), TrailingReturnBool.
Bool,
508 SubIf, ThenReturnBool.
Item);
517 return isa<UnaryOperator>(
E) &&
518 cast<UnaryOperator>(
E)->getOpcode() == UO_LNot;
521 template <
typename Functor>
523 return Func(BO->getLHS()) || Func(BO->getRHS());
527 const auto *BO = dyn_cast<BinaryOperator>(
E->IgnoreUnlessSpelledInSource());
530 if (!BO->getType()->isBooleanType())
532 switch (BO->getOpcode()) {
557 if (!Check->SimplifyDeMorgan || Op->getOpcode() != UO_LNot)
558 return Base::TraverseUnaryOperator(Op);
559 Expr *SubImp = Op->getSubExpr()->IgnoreImplicit();
560 auto *Parens = dyn_cast<ParenExpr>(SubImp);
563 ? dyn_cast<BinaryOperator>(Parens->getSubExpr()->IgnoreImplicit())
564 : dyn_cast<BinaryOperator>(SubImp);
565 if (!BinaryOp || !BinaryOp->isLogicalOp() ||
566 !BinaryOp->getType()->isBooleanType())
567 return Base::TraverseUnaryOperator(Op);
568 if (Check->SimplifyDeMorganRelaxed ||
572 if (Check->reportDeMorgan(Context, Op, BinaryOp, !IsProcessing,
parent(),
575 llvm::SaveAndRestore RAII(IsProcessing,
true);
576 return Base::TraverseUnaryOperator(Op);
579 return Base::TraverseUnaryOperator(Op);
583 bool IsProcessing =
false;
585 SmallVector<Stmt *, 32> StmtStack;
592 IgnoreMacros(Options.get(
"IgnoreMacros", false)),
593 ChainedConditionalReturn(Options.get(
"ChainedConditionalReturn", false)),
594 ChainedConditionalAssignment(
595 Options.get(
"ChainedConditionalAssignment", false)),
596 SimplifyDeMorgan(Options.get(
"SimplifyDeMorgan", true)),
597 SimplifyDeMorganRelaxed(Options.get(
"SimplifyDeMorganRelaxed", false)) {
598 if (SimplifyDeMorganRelaxed && !SimplifyDeMorgan)
600 "without 'SimplifyDeMorgan' enabled")
607 E =
E->IgnoreParenImpCasts();
608 if (isa<CXXBoolLiteralExpr>(
E))
610 if (
const auto *BinOp = dyn_cast<BinaryOperator>(
E))
613 if (
const auto *UnaryOp = dyn_cast<UnaryOperator>(
E))
618void SimplifyBooleanExprCheck::reportBinOp(
const ASTContext &Context,
619 const BinaryOperator *Op) {
620 const auto *LHS = Op->getLHS()->IgnoreParenImpCasts();
621 const auto *RHS = Op->getRHS()->IgnoreParenImpCasts();
623 const CXXBoolLiteralExpr *Bool =
nullptr;
624 const Expr *Other =
nullptr;
625 if ((Bool = dyn_cast<CXXBoolLiteralExpr>(LHS)) !=
nullptr)
627 else if ((Bool = dyn_cast<CXXBoolLiteralExpr>(RHS)) !=
nullptr)
632 if (Bool->getBeginLoc().isMacroID())
639 bool BoolValue = Bool->getValue();
641 auto ReplaceWithExpression = [
this, &Context, LHS, RHS,
642 Bool](
const Expr *ReplaceWith,
bool Negated) {
643 std::string Replacement =
645 SourceRange
Range(LHS->getBeginLoc(), RHS->getEndLoc());
650 switch (Op->getOpcode()) {
654 ReplaceWithExpression(Other,
false);
657 ReplaceWithExpression(Bool,
false);
662 ReplaceWithExpression(Bool,
false);
665 ReplaceWithExpression(Other,
false);
669 ReplaceWithExpression(Other, !BoolValue);
673 ReplaceWithExpression(Other, BoolValue);
682 Options.
store(Opts,
"ChainedConditionalReturn", ChainedConditionalReturn);
684 ChainedConditionalAssignment);
685 Options.
store(Opts,
"SimplifyDeMorgan", SimplifyDeMorgan);
686 Options.
store(Opts,
"SimplifyDeMorganRelaxed", SimplifyDeMorganRelaxed);
690 Finder->addMatcher(translationUnitDecl(),
this);
697void SimplifyBooleanExprCheck::issueDiag(
const ASTContext &Context,
699 StringRef Description,
700 SourceRange ReplacementRange,
701 StringRef Replacement) {
702 CharSourceRange CharRange =
703 Lexer::makeFileCharRange(CharSourceRange::getTokenRange(ReplacementRange),
706 DiagnosticBuilder Diag =
diag(
Loc, Description);
708 Diag << FixItHint::CreateReplacement(CharRange, Replacement);
711void SimplifyBooleanExprCheck::replaceWithThenStatement(
712 const ASTContext &Context,
const IfStmt *IfStatement,
713 const Expr *BoolLiteral) {
715 IfStatement->getSourceRange(),
716 getText(Context, *IfStatement->getThen()));
719void SimplifyBooleanExprCheck::replaceWithElseStatement(
720 const ASTContext &Context,
const IfStmt *IfStatement,
721 const Expr *BoolLiteral) {
722 const Stmt *ElseStatement = IfStatement->getElse();
724 IfStatement->getSourceRange(),
725 ElseStatement ? getText(Context, *ElseStatement) :
"");
728void SimplifyBooleanExprCheck::replaceWithCondition(
729 const ASTContext &Context,
const ConditionalOperator *Ternary,
731 std::string Replacement =
733 issueDiag(Context, Ternary->getTrueExpr()->getBeginLoc(),
734 "redundant boolean literal in ternary expression result",
735 Ternary->getSourceRange(), Replacement);
738void SimplifyBooleanExprCheck::replaceWithReturnCondition(
739 const ASTContext &Context,
const IfStmt *If,
const Expr *BoolLiteral,
741 StringRef Terminator = isa<CompoundStmt>(If->getElse()) ?
";" :
"";
744 std::string Replacement = (
"return " +
Condition + Terminator).str();
745 SourceLocation Start = BoolLiteral->getBeginLoc();
747 If->getSourceRange(), Replacement);
750void SimplifyBooleanExprCheck::replaceCompoundReturnWithCondition(
751 const ASTContext &Context,
const ReturnStmt *Ret,
bool Negated,
752 const IfStmt *If,
const Expr *ThenReturn) {
753 const std::string Replacement =
755 issueDiag(Context, ThenReturn->getBeginLoc(),
757 SourceRange(If->getBeginLoc(), Ret->getEndLoc()), Replacement);
760void SimplifyBooleanExprCheck::replaceWithAssignment(
const ASTContext &Context,
761 const IfStmt *IfAssign,
765 SourceRange
Range = IfAssign->getSourceRange();
766 StringRef VariableName = getText(Context, *Var);
767 StringRef Terminator = isa<CompoundStmt>(IfAssign->getElse()) ?
";" :
"";
770 std::string Replacement =
771 (VariableName +
" = " +
Condition + Terminator).str();
772 issueDiag(Context,
Loc,
"redundant boolean literal in conditional assignment",
778 const BinaryOperator *BO) {
779 assert(BO->isLogicalOp());
780 if (BO->getOperatorLoc().isMacroID())
782 Output.push_back(FixItHint::CreateReplacement(
783 BO->getOperatorLoc(), BO->getOpcode() == BO_LAnd ?
"||" :
"&&"));
788 assert(BinaryOperator::isLogicalOp(BO));
789 return BO == BO_LAnd ? BO_LOr : BO_LAnd;
793 const ASTContext &Ctx,
const Expr *
E,
794 std::optional<BinaryOperatorKind> OuterBO);
801 const ASTContext &Ctx,
const BinaryOperator *BinOp,
802 std::optional<BinaryOperatorKind> OuterBO,
803 const ParenExpr *Parens =
nullptr) {
804 switch (BinOp->getOpcode()) {
818 constexpr bool LogicalOpParentheses =
true;
819 if (((*OuterBO == NewOp) || (!LogicalOpParentheses &&
820 (*OuterBO == BO_LOr && NewOp == BO_LAnd))) &&
822 if (!Parens->getLParen().isMacroID() &&
823 !Parens->getRParen().isMacroID()) {
824 Fixes.push_back(FixItHint::CreateRemoval(Parens->getLParen()));
825 Fixes.push_back(FixItHint::CreateRemoval(Parens->getRParen()));
828 if (*OuterBO == BO_LAnd && NewOp == BO_LOr && !Parens) {
829 Fixes.push_back(FixItHint::CreateInsertion(BinOp->getBeginLoc(),
"("));
830 Fixes.push_back(FixItHint::CreateInsertion(
831 Lexer::getLocForEndOfToken(BinOp->getEndLoc(), 0,
832 Ctx.getSourceManager(),
849 if (BinOp->getOperatorLoc().isMacroID())
851 Fixes.push_back(FixItHint::CreateReplacement(
852 BinOp->getOperatorLoc(),
853 BinaryOperator::getOpcodeStr(
854 BinaryOperator::negateComparisonOp(BinOp->getOpcode()))));
860 if (Parens->getBeginLoc().isMacroID())
862 Fixes.push_back(FixItHint::CreateInsertion(Parens->getBeginLoc(),
"!"));
864 if (BinOp->getBeginLoc().isMacroID() || BinOp->getEndLoc().isMacroID())
866 Fixes.append({FixItHint::CreateInsertion(BinOp->getBeginLoc(),
"!("),
867 FixItHint::CreateInsertion(
868 Lexer::getLocForEndOfToken(BinOp->getEndLoc(), 0,
869 Ctx.getSourceManager(),
879 const ASTContext &Ctx,
const Expr *
E,
880 std::optional<BinaryOperatorKind> OuterBO) {
881 if (isa<UnaryOperator>(
E) && cast<UnaryOperator>(
E)->getOpcode() == UO_LNot) {
883 if (cast<UnaryOperator>(
E)->getOperatorLoc().
isMacroID())
886 FixItHint::CreateRemoval(cast<UnaryOperator>(
E)->getOperatorLoc()));
889 if (
const auto *BinOp = dyn_cast<BinaryOperator>(
E)) {
892 if (
const auto *Paren = dyn_cast<ParenExpr>(
E)) {
893 if (
const auto *BinOp = dyn_cast<BinaryOperator>(Paren->getSubExpr())) {
898 if (
E->getBeginLoc().isMacroID())
900 Fixes.push_back(FixItHint::CreateInsertion(
E->getBeginLoc(),
"!"));
905 BinaryOperatorKind NewOuterBinary,
906 const ParenExpr *Parens) {
911 switch (
Parent->getStmtClass()) {
912 case Stmt::BinaryOperatorClass: {
913 const auto *BO = cast<BinaryOperator>(
Parent);
914 if (BO->isAssignmentOp())
918 if (BO->getOpcode() == NewOuterBinary)
922 case Stmt::UnaryOperatorClass:
923 case Stmt::CXXRewrittenBinaryOperatorClass:
930bool SimplifyBooleanExprCheck::reportDeMorgan(
const ASTContext &Context,
931 const UnaryOperator *Outer,
932 const BinaryOperator *
Inner,
935 const ParenExpr *Parens) {
938 assert(
Inner->isLogicalOp());
941 diag(Outer->getBeginLoc(),
942 "boolean expression can be simplified by DeMorgan's theorem");
943 Diag << Outer->getSourceRange();
947 if (Outer->getOperatorLoc().isMacroID())
949 SmallVector<FixItHint> Fixes;
952 Fixes.push_back(FixItHint::CreateRemoval(
953 SourceRange(Outer->getOperatorLoc(), Parens->getLParen())));
954 Fixes.push_back(FixItHint::CreateRemoval(Parens->getRParen()));
956 Fixes.push_back(FixItHint::CreateRemoval(Outer->getOperatorLoc()));
std::pair< Context, Canceler > Inner
llvm::SmallString< 256U > Name
CharSourceRange Range
SourceRange for the file name.
std::string Condition
Condition used after the preprocessor directive.
::clang::DynTypedNode Node
void store(ClangTidyOptions::OptionMap &Options, StringRef LocalName, StringRef Value) const
Stores an option with the check-local name LocalName with string value Value to Options.
Base class for all clang-tidy checks.
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
DiagnosticBuilder configurationDiag(StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning) const
Adds a diagnostic to report errors in the check's configuration.
bool areDiagsSelfContained() const
Returns true when the check is run in a use case when only 1 fix will be applied at a time.
const LangOptions & getLangOpts() const
Returns the language options from the context.
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
bool TraverseUnaryOperator(UnaryOperator *Op)
Visitor(SimplifyBooleanExprCheck *Check, ASTContext &Context)
static bool isUnaryLNot(const Expr *E)
bool VisitConditionalOperator(ConditionalOperator *Cond)
static bool shouldIgnore(Stmt *S)
static std::optional< bool > getAsBoolLiteral(const Expr *E, bool FilterMacro)
bool dataTraverseStmtPost(Stmt *S)
static bool nestedDemorgan(const Expr *E, unsigned NestingLevel)
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...
bool VisitBinaryOperator(const BinaryOperator *Op) const
static ExprAndBool parseReturnLiteralBool(const Stmt *S)
Detect's return (true|false|!true|!false);.
bool VisitIfStmt(IfStmt *If)
Looks for boolean expressions involving boolean constants and simplifies them to use the appropriate ...
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Should store all options supported by this check with their current values or default values for opti...
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
SimplifyBooleanExprCheck(StringRef Name, ClangTidyContext *Context)
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
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 flipDemorganOperator(llvm::SmallVectorImpl< FixItHint > &Output, const BinaryOperator *BO)
Swaps a BinaryOperator opcode from && to || or vice-versa.
static bool needsNullPtrComparison(const Expr *E)
static constexpr char SimplifyConditionalReturnDiagnostic[]
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 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::pair< OverloadedOperatorKind, StringRef > OperatorNames[]
static bool needsStaticCast(const Expr *E)
llvm::StringMap< ClangTidyValue > OptionMap