1 //===-- SimplifyBooleanExprCheck.cpp - clang-tidy -------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "SimplifyBooleanExprCheck.h" 10 #include "clang/AST/RecursiveASTVisitor.h" 11 #include "clang/Lex/Lexer.h" 12 13 #include <string> 14 #include <utility> 15 16 using namespace clang::ast_matchers; 17 18 namespace clang { 19 namespace tidy { 20 namespace readability { 21 22 namespace { 23 24 StringRef getText(const ASTContext &Context, SourceRange Range) { 25 return Lexer::getSourceText(CharSourceRange::getTokenRange(Range), 26 Context.getSourceManager(), 27 Context.getLangOpts()); 28 } 29 30 template <typename T> StringRef getText(const ASTContext &Context, T &Node) { 31 return getText(Context, Node.getSourceRange()); 32 } 33 34 } // namespace 35 36 static constexpr char SimplifyOperatorDiagnostic[] = 37 "redundant boolean literal supplied to boolean operator"; 38 static constexpr char SimplifyConditionDiagnostic[] = 39 "redundant boolean literal in if statement condition"; 40 static constexpr char SimplifyConditionalReturnDiagnostic[] = 41 "redundant boolean literal in conditional return statement"; 42 43 static bool needsParensAfterUnaryNegation(const Expr *E) { 44 E = E->IgnoreImpCasts(); 45 if (isa<BinaryOperator>(E) || isa<ConditionalOperator>(E)) 46 return true; 47 48 if (const auto *Op = dyn_cast<CXXOperatorCallExpr>(E)) 49 return Op->getNumArgs() == 2 && Op->getOperator() != OO_Call && 50 Op->getOperator() != OO_Subscript; 51 52 return false; 53 } 54 55 static std::pair<BinaryOperatorKind, BinaryOperatorKind> Opposites[] = { 56 {BO_LT, BO_GE}, {BO_GT, BO_LE}, {BO_EQ, BO_NE}}; 57 58 static StringRef negatedOperator(const BinaryOperator *BinOp) { 59 const BinaryOperatorKind Opcode = BinOp->getOpcode(); 60 for (auto NegatableOp : Opposites) { 61 if (Opcode == NegatableOp.first) 62 return BinOp->getOpcodeStr(NegatableOp.second); 63 if (Opcode == NegatableOp.second) 64 return BinOp->getOpcodeStr(NegatableOp.first); 65 } 66 return {}; 67 } 68 69 static std::pair<OverloadedOperatorKind, StringRef> OperatorNames[] = { 70 {OO_EqualEqual, "=="}, {OO_ExclaimEqual, "!="}, {OO_Less, "<"}, 71 {OO_GreaterEqual, ">="}, {OO_Greater, ">"}, {OO_LessEqual, "<="}}; 72 73 static StringRef getOperatorName(OverloadedOperatorKind OpKind) { 74 for (auto Name : OperatorNames) { 75 if (Name.first == OpKind) 76 return Name.second; 77 } 78 79 return {}; 80 } 81 82 static std::pair<OverloadedOperatorKind, OverloadedOperatorKind> 83 OppositeOverloads[] = {{OO_EqualEqual, OO_ExclaimEqual}, 84 {OO_Less, OO_GreaterEqual}, 85 {OO_Greater, OO_LessEqual}}; 86 87 static StringRef negatedOperator(const CXXOperatorCallExpr *OpCall) { 88 const OverloadedOperatorKind Opcode = OpCall->getOperator(); 89 for (auto NegatableOp : OppositeOverloads) { 90 if (Opcode == NegatableOp.first) 91 return getOperatorName(NegatableOp.second); 92 if (Opcode == NegatableOp.second) 93 return getOperatorName(NegatableOp.first); 94 } 95 return {}; 96 } 97 98 static std::string asBool(StringRef Text, bool NeedsStaticCast) { 99 if (NeedsStaticCast) 100 return ("static_cast<bool>(" + Text + ")").str(); 101 102 return std::string(Text); 103 } 104 105 static bool needsNullPtrComparison(const Expr *E) { 106 if (const auto *ImpCast = dyn_cast<ImplicitCastExpr>(E)) 107 return ImpCast->getCastKind() == CK_PointerToBoolean || 108 ImpCast->getCastKind() == CK_MemberPointerToBoolean; 109 110 return false; 111 } 112 113 static bool needsZeroComparison(const Expr *E) { 114 if (const auto *ImpCast = dyn_cast<ImplicitCastExpr>(E)) 115 return ImpCast->getCastKind() == CK_IntegralToBoolean; 116 117 return false; 118 } 119 120 static bool needsStaticCast(const Expr *E) { 121 if (const auto *ImpCast = dyn_cast<ImplicitCastExpr>(E)) { 122 if (ImpCast->getCastKind() == CK_UserDefinedConversion && 123 ImpCast->getSubExpr()->getType()->isBooleanType()) { 124 if (const auto *MemCall = 125 dyn_cast<CXXMemberCallExpr>(ImpCast->getSubExpr())) { 126 if (const auto *MemDecl = 127 dyn_cast<CXXConversionDecl>(MemCall->getMethodDecl())) { 128 if (MemDecl->isExplicit()) 129 return true; 130 } 131 } 132 } 133 } 134 135 E = E->IgnoreImpCasts(); 136 return !E->getType()->isBooleanType(); 137 } 138 139 static std::string compareExpressionToConstant(const ASTContext &Context, 140 const Expr *E, bool Negated, 141 const char *Constant) { 142 E = E->IgnoreImpCasts(); 143 const std::string ExprText = 144 (isa<BinaryOperator>(E) ? ("(" + getText(Context, *E) + ")") 145 : getText(Context, *E)) 146 .str(); 147 return ExprText + " " + (Negated ? "!=" : "==") + " " + Constant; 148 } 149 150 static std::string compareExpressionToNullPtr(const ASTContext &Context, 151 const Expr *E, bool Negated) { 152 const char *NullPtr = Context.getLangOpts().CPlusPlus11 ? "nullptr" : "NULL"; 153 return compareExpressionToConstant(Context, E, Negated, NullPtr); 154 } 155 156 static std::string compareExpressionToZero(const ASTContext &Context, 157 const Expr *E, bool Negated) { 158 return compareExpressionToConstant(Context, E, Negated, "0"); 159 } 160 161 static std::string replacementExpression(const ASTContext &Context, 162 bool Negated, const Expr *E) { 163 E = E->IgnoreParenBaseCasts(); 164 if (const auto *EC = dyn_cast<ExprWithCleanups>(E)) 165 E = EC->getSubExpr(); 166 167 const bool NeedsStaticCast = needsStaticCast(E); 168 if (Negated) { 169 if (const auto *UnOp = dyn_cast<UnaryOperator>(E)) { 170 if (UnOp->getOpcode() == UO_LNot) { 171 if (needsNullPtrComparison(UnOp->getSubExpr())) 172 return compareExpressionToNullPtr(Context, UnOp->getSubExpr(), true); 173 174 if (needsZeroComparison(UnOp->getSubExpr())) 175 return compareExpressionToZero(Context, UnOp->getSubExpr(), true); 176 177 return replacementExpression(Context, false, UnOp->getSubExpr()); 178 } 179 } 180 181 if (needsNullPtrComparison(E)) 182 return compareExpressionToNullPtr(Context, E, false); 183 184 if (needsZeroComparison(E)) 185 return compareExpressionToZero(Context, E, false); 186 187 StringRef NegatedOperator; 188 const Expr *LHS = nullptr; 189 const Expr *RHS = nullptr; 190 if (const auto *BinOp = dyn_cast<BinaryOperator>(E)) { 191 NegatedOperator = negatedOperator(BinOp); 192 LHS = BinOp->getLHS(); 193 RHS = BinOp->getRHS(); 194 } else if (const auto *OpExpr = dyn_cast<CXXOperatorCallExpr>(E)) { 195 if (OpExpr->getNumArgs() == 2) { 196 NegatedOperator = negatedOperator(OpExpr); 197 LHS = OpExpr->getArg(0); 198 RHS = OpExpr->getArg(1); 199 } 200 } 201 if (!NegatedOperator.empty() && LHS && RHS) 202 return (asBool((getText(Context, *LHS) + " " + NegatedOperator + " " + 203 getText(Context, *RHS)) 204 .str(), 205 NeedsStaticCast)); 206 207 StringRef Text = getText(Context, *E); 208 if (!NeedsStaticCast && needsParensAfterUnaryNegation(E)) 209 return ("!(" + Text + ")").str(); 210 211 if (needsNullPtrComparison(E)) 212 return compareExpressionToNullPtr(Context, E, false); 213 214 if (needsZeroComparison(E)) 215 return compareExpressionToZero(Context, E, false); 216 217 return ("!" + asBool(Text, NeedsStaticCast)); 218 } 219 220 if (const auto *UnOp = dyn_cast<UnaryOperator>(E)) { 221 if (UnOp->getOpcode() == UO_LNot) { 222 if (needsNullPtrComparison(UnOp->getSubExpr())) 223 return compareExpressionToNullPtr(Context, UnOp->getSubExpr(), false); 224 225 if (needsZeroComparison(UnOp->getSubExpr())) 226 return compareExpressionToZero(Context, UnOp->getSubExpr(), false); 227 } 228 } 229 230 if (needsNullPtrComparison(E)) 231 return compareExpressionToNullPtr(Context, E, true); 232 233 if (needsZeroComparison(E)) 234 return compareExpressionToZero(Context, E, true); 235 236 return asBool(getText(Context, *E), NeedsStaticCast); 237 } 238 239 static bool containsDiscardedTokens(const ASTContext &Context, 240 CharSourceRange CharRange) { 241 std::string ReplacementText = 242 Lexer::getSourceText(CharRange, Context.getSourceManager(), 243 Context.getLangOpts()) 244 .str(); 245 Lexer Lex(CharRange.getBegin(), Context.getLangOpts(), ReplacementText.data(), 246 ReplacementText.data(), 247 ReplacementText.data() + ReplacementText.size()); 248 Lex.SetCommentRetentionState(true); 249 250 Token Tok; 251 while (!Lex.LexFromRawLexer(Tok)) { 252 if (Tok.is(tok::TokenKind::comment) || Tok.is(tok::TokenKind::hash)) 253 return true; 254 } 255 256 return false; 257 } 258 259 class SimplifyBooleanExprCheck::Visitor : public RecursiveASTVisitor<Visitor> { 260 public: 261 Visitor(SimplifyBooleanExprCheck *Check, ASTContext &Context) 262 : Check(Check), Context(Context) {} 263 264 bool traverse() { return TraverseAST(Context); } 265 266 static bool shouldIgnore(Stmt *S) { 267 switch (S->getStmtClass()) { 268 case Stmt::ImplicitCastExprClass: 269 case Stmt::MaterializeTemporaryExprClass: 270 case Stmt::CXXBindTemporaryExprClass: 271 return true; 272 default: 273 return false; 274 } 275 } 276 277 bool dataTraverseStmtPre(Stmt *S) { 278 if (S && !shouldIgnore(S)) 279 StmtStack.push_back(S); 280 return true; 281 } 282 283 bool dataTraverseStmtPost(Stmt *S) { 284 if (S && !shouldIgnore(S)) { 285 assert(StmtStack.back() == S); 286 StmtStack.pop_back(); 287 } 288 return true; 289 } 290 291 bool VisitBinaryOperator(const BinaryOperator *Op) const { 292 Check->reportBinOp(Context, Op); 293 return true; 294 } 295 296 // Extracts a bool if an expression is (true|false|!true|!false); 297 static Optional<bool> getAsBoolLiteral(const Expr *E, bool FilterMacro) { 298 if (const auto *Bool = dyn_cast<CXXBoolLiteralExpr>(E)) { 299 if (FilterMacro && Bool->getBeginLoc().isMacroID()) 300 return llvm::None; 301 return Bool->getValue(); 302 } 303 if (const auto *UnaryOp = dyn_cast<UnaryOperator>(E)) { 304 if (FilterMacro && UnaryOp->getBeginLoc().isMacroID()) 305 return None; 306 if (UnaryOp->getOpcode() == UO_LNot) 307 if (Optional<bool> Res = getAsBoolLiteral( 308 UnaryOp->getSubExpr()->IgnoreImplicit(), FilterMacro)) 309 return !*Res; 310 } 311 return llvm::None; 312 } 313 314 template <typename Node> struct NodeAndBool { 315 const Node *Item = nullptr; 316 bool Bool = false; 317 318 operator bool() const { return Item != nullptr; } 319 }; 320 321 using ExprAndBool = NodeAndBool<Expr>; 322 using DeclAndBool = NodeAndBool<Decl>; 323 324 /// Detect's return (true|false|!true|!false); 325 static ExprAndBool parseReturnLiteralBool(const Stmt *S) { 326 const auto *RS = dyn_cast<ReturnStmt>(S); 327 if (!RS || !RS->getRetValue()) 328 return {}; 329 if (Optional<bool> Ret = 330 getAsBoolLiteral(RS->getRetValue()->IgnoreImplicit(), false)) { 331 return {RS->getRetValue(), *Ret}; 332 } 333 return {}; 334 } 335 336 /// If \p S is not a \c CompoundStmt, applies F on \p S, otherwise if there is 337 /// only 1 statement in the \c CompoundStmt, applies F on that single 338 /// statement. 339 template <typename Functor> 340 static auto checkSingleStatement(Stmt *S, Functor F) -> decltype(F(S)) { 341 if (auto *CS = dyn_cast<CompoundStmt>(S)) { 342 if (CS->size() == 1) 343 return F(CS->body_front()); 344 return {}; 345 } 346 return F(S); 347 } 348 349 Stmt *parent() const { 350 return StmtStack.size() < 2 ? nullptr : StmtStack[StmtStack.size() - 2]; 351 } 352 353 bool VisitIfStmt(IfStmt *If) { 354 /* 355 * if (true) ThenStmt(); -> ThenStmt(); 356 * if (false) ThenStmt(); -> <Empty>; 357 * if (false) ThenStmt(); else ElseStmt() -> ElseStmt(); 358 */ 359 Expr *Cond = If->getCond()->IgnoreImplicit(); 360 if (Optional<bool> Bool = getAsBoolLiteral(Cond, true)) { 361 if (*Bool) 362 Check->replaceWithThenStatement(Context, If, Cond); 363 else 364 Check->replaceWithElseStatement(Context, If, Cond); 365 } 366 367 if (If->getElse()) { 368 /* 369 * if (Cond) return true; else return false; -> return Cond; 370 * if (Cond) return false; else return true; -> return !Cond; 371 */ 372 if (ExprAndBool ThenReturnBool = 373 checkSingleStatement(If->getThen(), parseReturnLiteralBool)) { 374 ExprAndBool ElseReturnBool = 375 checkSingleStatement(If->getElse(), parseReturnLiteralBool); 376 if (ElseReturnBool && ThenReturnBool.Bool != ElseReturnBool.Bool) { 377 if (Check->ChainedConditionalReturn || 378 !isa_and_nonnull<IfStmt>(parent())) { 379 Check->replaceWithReturnCondition(Context, If, ThenReturnBool.Item, 380 ElseReturnBool.Bool); 381 } 382 } 383 } else { 384 /* 385 * if (Cond) A = true; else A = false; -> A = Cond; 386 * if (Cond) A = false; else A = true; -> A = !Cond; 387 */ 388 Expr *Var = nullptr; 389 SourceLocation Loc; 390 auto VarBoolAssignmentMatcher = [&Var, 391 &Loc](const Stmt *S) -> DeclAndBool { 392 const auto *BO = dyn_cast<BinaryOperator>(S); 393 if (!BO || BO->getOpcode() != BO_Assign) 394 return {}; 395 Optional<bool> RightasBool = 396 getAsBoolLiteral(BO->getRHS()->IgnoreImplicit(), false); 397 if (!RightasBool) 398 return {}; 399 Expr *IgnImp = BO->getLHS()->IgnoreImplicit(); 400 if (!Var) { 401 // We only need to track these for the Then branch. 402 Loc = BO->getRHS()->getBeginLoc(); 403 Var = IgnImp; 404 } 405 if (auto *DRE = dyn_cast<DeclRefExpr>(IgnImp)) 406 return {DRE->getDecl(), *RightasBool}; 407 if (auto *ME = dyn_cast<MemberExpr>(IgnImp)) 408 return {ME->getMemberDecl(), *RightasBool}; 409 return {}; 410 }; 411 if (DeclAndBool ThenAssignment = 412 checkSingleStatement(If->getThen(), VarBoolAssignmentMatcher)) { 413 DeclAndBool ElseAssignment = 414 checkSingleStatement(If->getElse(), VarBoolAssignmentMatcher); 415 if (ElseAssignment.Item == ThenAssignment.Item && 416 ElseAssignment.Bool != ThenAssignment.Bool) { 417 if (Check->ChainedConditionalAssignment || 418 !isa_and_nonnull<IfStmt>(parent())) { 419 Check->replaceWithAssignment(Context, If, Var, Loc, 420 ElseAssignment.Bool); 421 } 422 } 423 } 424 } 425 } 426 return true; 427 } 428 429 bool VisitConditionalOperator(ConditionalOperator *Cond) { 430 /* 431 * Condition ? true : false; -> Condition 432 * Condition ? false : true; -> !Condition; 433 */ 434 if (Optional<bool> Then = 435 getAsBoolLiteral(Cond->getTrueExpr()->IgnoreImplicit(), false)) { 436 if (Optional<bool> Else = 437 getAsBoolLiteral(Cond->getFalseExpr()->IgnoreImplicit(), false)) { 438 if (*Then != *Else) 439 Check->replaceWithCondition(Context, Cond, *Else); 440 } 441 } 442 return true; 443 } 444 445 bool VisitCompoundStmt(CompoundStmt *CS) { 446 if (CS->size() < 2) 447 return true; 448 bool CurIf = false, PrevIf = false; 449 for (auto First = CS->body_begin(), Second = std::next(First), 450 End = CS->body_end(); 451 Second != End; ++Second, ++First) { 452 PrevIf = CurIf; 453 CurIf = isa<IfStmt>(*First); 454 ExprAndBool TrailingReturnBool = parseReturnLiteralBool(*Second); 455 if (!TrailingReturnBool) 456 continue; 457 458 if (CurIf) { 459 /* 460 * if (Cond) return true; return false; -> return Cond; 461 * if (Cond) return false; return true; -> return !Cond; 462 */ 463 auto *If = cast<IfStmt>(*First); 464 ExprAndBool ThenReturnBool = 465 checkSingleStatement(If->getThen(), parseReturnLiteralBool); 466 if (ThenReturnBool && ThenReturnBool.Bool != TrailingReturnBool.Bool) { 467 if (Check->ChainedConditionalReturn || 468 (!PrevIf && If->getElse() == nullptr)) { 469 Check->replaceCompoundReturnWithCondition( 470 Context, cast<ReturnStmt>(*Second), TrailingReturnBool.Bool, If, 471 ThenReturnBool.Item); 472 } 473 } 474 } else if (isa<LabelStmt, CaseStmt, DefaultStmt>(*First)) { 475 /* 476 * (case X|label_X|default): if (Cond) return BoolLiteral; 477 * return !BoolLiteral 478 */ 479 Stmt *SubStmt = 480 isa<LabelStmt>(*First) ? cast<LabelStmt>(*First)->getSubStmt() 481 : isa<CaseStmt>(*First) ? cast<CaseStmt>(*First)->getSubStmt() 482 : cast<DefaultStmt>(*First)->getSubStmt(); 483 auto *SubIf = dyn_cast<IfStmt>(SubStmt); 484 if (SubIf && !SubIf->getElse()) { 485 ExprAndBool ThenReturnBool = 486 checkSingleStatement(SubIf->getThen(), parseReturnLiteralBool); 487 if (ThenReturnBool && 488 ThenReturnBool.Bool != TrailingReturnBool.Bool) { 489 Check->replaceCompoundReturnWithCondition( 490 Context, cast<ReturnStmt>(*Second), TrailingReturnBool.Bool, 491 SubIf, ThenReturnBool.Item); 492 } 493 } 494 } 495 } 496 return true; 497 } 498 499 private: 500 SimplifyBooleanExprCheck *Check; 501 SmallVector<Stmt *, 32> StmtStack; 502 ASTContext &Context; 503 }; 504 505 SimplifyBooleanExprCheck::SimplifyBooleanExprCheck(StringRef Name, 506 ClangTidyContext *Context) 507 : ClangTidyCheck(Name, Context), 508 ChainedConditionalReturn(Options.get("ChainedConditionalReturn", false)), 509 ChainedConditionalAssignment( 510 Options.get("ChainedConditionalAssignment", false)) {} 511 512 static bool containsBoolLiteral(const Expr *E) { 513 if (!E) 514 return false; 515 E = E->IgnoreParenImpCasts(); 516 if (isa<CXXBoolLiteralExpr>(E)) 517 return true; 518 if (const auto *BinOp = dyn_cast<BinaryOperator>(E)) 519 return containsBoolLiteral(BinOp->getLHS()) || 520 containsBoolLiteral(BinOp->getRHS()); 521 if (const auto *UnaryOp = dyn_cast<UnaryOperator>(E)) 522 return containsBoolLiteral(UnaryOp->getSubExpr()); 523 return false; 524 } 525 526 void SimplifyBooleanExprCheck::reportBinOp(const ASTContext &Context, 527 const BinaryOperator *Op) { 528 const auto *LHS = Op->getLHS()->IgnoreParenImpCasts(); 529 const auto *RHS = Op->getRHS()->IgnoreParenImpCasts(); 530 531 const CXXBoolLiteralExpr *Bool; 532 const Expr *Other; 533 if ((Bool = dyn_cast<CXXBoolLiteralExpr>(LHS)) != nullptr) 534 Other = RHS; 535 else if ((Bool = dyn_cast<CXXBoolLiteralExpr>(RHS)) != nullptr) 536 Other = LHS; 537 else 538 return; 539 540 if (Bool->getBeginLoc().isMacroID()) 541 return; 542 543 // FIXME: why do we need this? 544 if (!isa<CXXBoolLiteralExpr>(Other) && containsBoolLiteral(Other)) 545 return; 546 547 bool BoolValue = Bool->getValue(); 548 549 auto ReplaceWithExpression = [this, &Context, LHS, RHS, 550 Bool](const Expr *ReplaceWith, bool Negated) { 551 std::string Replacement = 552 replacementExpression(Context, Negated, ReplaceWith); 553 SourceRange Range(LHS->getBeginLoc(), RHS->getEndLoc()); 554 issueDiag(Context, Bool->getBeginLoc(), SimplifyOperatorDiagnostic, Range, 555 Replacement); 556 }; 557 558 switch (Op->getOpcode()) { 559 case BO_LAnd: 560 if (BoolValue) 561 // expr && true -> expr 562 ReplaceWithExpression(Other, /*Negated=*/false); 563 else 564 // expr && false -> false 565 ReplaceWithExpression(Bool, /*Negated=*/false); 566 break; 567 case BO_LOr: 568 if (BoolValue) 569 // expr || true -> true 570 ReplaceWithExpression(Bool, /*Negated=*/false); 571 else 572 // expr || false -> expr 573 ReplaceWithExpression(Other, /*Negated=*/false); 574 break; 575 case BO_EQ: 576 // expr == true -> expr, expr == false -> !expr 577 ReplaceWithExpression(Other, /*Negated=*/!BoolValue); 578 break; 579 case BO_NE: 580 // expr != true -> !expr, expr != false -> expr 581 ReplaceWithExpression(Other, /*Negated=*/BoolValue); 582 break; 583 default: 584 break; 585 } 586 } 587 588 void SimplifyBooleanExprCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { 589 Options.store(Opts, "ChainedConditionalReturn", ChainedConditionalReturn); 590 Options.store(Opts, "ChainedConditionalAssignment", 591 ChainedConditionalAssignment); 592 } 593 594 void SimplifyBooleanExprCheck::registerMatchers(MatchFinder *Finder) { 595 Finder->addMatcher(translationUnitDecl(), this); 596 } 597 598 void SimplifyBooleanExprCheck::check(const MatchFinder::MatchResult &Result) { 599 Visitor(this, *Result.Context).traverse(); 600 } 601 602 void SimplifyBooleanExprCheck::issueDiag(const ASTContext &Context, 603 SourceLocation Loc, 604 StringRef Description, 605 SourceRange ReplacementRange, 606 StringRef Replacement) { 607 CharSourceRange CharRange = 608 Lexer::makeFileCharRange(CharSourceRange::getTokenRange(ReplacementRange), 609 Context.getSourceManager(), getLangOpts()); 610 611 DiagnosticBuilder Diag = diag(Loc, Description); 612 if (!containsDiscardedTokens(Context, CharRange)) 613 Diag << FixItHint::CreateReplacement(CharRange, Replacement); 614 } 615 616 void SimplifyBooleanExprCheck::replaceWithThenStatement( 617 const ASTContext &Context, const IfStmt *IfStatement, 618 const Expr *BoolLiteral) { 619 issueDiag(Context, BoolLiteral->getBeginLoc(), SimplifyConditionDiagnostic, 620 IfStatement->getSourceRange(), 621 getText(Context, *IfStatement->getThen())); 622 } 623 624 void SimplifyBooleanExprCheck::replaceWithElseStatement( 625 const ASTContext &Context, const IfStmt *IfStatement, 626 const Expr *BoolLiteral) { 627 const Stmt *ElseStatement = IfStatement->getElse(); 628 issueDiag(Context, BoolLiteral->getBeginLoc(), SimplifyConditionDiagnostic, 629 IfStatement->getSourceRange(), 630 ElseStatement ? getText(Context, *ElseStatement) : ""); 631 } 632 633 void SimplifyBooleanExprCheck::replaceWithCondition( 634 const ASTContext &Context, const ConditionalOperator *Ternary, 635 bool Negated) { 636 std::string Replacement = 637 replacementExpression(Context, Negated, Ternary->getCond()); 638 issueDiag(Context, Ternary->getTrueExpr()->getBeginLoc(), 639 "redundant boolean literal in ternary expression result", 640 Ternary->getSourceRange(), Replacement); 641 } 642 643 void SimplifyBooleanExprCheck::replaceWithReturnCondition( 644 const ASTContext &Context, const IfStmt *If, const Expr *BoolLiteral, 645 bool Negated) { 646 StringRef Terminator = isa<CompoundStmt>(If->getElse()) ? ";" : ""; 647 std::string Condition = 648 replacementExpression(Context, Negated, If->getCond()); 649 std::string Replacement = ("return " + Condition + Terminator).str(); 650 SourceLocation Start = BoolLiteral->getBeginLoc(); 651 issueDiag(Context, Start, SimplifyConditionalReturnDiagnostic, 652 If->getSourceRange(), Replacement); 653 } 654 655 void SimplifyBooleanExprCheck::replaceCompoundReturnWithCondition( 656 const ASTContext &Context, const ReturnStmt *Ret, bool Negated, 657 const IfStmt *If, const Expr *ThenReturn) { 658 const std::string Replacement = 659 "return " + replacementExpression(Context, Negated, If->getCond()); 660 issueDiag(Context, ThenReturn->getBeginLoc(), 661 SimplifyConditionalReturnDiagnostic, 662 SourceRange(If->getBeginLoc(), Ret->getEndLoc()), Replacement); 663 } 664 665 void SimplifyBooleanExprCheck::replaceWithAssignment(const ASTContext &Context, 666 const IfStmt *IfAssign, 667 const Expr *Var, 668 SourceLocation Loc, 669 bool Negated) { 670 SourceRange Range = IfAssign->getSourceRange(); 671 StringRef VariableName = getText(Context, *Var); 672 StringRef Terminator = isa<CompoundStmt>(IfAssign->getElse()) ? ";" : ""; 673 std::string Condition = 674 replacementExpression(Context, Negated, IfAssign->getCond()); 675 std::string Replacement = 676 (VariableName + " = " + Condition + Terminator).str(); 677 issueDiag(Context, Loc, "redundant boolean literal in conditional assignment", 678 Range, Replacement); 679 } 680 681 } // namespace readability 682 } // namespace tidy 683 } // namespace clang 684