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 const bool NeedsStaticCast = needsStaticCast(E); 199 if (Negated) { 200 if (const auto *UnOp = dyn_cast<UnaryOperator>(E)) { 201 if (UnOp->getOpcode() == UO_LNot) { 202 if (needsNullPtrComparison(UnOp->getSubExpr())) 203 return compareExpressionToNullPtr(Result, UnOp->getSubExpr(), true); 204 205 if (needsZeroComparison(UnOp->getSubExpr())) 206 return compareExpressionToZero(Result, UnOp->getSubExpr(), true); 207 208 return replacementExpression(Result, false, UnOp->getSubExpr()); 209 } 210 } 211 212 if (needsNullPtrComparison(E)) 213 return compareExpressionToNullPtr(Result, E, false); 214 215 if (needsZeroComparison(E)) 216 return compareExpressionToZero(Result, E, false); 217 218 StringRef NegatedOperator; 219 const Expr *LHS = nullptr; 220 const Expr *RHS = nullptr; 221 if (const auto *BinOp = dyn_cast<BinaryOperator>(E)) { 222 NegatedOperator = negatedOperator(BinOp); 223 LHS = BinOp->getLHS(); 224 RHS = BinOp->getRHS(); 225 } else if (const auto *OpExpr = dyn_cast<CXXOperatorCallExpr>(E)) { 226 if (OpExpr->getNumArgs() == 2) { 227 NegatedOperator = negatedOperator(OpExpr); 228 LHS = OpExpr->getArg(0); 229 RHS = OpExpr->getArg(1); 230 } 231 } 232 if (!NegatedOperator.empty() && LHS && RHS) 233 return (asBool((getText(Result, *LHS) + " " + NegatedOperator + " " + 234 getText(Result, *RHS)) 235 .str(), 236 NeedsStaticCast)); 237 238 StringRef Text = getText(Result, *E); 239 if (!NeedsStaticCast && needsParensAfterUnaryNegation(E)) 240 return ("!(" + Text + ")").str(); 241 242 if (needsNullPtrComparison(E)) 243 return compareExpressionToNullPtr(Result, E, false); 244 245 if (needsZeroComparison(E)) 246 return compareExpressionToZero(Result, E, false); 247 248 return ("!" + asBool(Text, NeedsStaticCast)); 249 } 250 251 if (const auto *UnOp = dyn_cast<UnaryOperator>(E)) { 252 if (UnOp->getOpcode() == UO_LNot) { 253 if (needsNullPtrComparison(UnOp->getSubExpr())) 254 return compareExpressionToNullPtr(Result, UnOp->getSubExpr(), false); 255 256 if (needsZeroComparison(UnOp->getSubExpr())) 257 return compareExpressionToZero(Result, UnOp->getSubExpr(), false); 258 } 259 } 260 261 if (needsNullPtrComparison(E)) 262 return compareExpressionToNullPtr(Result, E, true); 263 264 if (needsZeroComparison(E)) 265 return compareExpressionToZero(Result, E, true); 266 267 return asBool(getText(Result, *E), NeedsStaticCast); 268 } 269 270 const CXXBoolLiteralExpr *stmtReturnsBool(const ReturnStmt *Ret, bool Negated) { 271 if (const auto *Bool = dyn_cast<CXXBoolLiteralExpr>(Ret->getRetValue())) { 272 if (Bool->getValue() == !Negated) 273 return Bool; 274 } 275 276 return nullptr; 277 } 278 279 const CXXBoolLiteralExpr *stmtReturnsBool(const IfStmt *IfRet, bool Negated) { 280 if (IfRet->getElse() != nullptr) 281 return nullptr; 282 283 if (const auto *Ret = dyn_cast<ReturnStmt>(IfRet->getThen())) 284 return stmtReturnsBool(Ret, Negated); 285 286 if (const auto *Compound = dyn_cast<CompoundStmt>(IfRet->getThen())) { 287 if (Compound->size() == 1) { 288 if (const auto *CompoundRet = dyn_cast<ReturnStmt>(Compound->body_back())) 289 return stmtReturnsBool(CompoundRet, Negated); 290 } 291 } 292 293 return nullptr; 294 } 295 296 bool containsDiscardedTokens(const MatchFinder::MatchResult &Result, 297 CharSourceRange CharRange) { 298 std::string ReplacementText = 299 Lexer::getSourceText(CharRange, *Result.SourceManager, 300 Result.Context->getLangOpts()) 301 .str(); 302 Lexer Lex(CharRange.getBegin(), Result.Context->getLangOpts(), 303 ReplacementText.data(), ReplacementText.data(), 304 ReplacementText.data() + ReplacementText.size()); 305 Lex.SetCommentRetentionState(true); 306 307 Token Tok; 308 while (!Lex.LexFromRawLexer(Tok)) { 309 if (Tok.is(tok::TokenKind::comment) || Tok.is(tok::TokenKind::hash)) 310 return true; 311 } 312 313 return false; 314 } 315 316 } // namespace 317 318 class SimplifyBooleanExprCheck::Visitor : public RecursiveASTVisitor<Visitor> { 319 using Base = RecursiveASTVisitor<Visitor>; 320 321 public: 322 Visitor(SimplifyBooleanExprCheck *Check, 323 const MatchFinder::MatchResult &Result) 324 : Check(Check), Result(Result) {} 325 326 bool VisitBinaryOperator(BinaryOperator *Op) { 327 Check->reportBinOp(Result, Op); 328 return true; 329 } 330 331 private: 332 SimplifyBooleanExprCheck *Check; 333 const MatchFinder::MatchResult &Result; 334 }; 335 336 337 SimplifyBooleanExprCheck::SimplifyBooleanExprCheck(StringRef Name, 338 ClangTidyContext *Context) 339 : ClangTidyCheck(Name, Context), 340 ChainedConditionalReturn(Options.get("ChainedConditionalReturn", 0U)), 341 ChainedConditionalAssignment( 342 Options.get("ChainedConditionalAssignment", 0U)) {} 343 344 bool containsBoolLiteral(const Expr *E) { 345 if (!E) 346 return false; 347 E = E->IgnoreParenImpCasts(); 348 if (isa<CXXBoolLiteralExpr>(E)) 349 return true; 350 if (const auto *BinOp = dyn_cast<BinaryOperator>(E)) 351 return containsBoolLiteral(BinOp->getLHS()) || 352 containsBoolLiteral(BinOp->getRHS()); 353 if (const auto *UnaryOp = dyn_cast<UnaryOperator>(E)) 354 return containsBoolLiteral(UnaryOp->getSubExpr()); 355 return false; 356 } 357 358 void SimplifyBooleanExprCheck::reportBinOp( 359 const MatchFinder::MatchResult &Result, const BinaryOperator *Op) { 360 const auto *LHS = Op->getLHS()->IgnoreParenImpCasts(); 361 const auto *RHS = Op->getRHS()->IgnoreParenImpCasts(); 362 363 const CXXBoolLiteralExpr *Bool = nullptr; 364 const Expr *Other = nullptr; 365 if ((Bool = dyn_cast<CXXBoolLiteralExpr>(LHS))) 366 Other = RHS; 367 else if ((Bool = dyn_cast<CXXBoolLiteralExpr>(RHS))) 368 Other = LHS; 369 else 370 return; 371 372 if (Bool->getLocStart().isMacroID()) 373 return; 374 375 // FIXME: why do we need this? 376 if (!isa<CXXBoolLiteralExpr>(Other) && containsBoolLiteral(Other)) 377 return; 378 379 bool BoolValue = Bool->getValue(); 380 381 auto replaceWithExpression = [this, &Result, LHS, RHS, Bool]( 382 const Expr *ReplaceWith, bool Negated) { 383 std::string Replacement = 384 replacementExpression(Result, Negated, ReplaceWith); 385 SourceRange Range(LHS->getLocStart(), RHS->getLocEnd()); 386 issueDiag(Result, Bool->getLocStart(), SimplifyOperatorDiagnostic, Range, 387 Replacement); 388 }; 389 390 switch (Op->getOpcode()) { 391 case BO_LAnd: 392 if (BoolValue) { 393 // expr && true -> expr 394 replaceWithExpression(Other, /*Negated=*/false); 395 } else { 396 // expr && false -> false 397 replaceWithExpression(Bool, /*Negated=*/false); 398 } 399 break; 400 case BO_LOr: 401 if (BoolValue) { 402 // expr || true -> true 403 replaceWithExpression(Bool, /*Negated=*/false); 404 } else { 405 // expr || false -> expr 406 replaceWithExpression(Other, /*Negated=*/false); 407 } 408 break; 409 case BO_EQ: 410 // expr == true -> expr, expr == false -> !expr 411 replaceWithExpression(Other, /*Negated=*/!BoolValue); 412 break; 413 case BO_NE: 414 // expr != true -> !expr, expr != false -> expr 415 replaceWithExpression(Other, /*Negated=*/BoolValue); 416 break; 417 default: 418 break; 419 } 420 } 421 422 void SimplifyBooleanExprCheck::matchBoolCondition(MatchFinder *Finder, 423 bool Value, 424 StringRef BooleanId) { 425 Finder->addMatcher( 426 ifStmt(isExpansionInMainFile(), 427 hasCondition(cxxBoolLiteral(equals(Value)).bind(BooleanId))) 428 .bind(IfStmtId), 429 this); 430 } 431 432 void SimplifyBooleanExprCheck::matchTernaryResult(MatchFinder *Finder, 433 bool Value, 434 StringRef TernaryId) { 435 Finder->addMatcher( 436 conditionalOperator(isExpansionInMainFile(), 437 hasTrueExpression(cxxBoolLiteral(equals(Value))), 438 hasFalseExpression(cxxBoolLiteral(equals(!Value)))) 439 .bind(TernaryId), 440 this); 441 } 442 443 void SimplifyBooleanExprCheck::matchIfReturnsBool(MatchFinder *Finder, 444 bool Value, StringRef Id) { 445 if (ChainedConditionalReturn) 446 Finder->addMatcher(ifStmt(isExpansionInMainFile(), 447 hasThen(returnsBool(Value, ThenLiteralId)), 448 hasElse(returnsBool(!Value))) 449 .bind(Id), 450 this); 451 else 452 Finder->addMatcher(ifStmt(isExpansionInMainFile(), 453 unless(hasParent(ifStmt())), 454 hasThen(returnsBool(Value, ThenLiteralId)), 455 hasElse(returnsBool(!Value))) 456 .bind(Id), 457 this); 458 } 459 460 void SimplifyBooleanExprCheck::matchIfAssignsBool(MatchFinder *Finder, 461 bool Value, StringRef Id) { 462 auto SimpleThen = binaryOperator( 463 hasOperatorName("="), 464 hasLHS(declRefExpr(hasDeclaration(decl().bind(IfAssignObjId)))), 465 hasLHS(expr().bind(IfAssignVariableId)), 466 hasRHS(cxxBoolLiteral(equals(Value)).bind(IfAssignLocId))); 467 auto Then = anyOf(SimpleThen, compoundStmt(statementCountIs(1), 468 hasAnySubstatement(SimpleThen))); 469 auto SimpleElse = binaryOperator( 470 hasOperatorName("="), 471 hasLHS(declRefExpr(hasDeclaration(equalsBoundNode(IfAssignObjId)))), 472 hasRHS(cxxBoolLiteral(equals(!Value)))); 473 auto Else = anyOf(SimpleElse, compoundStmt(statementCountIs(1), 474 hasAnySubstatement(SimpleElse))); 475 if (ChainedConditionalAssignment) 476 Finder->addMatcher( 477 ifStmt(isExpansionInMainFile(), hasThen(Then), hasElse(Else)).bind(Id), 478 this); 479 else 480 Finder->addMatcher(ifStmt(isExpansionInMainFile(), 481 unless(hasParent(ifStmt())), hasThen(Then), 482 hasElse(Else)) 483 .bind(Id), 484 this); 485 } 486 487 void SimplifyBooleanExprCheck::matchCompoundIfReturnsBool(MatchFinder *Finder, 488 bool Value, 489 StringRef Id) { 490 Finder->addMatcher( 491 compoundStmt(allOf(hasAnySubstatement(ifStmt(hasThen(returnsBool(Value)), 492 unless(hasElse(stmt())))), 493 hasAnySubstatement( 494 returnStmt(has(ignoringParenImpCasts( 495 cxxBoolLiteral(equals(!Value))))) 496 .bind(CompoundReturnId)))) 497 .bind(Id), 498 this); 499 } 500 501 void SimplifyBooleanExprCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { 502 Options.store(Opts, "ChainedConditionalReturn", ChainedConditionalReturn); 503 Options.store(Opts, "ChainedConditionalAssignment", 504 ChainedConditionalAssignment); 505 } 506 507 void SimplifyBooleanExprCheck::registerMatchers(MatchFinder *Finder) { 508 Finder->addMatcher(translationUnitDecl().bind("top"), this); 509 510 matchBoolCondition(Finder, true, ConditionThenStmtId); 511 matchBoolCondition(Finder, false, ConditionElseStmtId); 512 513 matchTernaryResult(Finder, true, TernaryId); 514 matchTernaryResult(Finder, false, TernaryNegatedId); 515 516 matchIfReturnsBool(Finder, true, IfReturnsBoolId); 517 matchIfReturnsBool(Finder, false, IfReturnsNotBoolId); 518 519 matchIfAssignsBool(Finder, true, IfAssignBoolId); 520 matchIfAssignsBool(Finder, false, IfAssignNotBoolId); 521 522 matchCompoundIfReturnsBool(Finder, true, CompoundBoolId); 523 matchCompoundIfReturnsBool(Finder, false, CompoundNotBoolId); 524 } 525 526 void SimplifyBooleanExprCheck::check(const MatchFinder::MatchResult &Result) { 527 if (const CXXBoolLiteralExpr *TrueConditionRemoved = 528 getBoolLiteral(Result, ConditionThenStmtId)) 529 replaceWithThenStatement(Result, TrueConditionRemoved); 530 else if (const CXXBoolLiteralExpr *FalseConditionRemoved = 531 getBoolLiteral(Result, ConditionElseStmtId)) 532 replaceWithElseStatement(Result, FalseConditionRemoved); 533 else if (const auto *Ternary = 534 Result.Nodes.getNodeAs<ConditionalOperator>(TernaryId)) 535 replaceWithCondition(Result, Ternary); 536 else if (const auto *TernaryNegated = 537 Result.Nodes.getNodeAs<ConditionalOperator>(TernaryNegatedId)) 538 replaceWithCondition(Result, TernaryNegated, true); 539 else if (const auto *If = Result.Nodes.getNodeAs<IfStmt>(IfReturnsBoolId)) 540 replaceWithReturnCondition(Result, If); 541 else if (const auto *IfNot = 542 Result.Nodes.getNodeAs<IfStmt>(IfReturnsNotBoolId)) 543 replaceWithReturnCondition(Result, IfNot, true); 544 else if (const auto *IfAssign = 545 Result.Nodes.getNodeAs<IfStmt>(IfAssignBoolId)) 546 replaceWithAssignment(Result, IfAssign); 547 else if (const auto *IfAssignNot = 548 Result.Nodes.getNodeAs<IfStmt>(IfAssignNotBoolId)) 549 replaceWithAssignment(Result, IfAssignNot, true); 550 else if (const auto *Compound = 551 Result.Nodes.getNodeAs<CompoundStmt>(CompoundBoolId)) 552 replaceCompoundReturnWithCondition(Result, Compound); 553 else if (const auto *Compound = 554 Result.Nodes.getNodeAs<CompoundStmt>(CompoundNotBoolId)) 555 replaceCompoundReturnWithCondition(Result, Compound, true); 556 else if (const auto TU = Result.Nodes.getNodeAs<Decl>("top")) 557 Visitor(this, Result).TraverseDecl(const_cast<Decl*>(TU)); 558 } 559 560 void SimplifyBooleanExprCheck::issueDiag( 561 const ast_matchers::MatchFinder::MatchResult &Result, SourceLocation Loc, 562 StringRef Description, SourceRange ReplacementRange, 563 StringRef Replacement) { 564 CharSourceRange CharRange = 565 Lexer::makeFileCharRange(CharSourceRange::getTokenRange(ReplacementRange), 566 *Result.SourceManager, getLangOpts()); 567 568 DiagnosticBuilder Diag = diag(Loc, Description); 569 if (!containsDiscardedTokens(Result, CharRange)) 570 Diag << FixItHint::CreateReplacement(CharRange, Replacement); 571 } 572 573 void SimplifyBooleanExprCheck::replaceWithThenStatement( 574 const MatchFinder::MatchResult &Result, 575 const CXXBoolLiteralExpr *TrueConditionRemoved) { 576 const auto *IfStatement = Result.Nodes.getNodeAs<IfStmt>(IfStmtId); 577 issueDiag(Result, TrueConditionRemoved->getLocStart(), 578 SimplifyConditionDiagnostic, IfStatement->getSourceRange(), 579 getText(Result, *IfStatement->getThen())); 580 } 581 582 void SimplifyBooleanExprCheck::replaceWithElseStatement( 583 const MatchFinder::MatchResult &Result, 584 const CXXBoolLiteralExpr *FalseConditionRemoved) { 585 const auto *IfStatement = Result.Nodes.getNodeAs<IfStmt>(IfStmtId); 586 const Stmt *ElseStatement = IfStatement->getElse(); 587 issueDiag(Result, FalseConditionRemoved->getLocStart(), 588 SimplifyConditionDiagnostic, IfStatement->getSourceRange(), 589 ElseStatement ? getText(Result, *ElseStatement) : ""); 590 } 591 592 void SimplifyBooleanExprCheck::replaceWithCondition( 593 const MatchFinder::MatchResult &Result, const ConditionalOperator *Ternary, 594 bool Negated) { 595 std::string Replacement = 596 replacementExpression(Result, Negated, Ternary->getCond()); 597 issueDiag(Result, Ternary->getTrueExpr()->getLocStart(), 598 "redundant boolean literal in ternary expression result", 599 Ternary->getSourceRange(), Replacement); 600 } 601 602 void SimplifyBooleanExprCheck::replaceWithReturnCondition( 603 const MatchFinder::MatchResult &Result, const IfStmt *If, bool Negated) { 604 StringRef Terminator = isa<CompoundStmt>(If->getElse()) ? ";" : ""; 605 std::string Condition = replacementExpression(Result, Negated, If->getCond()); 606 std::string Replacement = ("return " + Condition + Terminator).str(); 607 SourceLocation Start = 608 Result.Nodes.getNodeAs<CXXBoolLiteralExpr>(ThenLiteralId)->getLocStart(); 609 issueDiag(Result, Start, SimplifyConditionalReturnDiagnostic, 610 If->getSourceRange(), Replacement); 611 } 612 613 void SimplifyBooleanExprCheck::replaceCompoundReturnWithCondition( 614 const MatchFinder::MatchResult &Result, const CompoundStmt *Compound, 615 bool Negated) { 616 const auto *Ret = Result.Nodes.getNodeAs<ReturnStmt>(CompoundReturnId); 617 618 // The body shouldn't be empty because the matcher ensures that it must 619 // contain at least two statements: 620 // 1) A `return` statement returning a boolean literal `false` or `true` 621 // 2) An `if` statement with no `else` clause that consists of a single 622 // `return` statement returning the opposite boolean literal `true` or 623 // `false`. 624 assert(Compound->size() >= 2); 625 const IfStmt *BeforeIf = nullptr; 626 CompoundStmt::const_body_iterator Current = Compound->body_begin(); 627 CompoundStmt::const_body_iterator After = Compound->body_begin(); 628 for (++After; After != Compound->body_end() && *Current != Ret; 629 ++Current, ++After) { 630 if (const auto *If = dyn_cast<IfStmt>(*Current)) { 631 if (const CXXBoolLiteralExpr *Lit = stmtReturnsBool(If, Negated)) { 632 if (*After == Ret) { 633 if (!ChainedConditionalReturn && BeforeIf) 634 continue; 635 636 const Expr *Condition = If->getCond(); 637 std::string Replacement = 638 "return " + replacementExpression(Result, Negated, Condition); 639 issueDiag( 640 Result, Lit->getLocStart(), SimplifyConditionalReturnDiagnostic, 641 SourceRange(If->getLocStart(), Ret->getLocEnd()), Replacement); 642 return; 643 } 644 645 BeforeIf = If; 646 } 647 } else { 648 BeforeIf = nullptr; 649 } 650 } 651 } 652 653 void SimplifyBooleanExprCheck::replaceWithAssignment( 654 const MatchFinder::MatchResult &Result, const IfStmt *IfAssign, 655 bool Negated) { 656 SourceRange Range = IfAssign->getSourceRange(); 657 StringRef VariableName = 658 getText(Result, *Result.Nodes.getNodeAs<Expr>(IfAssignVariableId)); 659 StringRef Terminator = isa<CompoundStmt>(IfAssign->getElse()) ? ";" : ""; 660 std::string Condition = 661 replacementExpression(Result, Negated, IfAssign->getCond()); 662 std::string Replacement = 663 (VariableName + " = " + Condition + Terminator).str(); 664 SourceLocation Location = 665 Result.Nodes.getNodeAs<CXXBoolLiteralExpr>(IfAssignLocId)->getLocStart(); 666 issueDiag(Result, Location, 667 "redundant boolean literal in conditional assignment", Range, 668 Replacement); 669 } 670 671 } // namespace readability 672 } // namespace tidy 673 } // namespace clang 674