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 // Skip any if's that have a condition var or an init statement. 355 if (If->hasInitStorage() || If->hasVarStorage()) 356 return true; 357 /* 358 * if (true) ThenStmt(); -> ThenStmt(); 359 * if (false) ThenStmt(); -> <Empty>; 360 * if (false) ThenStmt(); else ElseStmt() -> ElseStmt(); 361 */ 362 Expr *Cond = If->getCond()->IgnoreImplicit(); 363 if (Optional<bool> Bool = getAsBoolLiteral(Cond, true)) { 364 if (*Bool) 365 Check->replaceWithThenStatement(Context, If, Cond); 366 else 367 Check->replaceWithElseStatement(Context, If, Cond); 368 } 369 370 if (If->getElse()) { 371 /* 372 * if (Cond) return true; else return false; -> return Cond; 373 * if (Cond) return false; else return true; -> return !Cond; 374 */ 375 if (ExprAndBool ThenReturnBool = 376 checkSingleStatement(If->getThen(), parseReturnLiteralBool)) { 377 ExprAndBool ElseReturnBool = 378 checkSingleStatement(If->getElse(), parseReturnLiteralBool); 379 if (ElseReturnBool && ThenReturnBool.Bool != ElseReturnBool.Bool) { 380 if (Check->ChainedConditionalReturn || 381 !isa_and_nonnull<IfStmt>(parent())) { 382 Check->replaceWithReturnCondition(Context, If, ThenReturnBool.Item, 383 ElseReturnBool.Bool); 384 } 385 } 386 } else { 387 /* 388 * if (Cond) A = true; else A = false; -> A = Cond; 389 * if (Cond) A = false; else A = true; -> A = !Cond; 390 */ 391 Expr *Var = nullptr; 392 SourceLocation Loc; 393 auto VarBoolAssignmentMatcher = [&Var, 394 &Loc](const Stmt *S) -> DeclAndBool { 395 const auto *BO = dyn_cast<BinaryOperator>(S); 396 if (!BO || BO->getOpcode() != BO_Assign) 397 return {}; 398 Optional<bool> RightasBool = 399 getAsBoolLiteral(BO->getRHS()->IgnoreImplicit(), false); 400 if (!RightasBool) 401 return {}; 402 Expr *IgnImp = BO->getLHS()->IgnoreImplicit(); 403 if (!Var) { 404 // We only need to track these for the Then branch. 405 Loc = BO->getRHS()->getBeginLoc(); 406 Var = IgnImp; 407 } 408 if (auto *DRE = dyn_cast<DeclRefExpr>(IgnImp)) 409 return {DRE->getDecl(), *RightasBool}; 410 if (auto *ME = dyn_cast<MemberExpr>(IgnImp)) 411 return {ME->getMemberDecl(), *RightasBool}; 412 return {}; 413 }; 414 if (DeclAndBool ThenAssignment = 415 checkSingleStatement(If->getThen(), VarBoolAssignmentMatcher)) { 416 DeclAndBool ElseAssignment = 417 checkSingleStatement(If->getElse(), VarBoolAssignmentMatcher); 418 if (ElseAssignment.Item == ThenAssignment.Item && 419 ElseAssignment.Bool != ThenAssignment.Bool) { 420 if (Check->ChainedConditionalAssignment || 421 !isa_and_nonnull<IfStmt>(parent())) { 422 Check->replaceWithAssignment(Context, If, Var, Loc, 423 ElseAssignment.Bool); 424 } 425 } 426 } 427 } 428 } 429 return true; 430 } 431 432 bool VisitConditionalOperator(ConditionalOperator *Cond) { 433 /* 434 * Condition ? true : false; -> Condition 435 * Condition ? false : true; -> !Condition; 436 */ 437 if (Optional<bool> Then = 438 getAsBoolLiteral(Cond->getTrueExpr()->IgnoreImplicit(), false)) { 439 if (Optional<bool> Else = 440 getAsBoolLiteral(Cond->getFalseExpr()->IgnoreImplicit(), false)) { 441 if (*Then != *Else) 442 Check->replaceWithCondition(Context, Cond, *Else); 443 } 444 } 445 return true; 446 } 447 448 bool VisitCompoundStmt(CompoundStmt *CS) { 449 if (CS->size() < 2) 450 return true; 451 bool CurIf = false, PrevIf = false; 452 for (auto First = CS->body_begin(), Second = std::next(First), 453 End = CS->body_end(); 454 Second != End; ++Second, ++First) { 455 PrevIf = CurIf; 456 CurIf = isa<IfStmt>(*First); 457 ExprAndBool TrailingReturnBool = parseReturnLiteralBool(*Second); 458 if (!TrailingReturnBool) 459 continue; 460 461 if (CurIf) { 462 /* 463 * if (Cond) return true; return false; -> return Cond; 464 * if (Cond) return false; return true; -> return !Cond; 465 */ 466 auto *If = cast<IfStmt>(*First); 467 if (!If->hasInitStorage() && !If->hasVarStorage()) { 468 ExprAndBool ThenReturnBool = 469 checkSingleStatement(If->getThen(), parseReturnLiteralBool); 470 if (ThenReturnBool && 471 ThenReturnBool.Bool != TrailingReturnBool.Bool) { 472 if (Check->ChainedConditionalReturn || 473 (!PrevIf && If->getElse() == nullptr)) { 474 Check->replaceCompoundReturnWithCondition( 475 Context, cast<ReturnStmt>(*Second), TrailingReturnBool.Bool, 476 If, ThenReturnBool.Item); 477 } 478 } 479 } 480 } else if (isa<LabelStmt, CaseStmt, DefaultStmt>(*First)) { 481 /* 482 * (case X|label_X|default): if (Cond) return BoolLiteral; 483 * return !BoolLiteral 484 */ 485 Stmt *SubStmt = 486 isa<LabelStmt>(*First) ? cast<LabelStmt>(*First)->getSubStmt() 487 : isa<CaseStmt>(*First) ? cast<CaseStmt>(*First)->getSubStmt() 488 : cast<DefaultStmt>(*First)->getSubStmt(); 489 auto *SubIf = dyn_cast<IfStmt>(SubStmt); 490 if (SubIf && !SubIf->getElse() && !SubIf->hasInitStorage() && 491 !SubIf->hasVarStorage()) { 492 ExprAndBool ThenReturnBool = 493 checkSingleStatement(SubIf->getThen(), parseReturnLiteralBool); 494 if (ThenReturnBool && 495 ThenReturnBool.Bool != TrailingReturnBool.Bool) { 496 Check->replaceCompoundReturnWithCondition( 497 Context, cast<ReturnStmt>(*Second), TrailingReturnBool.Bool, 498 SubIf, ThenReturnBool.Item); 499 } 500 } 501 } 502 } 503 return true; 504 } 505 506 private: 507 SimplifyBooleanExprCheck *Check; 508 SmallVector<Stmt *, 32> StmtStack; 509 ASTContext &Context; 510 }; 511 512 SimplifyBooleanExprCheck::SimplifyBooleanExprCheck(StringRef Name, 513 ClangTidyContext *Context) 514 : ClangTidyCheck(Name, Context), 515 ChainedConditionalReturn(Options.get("ChainedConditionalReturn", false)), 516 ChainedConditionalAssignment( 517 Options.get("ChainedConditionalAssignment", false)) {} 518 519 static bool containsBoolLiteral(const Expr *E) { 520 if (!E) 521 return false; 522 E = E->IgnoreParenImpCasts(); 523 if (isa<CXXBoolLiteralExpr>(E)) 524 return true; 525 if (const auto *BinOp = dyn_cast<BinaryOperator>(E)) 526 return containsBoolLiteral(BinOp->getLHS()) || 527 containsBoolLiteral(BinOp->getRHS()); 528 if (const auto *UnaryOp = dyn_cast<UnaryOperator>(E)) 529 return containsBoolLiteral(UnaryOp->getSubExpr()); 530 return false; 531 } 532 533 void SimplifyBooleanExprCheck::reportBinOp(const ASTContext &Context, 534 const BinaryOperator *Op) { 535 const auto *LHS = Op->getLHS()->IgnoreParenImpCasts(); 536 const auto *RHS = Op->getRHS()->IgnoreParenImpCasts(); 537 538 const CXXBoolLiteralExpr *Bool; 539 const Expr *Other; 540 if ((Bool = dyn_cast<CXXBoolLiteralExpr>(LHS)) != nullptr) 541 Other = RHS; 542 else if ((Bool = dyn_cast<CXXBoolLiteralExpr>(RHS)) != nullptr) 543 Other = LHS; 544 else 545 return; 546 547 if (Bool->getBeginLoc().isMacroID()) 548 return; 549 550 // FIXME: why do we need this? 551 if (!isa<CXXBoolLiteralExpr>(Other) && containsBoolLiteral(Other)) 552 return; 553 554 bool BoolValue = Bool->getValue(); 555 556 auto ReplaceWithExpression = [this, &Context, LHS, RHS, 557 Bool](const Expr *ReplaceWith, bool Negated) { 558 std::string Replacement = 559 replacementExpression(Context, Negated, ReplaceWith); 560 SourceRange Range(LHS->getBeginLoc(), RHS->getEndLoc()); 561 issueDiag(Context, Bool->getBeginLoc(), SimplifyOperatorDiagnostic, Range, 562 Replacement); 563 }; 564 565 switch (Op->getOpcode()) { 566 case BO_LAnd: 567 if (BoolValue) 568 // expr && true -> expr 569 ReplaceWithExpression(Other, /*Negated=*/false); 570 else 571 // expr && false -> false 572 ReplaceWithExpression(Bool, /*Negated=*/false); 573 break; 574 case BO_LOr: 575 if (BoolValue) 576 // expr || true -> true 577 ReplaceWithExpression(Bool, /*Negated=*/false); 578 else 579 // expr || false -> expr 580 ReplaceWithExpression(Other, /*Negated=*/false); 581 break; 582 case BO_EQ: 583 // expr == true -> expr, expr == false -> !expr 584 ReplaceWithExpression(Other, /*Negated=*/!BoolValue); 585 break; 586 case BO_NE: 587 // expr != true -> !expr, expr != false -> expr 588 ReplaceWithExpression(Other, /*Negated=*/BoolValue); 589 break; 590 default: 591 break; 592 } 593 } 594 595 void SimplifyBooleanExprCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { 596 Options.store(Opts, "ChainedConditionalReturn", ChainedConditionalReturn); 597 Options.store(Opts, "ChainedConditionalAssignment", 598 ChainedConditionalAssignment); 599 } 600 601 void SimplifyBooleanExprCheck::registerMatchers(MatchFinder *Finder) { 602 Finder->addMatcher(translationUnitDecl(), this); 603 } 604 605 void SimplifyBooleanExprCheck::check(const MatchFinder::MatchResult &Result) { 606 Visitor(this, *Result.Context).traverse(); 607 } 608 609 void SimplifyBooleanExprCheck::issueDiag(const ASTContext &Context, 610 SourceLocation Loc, 611 StringRef Description, 612 SourceRange ReplacementRange, 613 StringRef Replacement) { 614 CharSourceRange CharRange = 615 Lexer::makeFileCharRange(CharSourceRange::getTokenRange(ReplacementRange), 616 Context.getSourceManager(), getLangOpts()); 617 618 DiagnosticBuilder Diag = diag(Loc, Description); 619 if (!containsDiscardedTokens(Context, CharRange)) 620 Diag << FixItHint::CreateReplacement(CharRange, Replacement); 621 } 622 623 void SimplifyBooleanExprCheck::replaceWithThenStatement( 624 const ASTContext &Context, const IfStmt *IfStatement, 625 const Expr *BoolLiteral) { 626 issueDiag(Context, BoolLiteral->getBeginLoc(), SimplifyConditionDiagnostic, 627 IfStatement->getSourceRange(), 628 getText(Context, *IfStatement->getThen())); 629 } 630 631 void SimplifyBooleanExprCheck::replaceWithElseStatement( 632 const ASTContext &Context, const IfStmt *IfStatement, 633 const Expr *BoolLiteral) { 634 const Stmt *ElseStatement = IfStatement->getElse(); 635 issueDiag(Context, BoolLiteral->getBeginLoc(), SimplifyConditionDiagnostic, 636 IfStatement->getSourceRange(), 637 ElseStatement ? getText(Context, *ElseStatement) : ""); 638 } 639 640 void SimplifyBooleanExprCheck::replaceWithCondition( 641 const ASTContext &Context, const ConditionalOperator *Ternary, 642 bool Negated) { 643 std::string Replacement = 644 replacementExpression(Context, Negated, Ternary->getCond()); 645 issueDiag(Context, Ternary->getTrueExpr()->getBeginLoc(), 646 "redundant boolean literal in ternary expression result", 647 Ternary->getSourceRange(), Replacement); 648 } 649 650 void SimplifyBooleanExprCheck::replaceWithReturnCondition( 651 const ASTContext &Context, const IfStmt *If, const Expr *BoolLiteral, 652 bool Negated) { 653 StringRef Terminator = isa<CompoundStmt>(If->getElse()) ? ";" : ""; 654 std::string Condition = 655 replacementExpression(Context, Negated, If->getCond()); 656 std::string Replacement = ("return " + Condition + Terminator).str(); 657 SourceLocation Start = BoolLiteral->getBeginLoc(); 658 issueDiag(Context, Start, SimplifyConditionalReturnDiagnostic, 659 If->getSourceRange(), Replacement); 660 } 661 662 void SimplifyBooleanExprCheck::replaceCompoundReturnWithCondition( 663 const ASTContext &Context, const ReturnStmt *Ret, bool Negated, 664 const IfStmt *If, const Expr *ThenReturn) { 665 const std::string Replacement = 666 "return " + replacementExpression(Context, Negated, If->getCond()); 667 issueDiag(Context, ThenReturn->getBeginLoc(), 668 SimplifyConditionalReturnDiagnostic, 669 SourceRange(If->getBeginLoc(), Ret->getEndLoc()), Replacement); 670 } 671 672 void SimplifyBooleanExprCheck::replaceWithAssignment(const ASTContext &Context, 673 const IfStmt *IfAssign, 674 const Expr *Var, 675 SourceLocation Loc, 676 bool Negated) { 677 SourceRange Range = IfAssign->getSourceRange(); 678 StringRef VariableName = getText(Context, *Var); 679 StringRef Terminator = isa<CompoundStmt>(IfAssign->getElse()) ? ";" : ""; 680 std::string Condition = 681 replacementExpression(Context, Negated, IfAssign->getCond()); 682 std::string Replacement = 683 (VariableName + " = " + Condition + Terminator).str(); 684 issueDiag(Context, Loc, "redundant boolean literal in conditional assignment", 685 Range, Replacement); 686 } 687 688 } // namespace readability 689 } // namespace tidy 690 } // namespace clang 691