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