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->getBeginLoc().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->getBeginLoc().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->getBeginLoc(), RHS->getEndLoc()); 389 issueDiag(Result, Bool->getBeginLoc(), 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( 495 hasAnySubstatement( 496 ifStmt(hasThen(returnsBool(Value)), unless(hasElse(stmt())))), 497 hasAnySubstatement(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 // This is a silly hack to let us run a RecursiveASTVisitor on the Context. 511 // We want to match exactly one node in the AST, doesn't matter which. 512 AST_MATCHER_P(Decl, matchOnce, bool *, Matched) { 513 if (*Matched) 514 return false; 515 return *Matched = true; 516 } 517 518 void SimplifyBooleanExprCheck::registerMatchers(MatchFinder *Finder) { 519 Finder->addMatcher(matchOnce(&MatchedOnce), this); 520 521 matchBoolCondition(Finder, true, ConditionThenStmtId); 522 matchBoolCondition(Finder, false, ConditionElseStmtId); 523 524 matchTernaryResult(Finder, true, TernaryId); 525 matchTernaryResult(Finder, false, TernaryNegatedId); 526 527 matchIfReturnsBool(Finder, true, IfReturnsBoolId); 528 matchIfReturnsBool(Finder, false, IfReturnsNotBoolId); 529 530 matchIfAssignsBool(Finder, true, IfAssignBoolId); 531 matchIfAssignsBool(Finder, false, IfAssignNotBoolId); 532 533 matchCompoundIfReturnsBool(Finder, true, CompoundBoolId); 534 matchCompoundIfReturnsBool(Finder, false, CompoundNotBoolId); 535 } 536 537 void SimplifyBooleanExprCheck::check(const MatchFinder::MatchResult &Result) { 538 if (const CXXBoolLiteralExpr *TrueConditionRemoved = 539 getBoolLiteral(Result, ConditionThenStmtId)) 540 replaceWithThenStatement(Result, TrueConditionRemoved); 541 else if (const CXXBoolLiteralExpr *FalseConditionRemoved = 542 getBoolLiteral(Result, ConditionElseStmtId)) 543 replaceWithElseStatement(Result, FalseConditionRemoved); 544 else if (const auto *Ternary = 545 Result.Nodes.getNodeAs<ConditionalOperator>(TernaryId)) 546 replaceWithCondition(Result, Ternary); 547 else if (const auto *TernaryNegated = 548 Result.Nodes.getNodeAs<ConditionalOperator>(TernaryNegatedId)) 549 replaceWithCondition(Result, TernaryNegated, true); 550 else if (const auto *If = Result.Nodes.getNodeAs<IfStmt>(IfReturnsBoolId)) 551 replaceWithReturnCondition(Result, If); 552 else if (const auto *IfNot = 553 Result.Nodes.getNodeAs<IfStmt>(IfReturnsNotBoolId)) 554 replaceWithReturnCondition(Result, IfNot, true); 555 else if (const auto *IfAssign = 556 Result.Nodes.getNodeAs<IfStmt>(IfAssignBoolId)) 557 replaceWithAssignment(Result, IfAssign); 558 else if (const auto *IfAssignNot = 559 Result.Nodes.getNodeAs<IfStmt>(IfAssignNotBoolId)) 560 replaceWithAssignment(Result, IfAssignNot, true); 561 else if (const auto *Compound = 562 Result.Nodes.getNodeAs<CompoundStmt>(CompoundBoolId)) 563 replaceCompoundReturnWithCondition(Result, Compound); 564 else if (const auto *Compound = 565 Result.Nodes.getNodeAs<CompoundStmt>(CompoundNotBoolId)) 566 replaceCompoundReturnWithCondition(Result, Compound, true); 567 else { // MatchOnce matcher 568 assert(MatchedOnce); 569 Visitor(this, Result).TraverseAST(*Result.Context); 570 } 571 } 572 573 void SimplifyBooleanExprCheck::issueDiag( 574 const ast_matchers::MatchFinder::MatchResult &Result, SourceLocation Loc, 575 StringRef Description, SourceRange ReplacementRange, 576 StringRef Replacement) { 577 CharSourceRange CharRange = 578 Lexer::makeFileCharRange(CharSourceRange::getTokenRange(ReplacementRange), 579 *Result.SourceManager, getLangOpts()); 580 581 DiagnosticBuilder Diag = diag(Loc, Description); 582 if (!containsDiscardedTokens(Result, CharRange)) 583 Diag << FixItHint::CreateReplacement(CharRange, Replacement); 584 } 585 586 void SimplifyBooleanExprCheck::replaceWithThenStatement( 587 const MatchFinder::MatchResult &Result, 588 const CXXBoolLiteralExpr *TrueConditionRemoved) { 589 const auto *IfStatement = Result.Nodes.getNodeAs<IfStmt>(IfStmtId); 590 issueDiag(Result, TrueConditionRemoved->getBeginLoc(), 591 SimplifyConditionDiagnostic, IfStatement->getSourceRange(), 592 getText(Result, *IfStatement->getThen())); 593 } 594 595 void SimplifyBooleanExprCheck::replaceWithElseStatement( 596 const MatchFinder::MatchResult &Result, 597 const CXXBoolLiteralExpr *FalseConditionRemoved) { 598 const auto *IfStatement = Result.Nodes.getNodeAs<IfStmt>(IfStmtId); 599 const Stmt *ElseStatement = IfStatement->getElse(); 600 issueDiag(Result, FalseConditionRemoved->getBeginLoc(), 601 SimplifyConditionDiagnostic, IfStatement->getSourceRange(), 602 ElseStatement ? getText(Result, *ElseStatement) : ""); 603 } 604 605 void SimplifyBooleanExprCheck::replaceWithCondition( 606 const MatchFinder::MatchResult &Result, const ConditionalOperator *Ternary, 607 bool Negated) { 608 std::string Replacement = 609 replacementExpression(Result, Negated, Ternary->getCond()); 610 issueDiag(Result, Ternary->getTrueExpr()->getBeginLoc(), 611 "redundant boolean literal in ternary expression result", 612 Ternary->getSourceRange(), Replacement); 613 } 614 615 void SimplifyBooleanExprCheck::replaceWithReturnCondition( 616 const MatchFinder::MatchResult &Result, const IfStmt *If, bool Negated) { 617 StringRef Terminator = isa<CompoundStmt>(If->getElse()) ? ";" : ""; 618 std::string Condition = replacementExpression(Result, Negated, If->getCond()); 619 std::string Replacement = ("return " + Condition + Terminator).str(); 620 SourceLocation Start = 621 Result.Nodes.getNodeAs<CXXBoolLiteralExpr>(ThenLiteralId)->getBeginLoc(); 622 issueDiag(Result, Start, SimplifyConditionalReturnDiagnostic, 623 If->getSourceRange(), Replacement); 624 } 625 626 void SimplifyBooleanExprCheck::replaceCompoundReturnWithCondition( 627 const MatchFinder::MatchResult &Result, const CompoundStmt *Compound, 628 bool Negated) { 629 const auto *Ret = Result.Nodes.getNodeAs<ReturnStmt>(CompoundReturnId); 630 631 // The body shouldn't be empty because the matcher ensures that it must 632 // contain at least two statements: 633 // 1) A `return` statement returning a boolean literal `false` or `true` 634 // 2) An `if` statement with no `else` clause that consists of a single 635 // `return` statement returning the opposite boolean literal `true` or 636 // `false`. 637 assert(Compound->size() >= 2); 638 const IfStmt *BeforeIf = nullptr; 639 CompoundStmt::const_body_iterator Current = Compound->body_begin(); 640 CompoundStmt::const_body_iterator After = Compound->body_begin(); 641 for (++After; After != Compound->body_end() && *Current != Ret; 642 ++Current, ++After) { 643 if (const auto *If = dyn_cast<IfStmt>(*Current)) { 644 if (const CXXBoolLiteralExpr *Lit = stmtReturnsBool(If, Negated)) { 645 if (*After == Ret) { 646 if (!ChainedConditionalReturn && BeforeIf) 647 continue; 648 649 const Expr *Condition = If->getCond(); 650 std::string Replacement = 651 "return " + replacementExpression(Result, Negated, Condition); 652 issueDiag( 653 Result, Lit->getBeginLoc(), SimplifyConditionalReturnDiagnostic, 654 SourceRange(If->getBeginLoc(), Ret->getEndLoc()), Replacement); 655 return; 656 } 657 658 BeforeIf = If; 659 } 660 } else { 661 BeforeIf = nullptr; 662 } 663 } 664 } 665 666 void SimplifyBooleanExprCheck::replaceWithAssignment( 667 const MatchFinder::MatchResult &Result, const IfStmt *IfAssign, 668 bool Negated) { 669 SourceRange Range = IfAssign->getSourceRange(); 670 StringRef VariableName = 671 getText(Result, *Result.Nodes.getNodeAs<Expr>(IfAssignVariableId)); 672 StringRef Terminator = isa<CompoundStmt>(IfAssign->getElse()) ? ";" : ""; 673 std::string Condition = 674 replacementExpression(Result, Negated, IfAssign->getCond()); 675 std::string Replacement = 676 (VariableName + " = " + Condition + Terminator).str(); 677 SourceLocation Location = 678 Result.Nodes.getNodeAs<CXXBoolLiteralExpr>(IfAssignLocId)->getBeginLoc(); 679 issueDiag(Result, Location, 680 "redundant boolean literal in conditional assignment", Range, 681 Replacement); 682 } 683 684 } // namespace readability 685 } // namespace tidy 686 } // namespace clang 687