1 //===--- SimplifyBooleanExpr.cpp clang-tidy ---------------------*- C++ -*-===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 10 #include "SimplifyBooleanExprCheck.h" 11 #include "clang/Lex/Lexer.h" 12 13 #include <cassert> 14 #include <string> 15 #include <utility> 16 17 using namespace clang::ast_matchers; 18 19 namespace clang { 20 namespace tidy { 21 namespace readability { 22 23 namespace { 24 25 StringRef getText(const MatchFinder::MatchResult &Result, SourceRange Range) { 26 return Lexer::getSourceText(CharSourceRange::getTokenRange(Range), 27 *Result.SourceManager, 28 Result.Context->getLangOpts()); 29 } 30 31 template <typename T> 32 StringRef getText(const MatchFinder::MatchResult &Result, T &Node) { 33 return getText(Result, Node.getSourceRange()); 34 } 35 36 const char RightExpressionId[] = "bool-op-expr-yields-expr"; 37 const char LeftExpressionId[] = "expr-op-bool-yields-expr"; 38 const char NegatedRightExpressionId[] = "bool-op-expr-yields-not-expr"; 39 const char NegatedLeftExpressionId[] = "expr-op-bool-yields-not-expr"; 40 const char ConditionThenStmtId[] = "if-bool-yields-then"; 41 const char ConditionElseStmtId[] = "if-bool-yields-else"; 42 const char TernaryId[] = "ternary-bool-yields-condition"; 43 const char TernaryNegatedId[] = "ternary-bool-yields-not-condition"; 44 const char IfReturnsBoolId[] = "if-return"; 45 const char IfReturnsNotBoolId[] = "if-not-return"; 46 const char ThenLiteralId[] = "then-literal"; 47 const char IfAssignVariableId[] = "if-assign-lvalue"; 48 const char IfAssignLocId[] = "if-assign-loc"; 49 const char IfAssignBoolId[] = "if-assign"; 50 const char IfAssignNotBoolId[] = "if-assign-not"; 51 const char IfAssignObjId[] = "if-assign-obj"; 52 const char CompoundReturnId[] = "compound-return"; 53 const char CompoundBoolId[] = "compound-bool"; 54 const char CompoundNotBoolId[] = "compound-bool-not"; 55 56 const char IfStmtId[] = "if"; 57 const char LHSId[] = "lhs-expr"; 58 const char RHSId[] = "rhs-expr"; 59 60 const char SimplifyOperatorDiagnostic[] = 61 "redundant boolean literal supplied to boolean operator"; 62 const char SimplifyConditionDiagnostic[] = 63 "redundant boolean literal in if statement condition"; 64 const char SimplifyConditionalReturnDiagnostic[] = 65 "redundant boolean literal in conditional return statement"; 66 67 const CXXBoolLiteralExpr *getBoolLiteral(const MatchFinder::MatchResult &Result, 68 StringRef Id) { 69 const auto *Literal = Result.Nodes.getNodeAs<CXXBoolLiteralExpr>(Id); 70 return (Literal && 71 Result.SourceManager->isMacroBodyExpansion(Literal->getLocStart())) 72 ? nullptr 73 : Literal; 74 } 75 76 internal::Matcher<Stmt> returnsBool(bool Value, StringRef Id = "ignored") { 77 auto SimpleReturnsBool = 78 returnStmt(has(cxxBoolLiteral(equals(Value)).bind(Id))) 79 .bind("returns-bool"); 80 return anyOf(SimpleReturnsBool, 81 compoundStmt(statementCountIs(1), has(SimpleReturnsBool))); 82 } 83 84 bool needsParensAfterUnaryNegation(const Expr *E) { 85 E = E->IgnoreImpCasts(); 86 if (isa<BinaryOperator>(E) || isa<ConditionalOperator>(E)) 87 return true; 88 if (const auto *Op = dyn_cast<CXXOperatorCallExpr>(E)) { 89 return Op->getNumArgs() == 2 && Op->getOperator() != OO_Call && 90 Op->getOperator() != OO_Subscript; 91 } 92 return false; 93 } 94 95 std::pair<BinaryOperatorKind, BinaryOperatorKind> Opposites[] = { 96 {BO_LT, BO_GE}, {BO_GT, BO_LE}, {BO_EQ, BO_NE}}; 97 98 StringRef negatedOperator(const BinaryOperator *BinOp) { 99 const BinaryOperatorKind Opcode = BinOp->getOpcode(); 100 for (auto NegatableOp : Opposites) { 101 if (Opcode == NegatableOp.first) 102 return BinOp->getOpcodeStr(NegatableOp.second); 103 if (Opcode == NegatableOp.second) 104 return BinOp->getOpcodeStr(NegatableOp.first); 105 } 106 return StringRef(); 107 } 108 109 std::pair<OverloadedOperatorKind, StringRef> OperatorNames[] = { 110 {OO_EqualEqual, "=="}, {OO_ExclaimEqual, "!="}, {OO_Less, "<"}, 111 {OO_GreaterEqual, ">="}, {OO_Greater, ">"}, {OO_LessEqual, "<="}}; 112 113 StringRef getOperatorName(OverloadedOperatorKind OpKind) { 114 for (auto Name : OperatorNames) { 115 if (Name.first == OpKind) 116 return Name.second; 117 } 118 119 return StringRef(); 120 } 121 122 std::pair<OverloadedOperatorKind, OverloadedOperatorKind> OppositeOverloads[] = 123 {{OO_EqualEqual, OO_ExclaimEqual}, 124 {OO_Less, OO_GreaterEqual}, 125 {OO_Greater, OO_LessEqual}}; 126 127 StringRef negatedOperator(const CXXOperatorCallExpr *OpCall) { 128 const OverloadedOperatorKind Opcode = OpCall->getOperator(); 129 for (auto NegatableOp : OppositeOverloads) { 130 if (Opcode == NegatableOp.first) 131 return getOperatorName(NegatableOp.second); 132 if (Opcode == NegatableOp.second) 133 return getOperatorName(NegatableOp.first); 134 } 135 return StringRef(); 136 } 137 138 std::string asBool(StringRef text, bool NeedsStaticCast) { 139 if (NeedsStaticCast) 140 return ("static_cast<bool>(" + text + ")").str(); 141 142 return text; 143 } 144 145 bool needsNullPtrComparison(const Expr *E) { 146 if (const auto *ImpCast = dyn_cast<ImplicitCastExpr>(E)) 147 return ImpCast->getCastKind() == CK_PointerToBoolean; 148 149 return false; 150 } 151 152 bool needsStaticCast(const Expr *E) { 153 if (const auto *ImpCast = dyn_cast<ImplicitCastExpr>(E)) { 154 if (ImpCast->getCastKind() == CK_UserDefinedConversion && 155 ImpCast->getSubExpr()->getType()->isBooleanType()) { 156 if (const auto *MemCall = 157 dyn_cast<CXXMemberCallExpr>(ImpCast->getSubExpr())) { 158 if (const auto *MemDecl = 159 dyn_cast<CXXConversionDecl>(MemCall->getMethodDecl())) { 160 if (MemDecl->isExplicit()) 161 return true; 162 } 163 } 164 } 165 } 166 167 E = E->IgnoreImpCasts(); 168 return !E->getType()->isBooleanType(); 169 } 170 171 std::string replacementExpression(const MatchFinder::MatchResult &Result, 172 bool Negated, const Expr *E) { 173 E = E->ignoreParenBaseCasts(); 174 const bool NeedsStaticCast = needsStaticCast(E); 175 if (Negated) { 176 if (const auto *UnOp = dyn_cast<UnaryOperator>(E)) { 177 if (UnOp->getOpcode() == UO_LNot) { 178 if (needsNullPtrComparison(UnOp->getSubExpr())) 179 return (getText(Result, *UnOp->getSubExpr()) + " != nullptr").str(); 180 181 return replacementExpression(Result, false, UnOp->getSubExpr()); 182 } 183 } 184 185 if (needsNullPtrComparison(E)) 186 return (getText(Result, *E) + " == nullptr").str(); 187 188 StringRef NegatedOperator; 189 const Expr *LHS = nullptr; 190 const Expr *RHS = nullptr; 191 if (const auto *BinOp = dyn_cast<BinaryOperator>(E)) { 192 NegatedOperator = negatedOperator(BinOp); 193 LHS = BinOp->getLHS(); 194 RHS = BinOp->getRHS(); 195 } else if (const auto *OpExpr = dyn_cast<CXXOperatorCallExpr>(E)) { 196 if (OpExpr->getNumArgs() == 2) { 197 NegatedOperator = negatedOperator(OpExpr); 198 LHS = OpExpr->getArg(0); 199 RHS = OpExpr->getArg(1); 200 } 201 } 202 if (!NegatedOperator.empty() && LHS && RHS) { 203 return (asBool((getText(Result, *LHS) + " " + NegatedOperator + " " + 204 getText(Result, *RHS)) 205 .str(), 206 NeedsStaticCast)); 207 } 208 209 StringRef Text = getText(Result, *E); 210 if (!NeedsStaticCast && needsParensAfterUnaryNegation(E)) 211 return ("!(" + Text + ")").str(); 212 213 if (needsNullPtrComparison(E)) 214 return (getText(Result, *E) + " == nullptr").str(); 215 216 return ("!" + asBool(Text, NeedsStaticCast)); 217 } 218 219 if (const auto *UnOp = dyn_cast<UnaryOperator>(E)) { 220 if (UnOp->getOpcode() == UO_LNot) { 221 if (needsNullPtrComparison(UnOp->getSubExpr())) 222 return (getText(Result, *UnOp->getSubExpr()) + " == nullptr").str(); 223 } 224 } 225 226 if (needsNullPtrComparison(E)) 227 return (getText(Result, *E) + " != nullptr").str(); 228 229 return asBool(getText(Result, *E), NeedsStaticCast); 230 } 231 232 const CXXBoolLiteralExpr *stmtReturnsBool(const ReturnStmt *Ret, bool Negated) { 233 if (const auto *Bool = dyn_cast<CXXBoolLiteralExpr>(Ret->getRetValue())) { 234 if (Bool->getValue() == !Negated) 235 return Bool; 236 } 237 238 return nullptr; 239 } 240 241 const CXXBoolLiteralExpr *stmtReturnsBool(const IfStmt *IfRet, bool Negated) { 242 if (IfRet->getElse() != nullptr) 243 return nullptr; 244 245 if (const auto *Ret = dyn_cast<ReturnStmt>(IfRet->getThen())) 246 return stmtReturnsBool(Ret, Negated); 247 248 if (const auto *Compound = dyn_cast<CompoundStmt>(IfRet->getThen())) { 249 if (Compound->size() == 1) { 250 if (const auto *CompoundRet = dyn_cast<ReturnStmt>(Compound->body_back())) 251 return stmtReturnsBool(CompoundRet, Negated); 252 } 253 } 254 255 return nullptr; 256 } 257 258 } // namespace 259 260 SimplifyBooleanExprCheck::SimplifyBooleanExprCheck(StringRef Name, 261 ClangTidyContext *Context) 262 : ClangTidyCheck(Name, Context), 263 ChainedConditionalReturn(Options.get("ChainedConditionalReturn", 0U)), 264 ChainedConditionalAssignment( 265 Options.get("ChainedConditionalAssignment", 0U)) {} 266 267 void SimplifyBooleanExprCheck::matchBoolBinOpExpr(MatchFinder *Finder, 268 bool Value, 269 StringRef OperatorName, 270 StringRef BooleanId) { 271 Finder->addMatcher( 272 binaryOperator( 273 isExpansionInMainFile(), hasOperatorName(OperatorName), 274 hasLHS(allOf(expr().bind(LHSId), 275 cxxBoolLiteral(equals(Value)).bind(BooleanId))), 276 hasRHS(expr().bind(RHSId)), 277 unless(hasRHS(hasDescendant(cxxBoolLiteral())))), 278 this); 279 } 280 281 void SimplifyBooleanExprCheck::matchExprBinOpBool(MatchFinder *Finder, 282 bool Value, 283 StringRef OperatorName, 284 StringRef BooleanId) { 285 Finder->addMatcher( 286 binaryOperator( 287 isExpansionInMainFile(), hasOperatorName(OperatorName), 288 hasLHS(expr().bind(LHSId)), 289 unless( 290 hasLHS(anyOf(cxxBoolLiteral(), hasDescendant(cxxBoolLiteral())))), 291 hasRHS(allOf(expr().bind(RHSId), 292 cxxBoolLiteral(equals(Value)).bind(BooleanId)))), 293 this); 294 } 295 296 void SimplifyBooleanExprCheck::matchBoolCompOpExpr(MatchFinder *Finder, 297 bool Value, 298 StringRef OperatorName, 299 StringRef BooleanId) { 300 Finder->addMatcher( 301 binaryOperator(isExpansionInMainFile(), hasOperatorName(OperatorName), 302 hasLHS(allOf(expr().bind(LHSId), 303 ignoringImpCasts(cxxBoolLiteral(equals(Value)) 304 .bind(BooleanId)))), 305 hasRHS(expr().bind(RHSId)), 306 unless(hasRHS(hasDescendant(cxxBoolLiteral())))), 307 this); 308 } 309 310 void SimplifyBooleanExprCheck::matchExprCompOpBool(MatchFinder *Finder, 311 bool Value, 312 StringRef OperatorName, 313 StringRef BooleanId) { 314 Finder->addMatcher( 315 binaryOperator(isExpansionInMainFile(), hasOperatorName(OperatorName), 316 unless(hasLHS(hasDescendant(cxxBoolLiteral()))), 317 hasLHS(expr().bind(LHSId)), 318 hasRHS(allOf(expr().bind(RHSId), 319 ignoringImpCasts(cxxBoolLiteral(equals(Value)) 320 .bind(BooleanId))))), 321 this); 322 } 323 324 void SimplifyBooleanExprCheck::matchBoolCondition(MatchFinder *Finder, 325 bool Value, 326 StringRef BooleanId) { 327 Finder->addMatcher( 328 ifStmt(isExpansionInMainFile(), 329 hasCondition(cxxBoolLiteral(equals(Value)).bind(BooleanId))) 330 .bind(IfStmtId), 331 this); 332 } 333 334 void SimplifyBooleanExprCheck::matchTernaryResult(MatchFinder *Finder, 335 bool Value, 336 StringRef TernaryId) { 337 Finder->addMatcher( 338 conditionalOperator(isExpansionInMainFile(), 339 hasTrueExpression(cxxBoolLiteral(equals(Value))), 340 hasFalseExpression(cxxBoolLiteral(equals(!Value)))) 341 .bind(TernaryId), 342 this); 343 } 344 345 void SimplifyBooleanExprCheck::matchIfReturnsBool(MatchFinder *Finder, 346 bool Value, StringRef Id) { 347 if (ChainedConditionalReturn) { 348 Finder->addMatcher(ifStmt(isExpansionInMainFile(), 349 hasThen(returnsBool(Value, ThenLiteralId)), 350 hasElse(returnsBool(!Value))) 351 .bind(Id), 352 this); 353 } else { 354 Finder->addMatcher(ifStmt(isExpansionInMainFile(), 355 unless(hasParent(ifStmt())), 356 hasThen(returnsBool(Value, ThenLiteralId)), 357 hasElse(returnsBool(!Value))) 358 .bind(Id), 359 this); 360 } 361 } 362 363 void SimplifyBooleanExprCheck::matchIfAssignsBool(MatchFinder *Finder, 364 bool Value, StringRef Id) { 365 auto SimpleThen = binaryOperator( 366 hasOperatorName("="), 367 hasLHS(declRefExpr(hasDeclaration(decl().bind(IfAssignObjId)))), 368 hasLHS(expr().bind(IfAssignVariableId)), 369 hasRHS(cxxBoolLiteral(equals(Value)).bind(IfAssignLocId))); 370 auto Then = anyOf(SimpleThen, compoundStmt(statementCountIs(1), 371 hasAnySubstatement(SimpleThen))); 372 auto SimpleElse = binaryOperator( 373 hasOperatorName("="), 374 hasLHS(declRefExpr(hasDeclaration(equalsBoundNode(IfAssignObjId)))), 375 hasRHS(cxxBoolLiteral(equals(!Value)))); 376 auto Else = anyOf(SimpleElse, compoundStmt(statementCountIs(1), 377 hasAnySubstatement(SimpleElse))); 378 if (ChainedConditionalAssignment) { 379 Finder->addMatcher( 380 ifStmt(isExpansionInMainFile(), hasThen(Then), hasElse(Else)).bind(Id), 381 this); 382 } else { 383 Finder->addMatcher(ifStmt(isExpansionInMainFile(), 384 unless(hasParent(ifStmt())), hasThen(Then), 385 hasElse(Else)) 386 .bind(Id), 387 this); 388 } 389 } 390 391 void SimplifyBooleanExprCheck::matchCompoundIfReturnsBool(MatchFinder *Finder, 392 bool Value, 393 StringRef Id) { 394 Finder->addMatcher( 395 compoundStmt(allOf(hasAnySubstatement(ifStmt(hasThen(returnsBool(Value)), 396 unless(hasElse(stmt())))), 397 hasAnySubstatement( 398 returnStmt(has(cxxBoolLiteral(equals(!Value)))) 399 .bind(CompoundReturnId)))) 400 .bind(Id), 401 this); 402 } 403 404 void SimplifyBooleanExprCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { 405 Options.store(Opts, "ChainedConditionalReturn", ChainedConditionalReturn); 406 Options.store(Opts, "ChainedConditionalAssignment", 407 ChainedConditionalAssignment); 408 } 409 410 void SimplifyBooleanExprCheck::registerMatchers(MatchFinder *Finder) { 411 matchBoolBinOpExpr(Finder, true, "&&", RightExpressionId); 412 matchBoolBinOpExpr(Finder, false, "||", RightExpressionId); 413 matchExprBinOpBool(Finder, false, "&&", RightExpressionId); 414 matchExprBinOpBool(Finder, true, "||", RightExpressionId); 415 matchBoolCompOpExpr(Finder, true, "==", RightExpressionId); 416 matchBoolCompOpExpr(Finder, false, "!=", RightExpressionId); 417 418 matchExprBinOpBool(Finder, true, "&&", LeftExpressionId); 419 matchExprBinOpBool(Finder, false, "||", LeftExpressionId); 420 matchBoolBinOpExpr(Finder, false, "&&", LeftExpressionId); 421 matchBoolBinOpExpr(Finder, true, "||", LeftExpressionId); 422 matchExprCompOpBool(Finder, true, "==", LeftExpressionId); 423 matchExprCompOpBool(Finder, false, "!=", LeftExpressionId); 424 425 matchBoolCompOpExpr(Finder, false, "==", NegatedRightExpressionId); 426 matchBoolCompOpExpr(Finder, true, "!=", NegatedRightExpressionId); 427 428 matchExprCompOpBool(Finder, false, "==", NegatedLeftExpressionId); 429 matchExprCompOpBool(Finder, true, "!=", NegatedLeftExpressionId); 430 431 matchBoolCondition(Finder, true, ConditionThenStmtId); 432 matchBoolCondition(Finder, false, ConditionElseStmtId); 433 434 matchTernaryResult(Finder, true, TernaryId); 435 matchTernaryResult(Finder, false, TernaryNegatedId); 436 437 matchIfReturnsBool(Finder, true, IfReturnsBoolId); 438 matchIfReturnsBool(Finder, false, IfReturnsNotBoolId); 439 440 matchIfAssignsBool(Finder, true, IfAssignBoolId); 441 matchIfAssignsBool(Finder, false, IfAssignNotBoolId); 442 443 matchCompoundIfReturnsBool(Finder, true, CompoundBoolId); 444 matchCompoundIfReturnsBool(Finder, false, CompoundNotBoolId); 445 } 446 447 void SimplifyBooleanExprCheck::check(const MatchFinder::MatchResult &Result) { 448 if (const CXXBoolLiteralExpr *LeftRemoved = 449 getBoolLiteral(Result, RightExpressionId)) { 450 replaceWithExpression(Result, LeftRemoved, false); 451 } else if (const CXXBoolLiteralExpr *RightRemoved = 452 getBoolLiteral(Result, LeftExpressionId)) { 453 replaceWithExpression(Result, RightRemoved, true); 454 } else if (const CXXBoolLiteralExpr *NegatedLeftRemoved = 455 getBoolLiteral(Result, NegatedRightExpressionId)) { 456 replaceWithExpression(Result, NegatedLeftRemoved, false, true); 457 } else if (const CXXBoolLiteralExpr *NegatedRightRemoved = 458 getBoolLiteral(Result, NegatedLeftExpressionId)) { 459 replaceWithExpression(Result, NegatedRightRemoved, true, true); 460 } else if (const CXXBoolLiteralExpr *TrueConditionRemoved = 461 getBoolLiteral(Result, ConditionThenStmtId)) { 462 replaceWithThenStatement(Result, TrueConditionRemoved); 463 } else if (const CXXBoolLiteralExpr *FalseConditionRemoved = 464 getBoolLiteral(Result, ConditionElseStmtId)) { 465 replaceWithElseStatement(Result, FalseConditionRemoved); 466 } else if (const auto *Ternary = 467 Result.Nodes.getNodeAs<ConditionalOperator>(TernaryId)) { 468 replaceWithCondition(Result, Ternary); 469 } else if (const auto *TernaryNegated = 470 Result.Nodes.getNodeAs<ConditionalOperator>( 471 TernaryNegatedId)) { 472 replaceWithCondition(Result, TernaryNegated, true); 473 } else if (const auto *If = Result.Nodes.getNodeAs<IfStmt>(IfReturnsBoolId)) { 474 replaceWithReturnCondition(Result, If); 475 } else if (const auto *IfNot = 476 Result.Nodes.getNodeAs<IfStmt>(IfReturnsNotBoolId)) { 477 replaceWithReturnCondition(Result, IfNot, true); 478 } else if (const auto *IfAssign = 479 Result.Nodes.getNodeAs<IfStmt>(IfAssignBoolId)) { 480 replaceWithAssignment(Result, IfAssign); 481 } else if (const auto *IfAssignNot = 482 Result.Nodes.getNodeAs<IfStmt>(IfAssignNotBoolId)) { 483 replaceWithAssignment(Result, IfAssignNot, true); 484 } else if (const auto *Compound = 485 Result.Nodes.getNodeAs<CompoundStmt>(CompoundBoolId)) { 486 replaceCompoundReturnWithCondition(Result, Compound); 487 } else if (const auto *Compound = 488 Result.Nodes.getNodeAs<CompoundStmt>(CompoundNotBoolId)) { 489 replaceCompoundReturnWithCondition(Result, Compound, true); 490 } 491 } 492 493 void SimplifyBooleanExprCheck::replaceWithExpression( 494 const ast_matchers::MatchFinder::MatchResult &Result, 495 const CXXBoolLiteralExpr *BoolLiteral, bool UseLHS, bool Negated) { 496 const auto *LHS = Result.Nodes.getNodeAs<Expr>(LHSId); 497 const auto *RHS = Result.Nodes.getNodeAs<Expr>(RHSId); 498 std::string Replacement = 499 replacementExpression(Result, Negated, UseLHS ? LHS : RHS); 500 SourceLocation Start = LHS->getLocStart(); 501 SourceLocation End = RHS->getLocEnd(); 502 diag(BoolLiteral->getLocStart(), SimplifyOperatorDiagnostic) 503 << FixItHint::CreateReplacement(SourceRange(Start, End), Replacement); 504 } 505 506 void SimplifyBooleanExprCheck::replaceWithThenStatement( 507 const MatchFinder::MatchResult &Result, 508 const CXXBoolLiteralExpr *TrueConditionRemoved) { 509 const auto *IfStatement = Result.Nodes.getNodeAs<IfStmt>(IfStmtId); 510 diag(TrueConditionRemoved->getLocStart(), SimplifyConditionDiagnostic) 511 << FixItHint::CreateReplacement(IfStatement->getSourceRange(), 512 getText(Result, *IfStatement->getThen())); 513 } 514 515 void SimplifyBooleanExprCheck::replaceWithElseStatement( 516 const MatchFinder::MatchResult &Result, 517 const CXXBoolLiteralExpr *FalseConditionRemoved) { 518 const auto *IfStatement = Result.Nodes.getNodeAs<IfStmt>(IfStmtId); 519 const Stmt *ElseStatement = IfStatement->getElse(); 520 diag(FalseConditionRemoved->getLocStart(), SimplifyConditionDiagnostic) 521 << FixItHint::CreateReplacement( 522 IfStatement->getSourceRange(), 523 ElseStatement ? getText(Result, *ElseStatement) : ""); 524 } 525 526 void SimplifyBooleanExprCheck::replaceWithCondition( 527 const MatchFinder::MatchResult &Result, const ConditionalOperator *Ternary, 528 bool Negated) { 529 std::string Replacement = 530 replacementExpression(Result, Negated, Ternary->getCond()); 531 diag(Ternary->getTrueExpr()->getLocStart(), 532 "redundant boolean literal in ternary expression result") 533 << FixItHint::CreateReplacement(Ternary->getSourceRange(), Replacement); 534 } 535 536 void SimplifyBooleanExprCheck::replaceWithReturnCondition( 537 const MatchFinder::MatchResult &Result, const IfStmt *If, bool Negated) { 538 StringRef Terminator = isa<CompoundStmt>(If->getElse()) ? ";" : ""; 539 std::string Condition = replacementExpression(Result, Negated, If->getCond()); 540 std::string Replacement = ("return " + Condition + Terminator).str(); 541 SourceLocation Start = 542 Result.Nodes.getNodeAs<CXXBoolLiteralExpr>(ThenLiteralId)->getLocStart(); 543 diag(Start, SimplifyConditionalReturnDiagnostic) 544 << FixItHint::CreateReplacement(If->getSourceRange(), Replacement); 545 } 546 547 void SimplifyBooleanExprCheck::replaceCompoundReturnWithCondition( 548 const MatchFinder::MatchResult &Result, const CompoundStmt *Compound, 549 bool Negated) { 550 const auto *Ret = Result.Nodes.getNodeAs<ReturnStmt>(CompoundReturnId); 551 552 // The body shouldn't be empty because the matcher ensures that it must 553 // contain at least two statements: 554 // 1) A `return` statement returning a boolean literal `false` or `true` 555 // 2) An `if` statement with no `else` clause that consists fo a single 556 // `return` statement returning the opposite boolean literal `true` or 557 // `false`. 558 assert(Compound->size() >= 2); 559 const IfStmt *BeforeIf = nullptr; 560 CompoundStmt::const_body_iterator Current = Compound->body_begin(); 561 CompoundStmt::const_body_iterator After = Compound->body_begin(); 562 for (++After; After != Compound->body_end() && *Current != Ret; 563 ++Current, ++After) { 564 if (const auto *If = dyn_cast<IfStmt>(*Current)) { 565 if (const CXXBoolLiteralExpr *Lit = stmtReturnsBool(If, Negated)) { 566 if (*After == Ret) { 567 if (!ChainedConditionalReturn && BeforeIf) 568 continue; 569 570 const Expr *Condition = If->getCond(); 571 std::string Replacement = 572 "return " + replacementExpression(Result, Negated, Condition); 573 diag(Lit->getLocStart(), SimplifyConditionalReturnDiagnostic) 574 << FixItHint::CreateReplacement( 575 SourceRange(If->getLocStart(), Ret->getLocEnd()), 576 Replacement); 577 return; 578 } 579 580 BeforeIf = If; 581 } 582 } else { 583 BeforeIf = nullptr; 584 } 585 } 586 } 587 588 void SimplifyBooleanExprCheck::replaceWithAssignment( 589 const MatchFinder::MatchResult &Result, const IfStmt *IfAssign, 590 bool Negated) { 591 SourceRange Range = IfAssign->getSourceRange(); 592 StringRef VariableName = 593 getText(Result, *Result.Nodes.getNodeAs<Expr>(IfAssignVariableId)); 594 StringRef Terminator = isa<CompoundStmt>(IfAssign->getElse()) ? ";" : ""; 595 std::string Condition = 596 replacementExpression(Result, Negated, IfAssign->getCond()); 597 std::string Replacement = 598 (VariableName + " = " + Condition + Terminator).str(); 599 SourceLocation Location = 600 Result.Nodes.getNodeAs<CXXBoolLiteralExpr>(IfAssignLocId)->getLocStart(); 601 this->diag(Location, "redundant boolean literal in conditional assignment") 602 << FixItHint::CreateReplacement(Range, Replacement); 603 } 604 605 } // namespace readability 606 } // namespace tidy 607 } // namespace clang 608