diff options
author | Alexander Kornienko <alexfh@google.com> | 2015-07-01 12:39:40 +0000 |
---|---|---|
committer | Alexander Kornienko <alexfh@google.com> | 2015-07-01 12:39:40 +0000 |
commit | 6ae400d12256de0547c1b06919d126f664c78d7e (patch) | |
tree | 2c16f7333e11b0f4c9bb2ee046f8b7a89ffd6b94 /clang-tools-extra/clang-tidy/readability/SimplifyBooleanExprCheck.cpp | |
parent | def554db451cafb36b71d95823eaf65538b0a3b7 (diff) | |
download | bcm5719-llvm-6ae400d12256de0547c1b06919d126f664c78d7e.tar.gz bcm5719-llvm-6ae400d12256de0547c1b06919d126f664c78d7e.zip |
[clang-tidy] Enhance clang-tidy readability-simplify-boolean-expr...
Enhance clang-tidy readability-simplify-boolean-expr to handle 'if (e) return
true; return false;' and improve replacement expressions.
This changeset extends the simplify boolean expression check in clang-tidy to
simplify if (e) return true; return false; to return e; (note the lack of an
else clause on the if statement.) By default, chained conditional assignment is
left unchanged, unless a configuration parameter is set to non-zero to override
this behavior.
It also improves the handling of replacement expressions to apply
static_cast<bool>(expr) when expr is not of type bool.
http://reviews.llvm.org/D9810
Patch by Richard Thomson!
llvm-svn: 241155
Diffstat (limited to 'clang-tools-extra/clang-tidy/readability/SimplifyBooleanExprCheck.cpp')
-rw-r--r-- | clang-tools-extra/clang-tidy/readability/SimplifyBooleanExprCheck.cpp | 257 |
1 files changed, 230 insertions, 27 deletions
diff --git a/clang-tools-extra/clang-tidy/readability/SimplifyBooleanExprCheck.cpp b/clang-tools-extra/clang-tidy/readability/SimplifyBooleanExprCheck.cpp index f7754e44332..704a49e8b40 100644 --- a/clang-tools-extra/clang-tidy/readability/SimplifyBooleanExprCheck.cpp +++ b/clang-tools-extra/clang-tidy/readability/SimplifyBooleanExprCheck.cpp @@ -12,6 +12,7 @@ #include <cassert> #include <string> +#include <utility> using namespace clang::ast_matchers; @@ -48,6 +49,9 @@ const char IfAssignLocId[] = "if-assign-loc"; const char IfAssignBoolId[] = "if-assign"; const char IfAssignNotBoolId[] = "if-assign-not"; const char IfAssignObjId[] = "if-assign-obj"; +const char CompoundReturnId[] = "compound-return"; +const char CompoundBoolId[] = "compound-bool"; +const char CompoundNotBoolId[] = "compound-bool-not"; const char IfStmtId[] = "if"; const char LHSId[] = "lhs-expr"; @@ -57,6 +61,8 @@ const char SimplifyOperatorDiagnostic[] = "redundant boolean literal supplied to boolean operator"; const char SimplifyConditionDiagnostic[] = "redundant boolean literal in if statement condition"; +const char SimplifyConditionalReturnDiagnostic[] = + "redundant boolean literal in conditional return statement"; const CXXBoolLiteralExpr *getBoolLiteral(const MatchFinder::MatchResult &Result, StringRef Id) { @@ -67,25 +73,26 @@ const CXXBoolLiteralExpr *getBoolLiteral(const MatchFinder::MatchResult &Result, : Literal; } -internal::Matcher<Stmt> ReturnsBool(bool Value, StringRef Id = "") { - auto SimpleReturnsBool = returnStmt( - has(boolLiteral(equals(Value)).bind(Id.empty() ? "ignored" : Id))); +internal::Matcher<Stmt> returnsBool(bool Value, StringRef Id = "ignored") { + auto SimpleReturnsBool = + returnStmt(has(boolLiteral(equals(Value)).bind(Id))).bind("returns-bool"); return anyOf(SimpleReturnsBool, compoundStmt(statementCountIs(1), has(SimpleReturnsBool))); } bool needsParensAfterUnaryNegation(const Expr *E) { + E = E->IgnoreImpCasts(); if (isa<BinaryOperator>(E) || isa<ConditionalOperator>(E)) return true; - if (const auto *Op = dyn_cast<CXXOperatorCallExpr>(E)) + if (const auto *Op = dyn_cast<CXXOperatorCallExpr>(E)) { return Op->getNumArgs() == 2 && Op->getOperator() != OO_Call && Op->getOperator() != OO_Subscript; + } return false; } std::pair<BinaryOperatorKind, BinaryOperatorKind> Opposites[] = { - std::make_pair(BO_LT, BO_GE), std::make_pair(BO_GT, BO_LE), - std::make_pair(BO_EQ, BO_NE)}; + {BO_LT, BO_GE}, {BO_GT, BO_LE}, {BO_EQ, BO_NE}}; StringRef negatedOperator(const BinaryOperator *BinOp) { const BinaryOperatorKind Opcode = BinOp->getOpcode(); @@ -98,24 +105,153 @@ StringRef negatedOperator(const BinaryOperator *BinOp) { return StringRef(); } +std::pair<OverloadedOperatorKind, StringRef> OperatorNames[] = { + {OO_EqualEqual, "=="}, {OO_ExclaimEqual, "!="}, {OO_Less, "<"}, + {OO_GreaterEqual, ">="}, {OO_Greater, ">"}, {OO_LessEqual, "<="}}; + +StringRef getOperatorName(OverloadedOperatorKind OpKind) { + for (auto Name : OperatorNames) { + if (Name.first == OpKind) + return Name.second; + } + + return StringRef(); +} + +std::pair<OverloadedOperatorKind, OverloadedOperatorKind> OppositeOverloads[] = + {{OO_EqualEqual, OO_ExclaimEqual}, + {OO_Less, OO_GreaterEqual}, + {OO_Greater, OO_LessEqual}}; + +StringRef negatedOperator(const CXXOperatorCallExpr *OpCall) { + const OverloadedOperatorKind Opcode = OpCall->getOperator(); + for (auto NegatableOp : OppositeOverloads) { + if (Opcode == NegatableOp.first) + return getOperatorName(NegatableOp.second); + if (Opcode == NegatableOp.second) + return getOperatorName(NegatableOp.first); + } + return StringRef(); +} + +std::string asBool(StringRef text, bool NeedsStaticCast) { + if (NeedsStaticCast) + return ("static_cast<bool>(" + text + ")").str(); + + return text; +} + +bool needsNullPtrComparison(const Expr *E) { + if (const auto *ImpCast = dyn_cast<ImplicitCastExpr>(E)) + return ImpCast->getCastKind() == CK_PointerToBoolean; + + return false; +} + +bool needsStaticCast(const Expr *E) { + if (const auto *ImpCast = dyn_cast<ImplicitCastExpr>(E)) { + if (ImpCast->getCastKind() == CK_UserDefinedConversion && + ImpCast->getSubExpr()->getType()->isBooleanType()) { + if (const auto *MemCall = + dyn_cast<CXXMemberCallExpr>(ImpCast->getSubExpr())) { + if (const auto *MemDecl = + dyn_cast<CXXConversionDecl>(MemCall->getMethodDecl())) { + if (MemDecl->isExplicit()) + return true; + } + } + } + } + + E = E->IgnoreImpCasts(); + return !E->getType()->isBooleanType(); +} + std::string replacementExpression(const MatchFinder::MatchResult &Result, bool Negated, const Expr *E) { - while (const auto *Parenthesized = dyn_cast<ParenExpr>(E)) { - E = Parenthesized->getSubExpr(); - } + E = E->ignoreParenBaseCasts(); + const bool NeedsStaticCast = needsStaticCast(E); if (Negated) { + if (const auto *UnOp = dyn_cast<UnaryOperator>(E)) { + if (UnOp->getOpcode() == UO_LNot) { + if (needsNullPtrComparison(UnOp->getSubExpr())) + return (getText(Result, *UnOp->getSubExpr()) + " != nullptr").str(); + + return replacementExpression(Result, false, UnOp->getSubExpr()); + } + } + + if (needsNullPtrComparison(E)) + return (getText(Result, *E) + " == nullptr").str(); + + StringRef NegatedOperator; + const Expr *LHS = nullptr; + const Expr *RHS = nullptr; if (const auto *BinOp = dyn_cast<BinaryOperator>(E)) { - StringRef NegatedOperator = negatedOperator(BinOp); - if (!NegatedOperator.empty()) { - return (getText(Result, *BinOp->getLHS()) + " " + NegatedOperator + - " " + getText(Result, *BinOp->getRHS())).str(); + NegatedOperator = negatedOperator(BinOp); + LHS = BinOp->getLHS(); + RHS = BinOp->getRHS(); + } else if (const auto *OpExpr = dyn_cast<CXXOperatorCallExpr>(E)) { + if (OpExpr->getNumArgs() == 2) { + NegatedOperator = negatedOperator(OpExpr); + LHS = OpExpr->getArg(0); + RHS = OpExpr->getArg(1); } } + if (!NegatedOperator.empty() && LHS && RHS) { + return (asBool((getText(Result, *LHS) + " " + NegatedOperator + " " + + getText(Result, *RHS)) + .str(), + NeedsStaticCast)); + } + + StringRef Text = getText(Result, *E); + if (!NeedsStaticCast && needsParensAfterUnaryNegation(E)) + return ("!(" + Text + ")").str(); + + if (needsNullPtrComparison(E)) + return (getText(Result, *E) + " == nullptr").str(); + + return ("!" + asBool(Text, NeedsStaticCast)); } - StringRef Text = getText(Result, *E); - return (Negated ? (needsParensAfterUnaryNegation(E) ? "!(" + Text + ")" - : "!" + Text) - : Text).str(); + + if (const auto *UnOp = dyn_cast<UnaryOperator>(E)) { + if (UnOp->getOpcode() == UO_LNot) { + if (needsNullPtrComparison(UnOp->getSubExpr())) + return (getText(Result, *UnOp->getSubExpr()) + " == nullptr").str(); + } + } + + if (needsNullPtrComparison(E)) + return (getText(Result, *E) + " != nullptr").str(); + + return asBool(getText(Result, *E), NeedsStaticCast); +} + +const CXXBoolLiteralExpr *stmtReturnsBool(const ReturnStmt *Ret, bool Negated) { + if (const auto *Bool = dyn_cast<CXXBoolLiteralExpr>(Ret->getRetValue())) { + if (Bool->getValue() == !Negated) + return Bool; + } + + return nullptr; +} + +const CXXBoolLiteralExpr *stmtReturnsBool(const IfStmt *IfRet, bool Negated) { + if (IfRet->getElse() != nullptr) + return nullptr; + + if (const auto *Ret = dyn_cast<ReturnStmt>(IfRet->getThen())) + return stmtReturnsBool(Ret, Negated); + + if (const auto *Compound = dyn_cast<CompoundStmt>(IfRet->getThen())) { + if (Compound->size() == 1) { + if (const auto *CompoundRet = dyn_cast<ReturnStmt>(Compound->body_back())) + return stmtReturnsBool(CompoundRet, Negated); + } + } + + return nullptr; } } // namespace @@ -185,10 +321,11 @@ void SimplifyBooleanExprCheck::matchExprCompOpBool(MatchFinder *Finder, void SimplifyBooleanExprCheck::matchBoolCondition(MatchFinder *Finder, bool Value, StringRef BooleanId) { - Finder->addMatcher(ifStmt(isExpansionInMainFile(), - hasCondition(boolLiteral(equals(Value)) - .bind(BooleanId))).bind(IfStmtId), - this); + Finder->addMatcher( + ifStmt(isExpansionInMainFile(), + hasCondition(boolLiteral(equals(Value)).bind(BooleanId))) + .bind(IfStmtId), + this); } void SimplifyBooleanExprCheck::matchTernaryResult(MatchFinder *Finder, @@ -206,14 +343,16 @@ void SimplifyBooleanExprCheck::matchIfReturnsBool(MatchFinder *Finder, bool Value, StringRef Id) { if (ChainedConditionalReturn) { Finder->addMatcher(ifStmt(isExpansionInMainFile(), - hasThen(ReturnsBool(Value, ThenLiteralId)), - hasElse(ReturnsBool(!Value))).bind(Id), + hasThen(returnsBool(Value, ThenLiteralId)), + hasElse(returnsBool(!Value))) + .bind(Id), this); } else { Finder->addMatcher(ifStmt(isExpansionInMainFile(), unless(hasParent(ifStmt())), - hasThen(ReturnsBool(Value, ThenLiteralId)), - hasElse(ReturnsBool(!Value))).bind(Id), + hasThen(returnsBool(Value, ThenLiteralId)), + hasElse(returnsBool(!Value))) + .bind(Id), this); } } @@ -240,11 +379,25 @@ void SimplifyBooleanExprCheck::matchIfAssignsBool(MatchFinder *Finder, } else { Finder->addMatcher(ifStmt(isExpansionInMainFile(), unless(hasParent(ifStmt())), hasThen(Then), - hasElse(Else)).bind(Id), + hasElse(Else)) + .bind(Id), this); } } +void SimplifyBooleanExprCheck::matchCompoundIfReturnsBool(MatchFinder *Finder, + bool Value, + StringRef Id) { + Finder->addMatcher( + compoundStmt( + allOf(hasAnySubstatement(ifStmt(hasThen(returnsBool(Value)), + unless(hasElse(stmt())))), + hasAnySubstatement(returnStmt(has(boolLiteral(equals(!Value)))) + .bind(CompoundReturnId)))) + .bind(Id), + this); +} + void SimplifyBooleanExprCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { Options.store(Opts, "ChainedConditionalReturn", ChainedConditionalReturn); Options.store(Opts, "ChainedConditionalAssignment", @@ -283,6 +436,9 @@ void SimplifyBooleanExprCheck::registerMatchers(MatchFinder *Finder) { matchIfAssignsBool(Finder, true, IfAssignBoolId); matchIfAssignsBool(Finder, false, IfAssignNotBoolId); + + matchCompoundIfReturnsBool(Finder, true, CompoundBoolId); + matchCompoundIfReturnsBool(Finder, false, CompoundNotBoolId); } void SimplifyBooleanExprCheck::check(const MatchFinder::MatchResult &Result) { @@ -322,6 +478,12 @@ void SimplifyBooleanExprCheck::check(const MatchFinder::MatchResult &Result) { } else if (const auto *IfAssignNot = Result.Nodes.getNodeAs<IfStmt>(IfAssignNotBoolId)) { replaceWithAssignment(Result, IfAssignNot, true); + } else if (const auto *Compound = + Result.Nodes.getNodeAs<CompoundStmt>(CompoundBoolId)) { + replaceCompoundReturnWithCondition(Result, Compound); + } else if (const auto *Compound = + Result.Nodes.getNodeAs<CompoundStmt>(CompoundNotBoolId)) { + replaceCompoundReturnWithCondition(Result, Compound, true); } } @@ -375,10 +537,51 @@ void SimplifyBooleanExprCheck::replaceWithReturnCondition( std::string Replacement = ("return " + Condition + Terminator).str(); SourceLocation Start = Result.Nodes.getNodeAs<CXXBoolLiteralExpr>(ThenLiteralId)->getLocStart(); - diag(Start, "redundant boolean literal in conditional return statement") + diag(Start, SimplifyConditionalReturnDiagnostic) << FixItHint::CreateReplacement(If->getSourceRange(), Replacement); } +void SimplifyBooleanExprCheck::replaceCompoundReturnWithCondition( + const MatchFinder::MatchResult &Result, const CompoundStmt *Compound, + bool Negated) { + const auto *Ret = Result.Nodes.getNodeAs<ReturnStmt>(CompoundReturnId); + + // The body shouldn't be empty because the matcher ensures that it must + // contain at least two statements: + // 1) A `return` statement returning a boolean literal `false` or `true` + // 2) An `if` statement with no `else` clause that consists fo a single + // `return` statement returning the opposite boolean literal `true` or + // `false`. + assert(Compound->size() >= 2); + const IfStmt *BeforeIf = nullptr; + CompoundStmt::const_body_iterator Current = Compound->body_begin(); + CompoundStmt::const_body_iterator After = Compound->body_begin(); + for (++After; After != Compound->body_end() && *Current != Ret; + ++Current, ++After) { + if (const auto *If = dyn_cast<IfStmt>(*Current)) { + if (const CXXBoolLiteralExpr *Lit = stmtReturnsBool(If, Negated)) { + if (*After == Ret) { + if (!ChainedConditionalReturn && BeforeIf) + continue; + + const Expr *Condition = If->getCond(); + std::string Replacement = + "return " + replacementExpression(Result, Negated, Condition); + diag(Lit->getLocStart(), SimplifyConditionalReturnDiagnostic) + << FixItHint::CreateReplacement( + SourceRange(If->getLocStart(), Ret->getLocEnd()), + Replacement); + return; + } + + BeforeIf = If; + } + } else { + BeforeIf = nullptr; + } + } +} + void SimplifyBooleanExprCheck::replaceWithAssignment( const MatchFinder::MatchResult &Result, const IfStmt *IfAssign, bool Negated) { |