Index: clang-tidy/readability/CMakeLists.txt =================================================================== --- clang-tidy/readability/CMakeLists.txt +++ clang-tidy/readability/CMakeLists.txt @@ -9,6 +9,7 @@ ReadabilityTidyModule.cpp RedundantSmartptrGet.cpp ShrinkToFitCheck.cpp + SimplifyBooleanExpr.cpp LINK_LIBS clangAST Index: clang-tidy/readability/ReadabilityTidyModule.cpp =================================================================== --- clang-tidy/readability/ReadabilityTidyModule.cpp +++ clang-tidy/readability/ReadabilityTidyModule.cpp @@ -16,6 +16,7 @@ #include "FunctionSize.h" #include "RedundantSmartptrGet.h" #include "ShrinkToFitCheck.h" +#include "SimplifyBooleanExpr.h" namespace clang { namespace tidy { @@ -36,6 +37,8 @@ "readability-redundant-smartptr-get"); CheckFactories.registerCheck( "readability-shrink-to-fit"); + CheckFactories.registerCheck( + "readability-simplify-boolean-expr"); } }; Index: clang-tidy/readability/SimplifyBooleanExpr.h =================================================================== --- /dev/null +++ clang-tidy/readability/SimplifyBooleanExpr.h @@ -0,0 +1,103 @@ +//===--- SimplifyBooleanExpr.h clang-tidy -----------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_SIMPLIFY_BOOLEAN_EXPR_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_SIMPLIFY_BOOLEAN_EXPR_H + +#include "../ClangTidy.h" + +namespace clang { +namespace tidy { +namespace readability { + +/// \brief Looks for boolean expressions involving boolean constants and +// simplifies them to use the appropriate boolean expression directly. +/// +/// Examples: +/// if (b == true) becomes if (b) +/// if (b == false) becomes if (!b) +/// if (b && true) becomes if (b) +/// if (b && false) becomes if (false) +/// if (b || true) becomes if (true) +/// if (b || false) becomes if (b) +/// e ? true : false becomes e +/// e ? false : true becomes !e +/// if (true) t(); else f(); becomes t(); +/// if (false) t(); else f(); becomes f(); +/// if (e) return true; else return false; becomes return (e); +/// if (e) return false; else return true; becomes return !(e); +/// +class SimplifyBooleanExpr : public ClangTidyCheck { +public: + SimplifyBooleanExpr(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + +private: + void matchBoolBinOpExpr(ast_matchers::MatchFinder *Finder, bool Value, + const char *OperatorName, const char *BooleanId); + + void matchExprBinOpBool(ast_matchers::MatchFinder *Finder, bool Value, + const char *OperatorName, const char *BooleanId); + + void matchBoolCompOpExpr(ast_matchers::MatchFinder *Finder, bool Value, + const char *OperatorName, const char *BooleanId); + + void matchExprCompOpBool(ast_matchers::MatchFinder *Finder, bool Value, + const char *OperatorName, const char *BooleanId); + + void matchBoolCondition(ast_matchers::MatchFinder *Finder, bool Value, + const char *BooleanId); + + void matchTernaryResult(ast_matchers::MatchFinder *Finder, bool Value, + const char *TernaryId); + + void matchIfReturnsBool(ast_matchers::MatchFinder *Finder, bool Value, + char const *const Id); + + void replaceWithRightExpression( + const ast_matchers::MatchFinder::MatchResult &Result, + const Expr *BoolLiteral, bool Negate = false); + + void replaceWithLeftBooleanLiteral( + const ast_matchers::MatchFinder::MatchResult &Result, + const CXXBoolLiteralExpr *BoolLiteral); + + void replaceWithRightBooleanLiteral( + const ast_matchers::MatchFinder::MatchResult &Result, + const CXXBoolLiteralExpr *BoolLiteral); + + void replaceWithLeftExpression( + const ast_matchers::MatchFinder::MatchResult &Result, + const CXXBoolLiteralExpr *BoolLiteral, bool Negated = false); + + void + replaceWithThenStatement(const ast_matchers::MatchFinder::MatchResult &Result, + const CXXBoolLiteralExpr *BoolLiteral); + + void + replaceWithElseStatement(const ast_matchers::MatchFinder::MatchResult &Result, + const CXXBoolLiteralExpr *FalseConditionRemoved); + + void + replaceWithCondition(const ast_matchers::MatchFinder::MatchResult &Result, + const ConditionalOperator *Ternary, + bool Negated = false); + + void replaceWithReturnCondition( + const ast_matchers::MatchFinder::MatchResult &Result, const IfStmt *If, + bool Negated = false); +}; + +} // namespace readability +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_SIMPLIFY_BOOLEAN_EXPR_H Index: clang-tidy/readability/SimplifyBooleanExpr.cpp =================================================================== --- /dev/null +++ clang-tidy/readability/SimplifyBooleanExpr.cpp @@ -0,0 +1,314 @@ +//===--- SimplifyBooleanExpr.cpp clang-tidy ---------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "SimplifyBooleanExpr.h" +#include "clang/Lex/Lexer.h" + +#include + +using namespace clang; +using namespace clang::ast_matchers; + +namespace { + +StringRef getText(const MatchFinder::MatchResult &Result, SourceRange Range) { + return Lexer::getSourceText(CharSourceRange::getTokenRange(Range), + *Result.SourceManager, + Result.Context->getLangOpts()); +} + +template +StringRef getText(const MatchFinder::MatchResult &Result, T &Node) { + return getText(Result, Node.getSourceRange()); +} + +const auto LeftBooleanLiteralId = "bool-op-expr-yields-bool"; +const auto RightExpressionId = "bool-op-expr-yields-expr"; +const auto RightBooleanLiteralId = "expr-op-bool-yields-bool"; +const auto LeftExpressionId = "expr-op-bool-yields-expr"; +const auto NegatedRightExpressionId = "bool-op-expr-yields-not-expr"; +const auto NegatedLeftExpressionId = "expr-op-bool-yields-not-expr"; +const auto ConditionThenStmtId = "if-bool-yields-then"; +const auto ConditionElseStmtId = "if-bool-yields-else"; +const auto TernaryId = "ternary-bool-yields-condition"; +const auto TernaryNegatedId = "ternary-bool-yields-not-condition"; +const auto IfReturnsBoolId = "if-return"; +const auto IfReturnsNotBoolId = "if-not-return"; +const auto ThenLiteralId = "then-literal"; + +const auto IfStmtId = "if"; +const auto ExpressionId = "expr"; + +const auto SimplifyOperatorDiagnostic = + "redundant boolean literal supplied to boolean operator"; +const auto SimplifyConditionDiagnostic = + "redundant boolean literal in if statement condition"; + +CXXBoolLiteralExpr const *getBoolLiteral(MatchFinder::MatchResult const &Result, + const char *const Id) { + CXXBoolLiteralExpr const *Literal = + Result.Nodes.getNodeAs(Id); + return Literal && + Result.SourceManager->isMacroBodyExpansion( + Literal->getLocStart()) + ? nullptr + : Literal; +} + +internal::BindableMatcher +SimpleReturnsBool(bool Value, char const *const Id = nullptr) { + return returnStmt(has(boolLiteral(equals(Value)).bind(Id ? Id : "ignored"))); +} + +internal::VariadicOperatorMatcher, + internal::BindableMatcher> +ReturnsBool(bool Value, char const *const Id = nullptr) { + return anyOf( + SimpleReturnsBool(Value, Id), + compoundStmt(statementCountIs(1), has(SimpleReturnsBool(Value, Id)))); +} + +} // namespace + +namespace clang { +namespace tidy { +namespace readability { + +void SimplifyBooleanExpr::matchBoolBinOpExpr(MatchFinder *Finder, bool Value, + const char *const OperatorName, + const char *const BooleanId) { + Finder->addMatcher( + binaryOperator(isExpansionInMainFile(), hasOperatorName(OperatorName), + hasLHS(boolLiteral(equals(Value)).bind(BooleanId)), + hasRHS(expr().bind(ExpressionId)), + unless(hasRHS(hasDescendant(boolLiteral())))), + this); +} + +void SimplifyBooleanExpr::matchExprBinOpBool(MatchFinder *Finder, bool Value, + const char *const OperatorName, + const char *const BooleanId) { + Finder->addMatcher( + binaryOperator(isExpansionInMainFile(), hasOperatorName(OperatorName), + hasLHS(expr().bind(ExpressionId)), + unless(hasLHS(boolLiteral())), + unless(hasLHS(hasDescendant(boolLiteral()))), + hasRHS(boolLiteral(equals(Value)).bind(BooleanId))), + this); +} + +void SimplifyBooleanExpr::matchBoolCompOpExpr(MatchFinder *Finder, bool Value, + const char *const OperatorName, + const char *const BooleanId) { + Finder->addMatcher( + binaryOperator( + isExpansionInMainFile(), hasOperatorName(OperatorName), + hasLHS(hasDescendant(boolLiteral(equals(Value)).bind(BooleanId))), + hasRHS(expr().bind(ExpressionId)), + unless(hasRHS(hasDescendant(boolLiteral())))), + this); +} + +void SimplifyBooleanExpr::matchExprCompOpBool(MatchFinder *Finder, bool Value, + const char *const OperatorName, + const char *const BooleanId) { + Finder->addMatcher( + binaryOperator( + isExpansionInMainFile(), hasOperatorName(OperatorName), + hasLHS(expr().bind(ExpressionId)), unless(hasLHS(boolLiteral())), + unless(hasLHS(hasDescendant(boolLiteral()))), + hasRHS(hasDescendant(boolLiteral(equals(Value)).bind(BooleanId)))), + this); +} + +void SimplifyBooleanExpr::matchBoolCondition(MatchFinder *Finder, bool Value, + const char *const BooleanId) { + Finder->addMatcher(ifStmt(isExpansionInMainFile(), + hasCondition(boolLiteral(equals(Value)) + .bind(BooleanId))).bind(IfStmtId), + this); +} + +void SimplifyBooleanExpr::matchTernaryResult(MatchFinder *Finder, bool Value, + const char *const TernaryId) { + Finder->addMatcher( + conditionalOperator(isExpansionInMainFile(), + hasTrueExpression(boolLiteral(equals(Value))), + hasFalseExpression(boolLiteral(equals(!Value)))) + .bind(TernaryId), + this); +} + +void SimplifyBooleanExpr::matchIfReturnsBool(MatchFinder *Finder, bool Value, + char const *const Id) { + Finder->addMatcher(ifStmt(isExpansionInMainFile(), + hasThen(ReturnsBool(Value, ThenLiteralId)), + hasElse(ReturnsBool(!Value))).bind(Id), + this); +} + +void SimplifyBooleanExpr::registerMatchers(MatchFinder *Finder) { + matchBoolBinOpExpr(Finder, true, "&&", RightExpressionId); + matchBoolBinOpExpr(Finder, false, "||", RightExpressionId); + matchBoolCompOpExpr(Finder, true, "==", RightExpressionId); + matchBoolCompOpExpr(Finder, false, "!=", RightExpressionId); + + matchBoolBinOpExpr(Finder, false, "&&", LeftBooleanLiteralId); + matchBoolBinOpExpr(Finder, true, "||", LeftBooleanLiteralId); + + matchExprBinOpBool(Finder, false, "&&", RightBooleanLiteralId); + matchExprBinOpBool(Finder, true, "||", RightBooleanLiteralId); + + matchExprBinOpBool(Finder, true, "&&", LeftExpressionId); + matchExprBinOpBool(Finder, false, "||", LeftExpressionId); + matchExprCompOpBool(Finder, true, "==", LeftExpressionId); + matchExprCompOpBool(Finder, false, "!=", LeftExpressionId); + + matchBoolCompOpExpr(Finder, false, "==", NegatedRightExpressionId); + matchBoolCompOpExpr(Finder, true, "!=", NegatedRightExpressionId); + + matchExprCompOpBool(Finder, false, "==", NegatedLeftExpressionId); + matchExprCompOpBool(Finder, true, "!=", NegatedLeftExpressionId); + + matchBoolCondition(Finder, true, ConditionThenStmtId); + matchBoolCondition(Finder, false, ConditionElseStmtId); + + matchTernaryResult(Finder, true, TernaryId); + matchTernaryResult(Finder, false, TernaryNegatedId); + + matchIfReturnsBool(Finder, true, IfReturnsBoolId); + matchIfReturnsBool(Finder, false, IfReturnsNotBoolId); +} + +void SimplifyBooleanExpr::check(const MatchFinder::MatchResult &Result) { + if (auto LeftRemoved = getBoolLiteral(Result, RightExpressionId)) { + replaceWithRightExpression(Result, LeftRemoved); + } else if (auto LeftReplacing = + getBoolLiteral(Result, LeftBooleanLiteralId)) { + replaceWithLeftBooleanLiteral(Result, LeftReplacing); + } else if (auto RightReplacing = + getBoolLiteral(Result, RightBooleanLiteralId)) { + replaceWithRightBooleanLiteral(Result, RightReplacing); + } else if (auto RightRemoved = getBoolLiteral(Result, LeftExpressionId)) { + replaceWithLeftExpression(Result, RightRemoved); + } else if (auto NegatedLeftRemoved = + getBoolLiteral(Result, NegatedRightExpressionId)) { + replaceWithRightExpression(Result, NegatedLeftRemoved, true); + } else if (auto NegatedRightRemoved = + getBoolLiteral(Result, NegatedLeftExpressionId)) { + replaceWithLeftExpression(Result, NegatedRightRemoved, "!"); + } else if (auto TrueConditionRemoved = + getBoolLiteral(Result, ConditionThenStmtId)) { + replaceWithThenStatement(Result, TrueConditionRemoved); + } else if (auto FalseConditionRemoved = + getBoolLiteral(Result, ConditionElseStmtId)) { + replaceWithElseStatement(Result, FalseConditionRemoved); + } else if (auto Ternary = + Result.Nodes.getNodeAs(TernaryId)) { + replaceWithCondition(Result, Ternary); + } else if (auto TernaryNegated = Result.Nodes.getNodeAs( + TernaryNegatedId)) { + replaceWithCondition(Result, TernaryNegated, true); + } else if (auto If = Result.Nodes.getNodeAs(IfReturnsBoolId)) { + replaceWithReturnCondition(Result, If); + } else if (auto If = Result.Nodes.getNodeAs(IfReturnsNotBoolId)) { + replaceWithReturnCondition(Result, If, true); + } +} + +void SimplifyBooleanExpr::replaceWithRightExpression( + const MatchFinder::MatchResult &Result, Expr const *BoolLiteral, + const bool Negated) { + const auto Expression = Result.Nodes.getNodeAs(ExpressionId); + const auto Replacement = (Negated ? "!" : "") + getText(Result, *Expression); + diag(BoolLiteral->getLocStart(), SimplifyOperatorDiagnostic) + << FixItHint::CreateReplacement( + SourceRange(BoolLiteral->getLocStart(), Expression->getLocEnd()), + Replacement.str()); +} + +void SimplifyBooleanExpr::replaceWithLeftBooleanLiteral( + const MatchFinder::MatchResult &Result, + CXXBoolLiteralExpr const *BoolLiteral) { + const auto Expression = Result.Nodes.getNodeAs(ExpressionId); + diag(BoolLiteral->getLocStart(), SimplifyOperatorDiagnostic) + << FixItHint::CreateReplacement( + SourceRange(BoolLiteral->getLocStart(), Expression->getLocEnd()), + getText(Result, *BoolLiteral)); +} + +void SimplifyBooleanExpr::replaceWithRightBooleanLiteral( + const MatchFinder::MatchResult &Result, + CXXBoolLiteralExpr const *BoolLiteral) { + const auto Expression = Result.Nodes.getNodeAs(ExpressionId); + diag(BoolLiteral->getLocStart(), SimplifyOperatorDiagnostic) + << FixItHint::CreateReplacement( + SourceRange(Expression->getLocStart(), BoolLiteral->getLocEnd()), + getText(Result, *BoolLiteral)); +} + +void SimplifyBooleanExpr::replaceWithLeftExpression( + const MatchFinder::MatchResult &Result, + CXXBoolLiteralExpr const *BoolLiteral, const bool Negated) { + const auto Expression = Result.Nodes.getNodeAs(ExpressionId); + const auto Replacement = (Negated ? "!" : "") + getText(Result, *Expression); + diag(BoolLiteral->getLocStart(), SimplifyOperatorDiagnostic) + << FixItHint::CreateReplacement( + SourceRange(Expression->getLocStart(), BoolLiteral->getLocEnd()), + Replacement.str()); +} + +void SimplifyBooleanExpr::replaceWithThenStatement( + const MatchFinder::MatchResult &Result, + CXXBoolLiteralExpr const *TrueConditionRemoved) { + const auto IfStatement = Result.Nodes.getNodeAs(IfStmtId); + diag(TrueConditionRemoved->getLocStart(), SimplifyConditionDiagnostic) + << FixItHint::CreateReplacement(IfStatement->getSourceRange(), + getText(Result, *IfStatement->getThen())); +} + +void SimplifyBooleanExpr::replaceWithElseStatement( + const MatchFinder::MatchResult &Result, + CXXBoolLiteralExpr const *FalseConditionRemoved) { + const auto IfStatement = Result.Nodes.getNodeAs(IfStmtId); + const auto ElseStatement = IfStatement->getElse(); + diag(FalseConditionRemoved->getLocStart(), SimplifyConditionDiagnostic) + << FixItHint::CreateReplacement( + IfStatement->getSourceRange(), + ElseStatement ? getText(Result, *ElseStatement) : ""); +} + +void SimplifyBooleanExpr::replaceWithCondition( + const MatchFinder::MatchResult &Result, ConditionalOperator const *Ternary, + const bool Negated) { + const auto Replacement = + (Negated ? "!" : "") + getText(Result, *Ternary->getCond()); + diag(Ternary->getTrueExpr()->getLocStart(), + "redundant boolean literal in ternary expression result") + << FixItHint::CreateReplacement(Ternary->getSourceRange(), + Replacement.str()); +} + +void SimplifyBooleanExpr::replaceWithReturnCondition( + const MatchFinder::MatchResult &Result, const IfStmt *If, bool Negated) { + const auto Terminator = + (If->getElse()->getStmtClass() == Stmt::StmtClass::CompoundStmtClass) + ? ";" + : ""; + const auto Replacement = "return " + StringRef(Negated ? "!" : "") + "(" + + getText(Result, *If->getCond()) + ")" + Terminator; + const auto Start = + Result.Nodes.getNodeAs(ThenLiteralId)->getLocStart(); + diag(Start, "redundant boolean literal in conditional return statement") + << FixItHint::CreateReplacement(If->getSourceRange(), Replacement.str()); +} + +} // namespace readability +} // namespace tidy +} // namespace clang Index: test/clang-tidy/readability-simplify-bool-expr.cpp =================================================================== --- /dev/null +++ test/clang-tidy/readability-simplify-bool-expr.cpp @@ -0,0 +1,440 @@ +// RUN: $(dirname %s)/check_clang_tidy.sh %s readability-simplify-boolean-expr %t +// REQUIRES: shell + +bool a1 = false; + +//=-=-=-=-=-=-= operator == +bool aa = false == a1; +// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: redundant boolean literal supplied to boolean operator [readability-simplify-boolean-expr] +// CHECK-FIXES: {{^bool aa = !a1;$}} +bool ab = true == a1; +// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: {{.*}} to boolean operator +// CHECK-FIXES: {{^bool ab = a1;$}} +bool a2 = a1 == false; +// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: {{.*}} to boolean operator +// CHECK-FIXES: {{^bool a2 = !a1;$}} +bool a3 = a1 == true; +// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: {{.*}} to boolean operator +// CHECK-FIXES: {{^bool a3 = a1;$}} + +//=-=-=-=-=-=-= operator != +bool n1 = a1 != false; +// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: {{.*}} to boolean operator +// CHECK-FIXES: {{^bool n1 = a1;$}} +bool n2 = a1 != true; +// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: {{.*}} to boolean operator +// CHECK-FIXES: {{^bool n2 = !a1;$}} +bool n3 = false != a1; +// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: {{.*}} to boolean operator +// CHECK-FIXES: {{^bool n3 = a1;$}} +bool n4 = true != a1; +// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: {{.*}} to boolean operator +// CHECK-FIXES: {{^bool n4 = !a1;$}} + +//=-=-=-=-=-=-= operator || +bool a4 = a1 || false; +// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: {{.*}} to boolean operator +// CHECK-FIXES: {{^bool a4 = a1;$}} +bool a5 = a1 || true; +// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: {{.*}} to boolean operator +// CHECK-FIXES: {{^bool a5 = true;$}} +bool a6 = false || a1; +// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: {{.*}} to boolean operator +// CHECK-FIXES: {{^bool a6 = a1;$}} +bool a7 = true || a1; +// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: {{.*}} to boolean operator +// CHECK-FIXES: {{^bool a7 = true;$}} + +//=-=-=-=-=-=-= operator && +bool a8 = a1 && false; +// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: {{.*}} to boolean operator +// X-CHECK-FIXES: {{^bool a8 = false;$}} +bool a9 = a1 && true; +// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: {{.*}} to boolean operator +// CHECK-FIXES: {{^bool a9 = a1;$}} +bool ac = false && a1; +// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: {{.*}} to boolean operator +// CHECK-FIXES: {{^bool ac = false;$}} +bool ad = true && a1; +// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: {{.*}} to boolean operator +// CHECK-FIXES: {{^bool ad = a1;$}} + +void if_with_bool_literal_condition() +{ + int i = 0; + if (false) { + i = 1; + } else { + i = 2; + } + i = 3; + // CHECK-MESSAGES: :[[@LINE-6]]:9: warning: {{.*}} in if statement condition + // CHECK-FIXES: {{^ int i = 0;$}} + // CHECK-FIXES-NEXT: {{^ {$}} + // CHECK-FIXES-NEXT: {{^ i = 2;$}} + // CHECK-FIXES-NEXT: {{^ }$}} + // CHECK-FIXES-NEXT: {{^ i = 3;$}} + + i = 4; + if (true) { + i = 5; + } else { + i = 6; + } + i = 7; + // CHECK-MESSAGES: :[[@LINE-6]]:9: warning: {{.*}} in if statement condition + // CHECK-FIXES: {{^ i = 4;$}} + // CHECK-FIXES-NEXT: {{^ {$}} + // CHECK-FIXES-NEXT: {{^ i = 5;$}} + // CHECK-FIXES-NEXT: {{^ }$}} + // CHECK-FIXES-NEXT: {{^ i = 7;$}} + + i = 8; + if (false) { + i = 9; + } + i = 11; + // CHECK-MESSAGES: :[[@LINE-4]]:9: warning: {{.*}} in if statement condition + // CHECK-FIXES: {{^ i = 8;$}} + // CHECK-FIXES-NEXT: {{^ $}} + // CHECK-FIXES-NEXT: {{^ i = 11;$}} +} + +void operator_equals() +{ + int i = 0; + bool b1 = (i > 2); + if (b1 == true) { + // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: {{.*}} to boolean operator + // CHECK-FIXES: {{^ if \(b1\) {$}} + i = 5; + } else { + i = 6; + } + bool b2 = (i > 4); + if (b2 == false) { + // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: {{.*}} to boolean operator + // CHECK-FIXES: {{^ if \(!b2\) {$}} + i = 7; + } else { + i = 9; + } + bool b3 = (i > 6); + if (true == b3) { + // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: {{.*}} to boolean operator + // CHECK-FIXES: {{^ if \(b3\) {$}} + i = 10; + } else { + i = 11; + } + bool b4 = (i > 8); + if (false == b4) { + // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: {{.*}} to boolean operator + // CHECK-FIXES: {{^ if \(!b4\) {$}} + i = 12; + } else { + i = 13; + } +} + +void operator_or() +{ + int i = 0; + bool b5 = (i > 10); + if (b5 || false) { + // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: {{.*}} to boolean operator + // CHECK-FIXES: {{^ if \(b5\) {$}} + i = 14; + } else { + i = 15; + } + bool b6 = (i > 10); + if (b6 || true) { + // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: {{.*}} to boolean operator + // CHECK-FIXES: {{^ if \(true\) {$}} + i = 16; + } else { + i = 17; + } + bool b7 = (i > 10); + if (false || b7) { + // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: {{.*}} to boolean operator + // CHECK-FIXES: {{^ if \(b7\) {$}} + i = 18; + } else { + i = 19; + } + bool b8 = (i > 10); + if (true || b8) { + // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: {{.*}} to boolean operator + // X-CHECK-FIXES: {{^ if \(true\) {$}} + i = 20; + } else { + i = 21; + } +} + +void operator_and() +{ + int i = 0; + bool b9 = (i > 20); + if (b9 && false) { + // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: {{.*}} to boolean operator + // CHECK-FIXES: {{^ if \(false\) {$}} + i = 22; + } else { + i = 23; + } + bool ba = (i > 20); + if (ba && true) { + // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: {{.*}} to boolean operator + // CHECK-FIXES: {{^ if \(ba\) {$}} + i = 24; + } else { + i = 25; + } + bool bb = (i > 20); + if (false && bb) { + // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: {{.*}} to boolean operator + // CHECK-FIXES: {{^ if \(false\) {$}} + i = 26; + } else { + i = 27; + } + bool bc = (i > 20); + if (true && bc) { + // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: {{.*}} to boolean operator + // CHECK-FIXES: {{^ if \(bc\) {$}} + i = 28; + } else { + i = 29; + } +} + +void ternary_operator() +{ + int i = 0; + bool bd = (i > 20) ? true : false; + // CHECK-MESSAGES: :[[@LINE-1]]:26: warning: {{.*}} in ternary expression result + // CHECK-FIXES: {{^ bool bd = \(i > 20\);$}} + bool be = (i > 20) ? false : true; + // CHECK-MESSAGES: :[[@LINE-1]]:26: warning: {{.*}} in ternary expression result + // CHECK-FIXES: {{^ bool be = !\(i > 20\);$}} +} + +void operator_not_equal() +{ + int i = 0; + bool bf = (i > 20); + if (false != bf) { + // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: {{.*}} to boolean operator + // CHECK-FIXES: {{^ if \(bf\) {$}} + i = 30; + } else { + i = 31; + } + bool bg = (i > 20); + if (true != bg) { + // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: {{.*}} to boolean operator + // CHECK-FIXES: {{^ if \(!bg\) {$}} + i = 32; + } else { + i = 33; + } + bool bh = (i > 20); + if (bh != false) { + // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: {{.*}} to boolean operator + // CHECK-FIXES: {{^ if \(bh\) {$}} + i = 34; + } else { + i = 35; + } + bool bi = (i > 20); + if (bi != true) { + // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: {{.*}} to boolean operator + // CHECK-FIXES: {{^ if \(!bi\) {$}} + i = 36; + } else { + i = 37; + } +} + +void nested_booleans() +{ + if (false || (true || false)) { + // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: {{.*}} to boolean operator + // CHECK-FIXES: {{^ if \(false \|\| \(true\)\) {$}} + } + if (true && (true || false)) { + // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: {{.*}} to boolean operator + // CHECK-FIXES: {{^ if \(true && \(true\)\) {$}} + } + if (false || (true && false)) { + // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: {{.*}} to boolean operator + // CHECK-FIXES: {{^ if \(false \|\| \(false\)\) {$}} + } + if (true && (true && false)) { + // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: {{.*}} to boolean operator + // CHECK-FIXES: {{^ if \(true && \(false\)\) {$}} + } +} + +static constexpr bool truthy() +{ + return true; +} + +#define HAS_XYZ_FEATURE true + +void macros_and_constexprs(int i = 0) +{ + bool b = (i == 1); + if (b && truthy()) { + // leave this alone; if you want it simplified, then you should + // inline the constexpr function first. + i = 1; + } + i = 2; + if (b && HAS_XYZ_FEATURE) { + // leave this alone; if you want it simplified, then you should + // inline the macro first. + i = 3; + } + i = 4; +} + +bool conditional_return_statements(int i) +{ + if (i == 0) return true; else return false; +} +// CHECK-MESSAGES: :[[@LINE-2]]:24: warning: redundant boolean literal in conditional return statement +// CHECK-FIXES: {{^}} return (i == 0);{{$}} +// CHECK-FIXES-NEXT: {{^}$}} + +bool conditional_return_statements_then_expr(int i, int j) +{ + if (i == j) return (i == 0); else return false; +} + +bool conditional_return_statements_else_expr(int i, int j) +{ + if (i == j) return true; else return (i == 0); +} + +bool negated_conditional_return_statements(int i) +{ + if (i == 0) return false; else return true; +} +// CHECK-MESSAGES: :[[@LINE-2]]:24: warning: redundant boolean literal in conditional return statement +// CHECK-FIXES: {{^}} return !(i == 0);{{$}} +// CHECK-FIXES-NEXT: {{^}$}} + +bool conditional_compound_return_statements(int i) +{ + if (i == 1) { + return true; + } else { + return false; + } +} +// CHECK-MESSAGES: :[[@LINE-5]]:16: warning: redundant boolean literal in conditional return statement +// CHECK-FIXES: {{^{$}} +// CHECK-FIXES-NEXT: {{^}} return (i == 1);{{$}} +// CHECK-FIXES-NEXT: {{^}$}} + +bool negated_conditional_compound_return_statements(int i) +{ + if (i == 1) { + return false; + } else { + return true; + } +} +// CHECK-MESSAGES: :[[@LINE-5]]:16: warning: redundant boolean literal in conditional return statement +// CHECK-FIXES: {{^{$}} +// CHECK-FIXES-NEXT: {{^}} return !(i == 1);{{$}} +// CHECK-FIXES-NEXT: {{^}$}} + +bool conditional_return_statements_side_effects_then(int i) +{ + if (i == 2) { + macros_and_constexprs(); + return true; + } else + return false; +} + +bool negated_conditional_return_statements_side_effects_then(int i) +{ + if (i == 2) { + macros_and_constexprs(); + return false; + } else + return true; +} + +bool conditional_return_statements_side_effects_else(int i) +{ + if (i == 2) + return true; + else { + macros_and_constexprs(); + return false; + } +} + +bool negated_conditional_return_statements_side_effects_else(int i) +{ + if (i == 2) + return false; + else { + macros_and_constexprs(); + return true; + } +} + +void lambda_conditional_return_statements() +{ + auto lambda = [](int n) -> bool { if (n > 0) return true; else return false; }; + // CHECK-MESSAGES: :[[@LINE-1]]:57: warning: redundant boolean literal in conditional return statement + // CHECK-FIXES: {{^}} auto lambda = [](int n) -> bool { return (n > 0); };{{$}} + + auto lambda2 = [](int n) -> bool { + if (n > 0) { + return true; + } else { + return false; + } + }; + // CHECK-MESSAGES: :[[@LINE-5]]:20: warning: redundant boolean literal in conditional return statement + // CHECK-FIXES: {{^}} auto lambda2 = [](int n) -> bool {{{$}} + // CHECK-FIXES-NEXT: {{^}} return (n > 0);{{$}} + // CHECK-FIXES-NEXT: {{^}} };{{$}} + + auto lambda3 = [](int n) -> bool { if (n > 0) {macros_and_constexprs(); return true; } else return false; }; + + auto lambda4 = [](int n) -> bool { + if (n > 0) + return true; + else { + macros_and_constexprs(); + return false; + } + }; + + auto lambda5 = [](int n) -> bool { if (n > 0) return false; else return true; }; + // CHECK-MESSAGES: :[[@LINE-1]]:58: warning: redundant boolean literal in conditional return statement + // CHECK-FIXES: {{^}} auto lambda5 = [](int n) -> bool { return !(n > 0); };{{$}} + + auto lambda6 = [](int n) -> bool { + if (n > 0) { + return false; + } else { + return true; + } + }; + // CHECK-MESSAGES: :[[@LINE-5]]:20: warning: redundant boolean literal in conditional return statement + // CHECK-FIXES: {{^}} auto lambda6 = [](int n) -> bool {{{$}} + // CHECK-FIXES-NEXT: {{^}} return !(n > 0);{{$}} + // CHECK-FIXES-NEXT: {{^}} };{{$}} +}