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 const Expr *stmtReturnsBool(const ReturnStmt *Ret, bool Negated) { 240 if (const auto *Bool = dyn_cast<CXXBoolLiteralExpr>(Ret->getRetValue())) { 241 if (Bool->getValue() == !Negated) 242 return Bool; 243 } 244 if (const auto *Unary = dyn_cast<UnaryOperator>(Ret->getRetValue())) { 245 if (Unary->getOpcode() == UO_LNot) { 246 if (const auto *Bool = 247 dyn_cast<CXXBoolLiteralExpr>(Unary->getSubExpr())) { 248 if (Bool->getValue() == Negated) 249 return Bool; 250 } 251 } 252 } 253 254 return nullptr; 255 } 256 257 static const Expr *stmtReturnsBool(const IfStmt *IfRet, bool Negated) { 258 if (IfRet->getElse() != nullptr) 259 return nullptr; 260 261 if (const auto *Ret = dyn_cast<ReturnStmt>(IfRet->getThen())) 262 return stmtReturnsBool(Ret, Negated); 263 264 if (const auto *Compound = dyn_cast<CompoundStmt>(IfRet->getThen())) { 265 if (Compound->size() == 1) { 266 if (const auto *CompoundRet = dyn_cast<ReturnStmt>(Compound->body_back())) 267 return stmtReturnsBool(CompoundRet, Negated); 268 } 269 } 270 271 return nullptr; 272 } 273 274 static bool containsDiscardedTokens(const ASTContext &Context, 275 CharSourceRange CharRange) { 276 std::string ReplacementText = 277 Lexer::getSourceText(CharRange, Context.getSourceManager(), 278 Context.getLangOpts()) 279 .str(); 280 Lexer Lex(CharRange.getBegin(), Context.getLangOpts(), ReplacementText.data(), 281 ReplacementText.data(), 282 ReplacementText.data() + ReplacementText.size()); 283 Lex.SetCommentRetentionState(true); 284 285 Token Tok; 286 while (!Lex.LexFromRawLexer(Tok)) { 287 if (Tok.is(tok::TokenKind::comment) || Tok.is(tok::TokenKind::hash)) 288 return true; 289 } 290 291 return false; 292 } 293 294 class SimplifyBooleanExprCheck::Visitor : public RecursiveASTVisitor<Visitor> { 295 public: 296 Visitor(SimplifyBooleanExprCheck *Check, ASTContext &Context) 297 : Check(Check), Context(Context) {} 298 299 bool traverse() { return TraverseAST(Context); } 300 301 static bool shouldIgnore(Stmt *S) { 302 switch (S->getStmtClass()) { 303 case Stmt::ImplicitCastExprClass: 304 case Stmt::MaterializeTemporaryExprClass: 305 case Stmt::CXXBindTemporaryExprClass: 306 return true; 307 default: 308 return false; 309 } 310 } 311 312 bool dataTraverseStmtPre(Stmt *S) { 313 if (S && !shouldIgnore(S)) 314 StmtStack.push_back(S); 315 return true; 316 } 317 318 bool dataTraverseStmtPost(Stmt *S) { 319 if (S && !shouldIgnore(S)) { 320 assert(StmtStack.back() == S); 321 StmtStack.pop_back(); 322 } 323 return true; 324 } 325 326 bool VisitBinaryOperator(const BinaryOperator *Op) const { 327 Check->reportBinOp(Context, Op); 328 return true; 329 } 330 331 // Extracts a bool if an expression is (true|false|!true|!false); 332 static Optional<bool> getAsBoolLiteral(const Expr *E, bool FilterMacro) { 333 if (const auto *Bool = dyn_cast<CXXBoolLiteralExpr>(E)) { 334 if (FilterMacro && Bool->getBeginLoc().isMacroID()) 335 return llvm::None; 336 return Bool->getValue(); 337 } 338 if (const auto *UnaryOp = dyn_cast<UnaryOperator>(E)) { 339 if (FilterMacro && UnaryOp->getBeginLoc().isMacroID()) 340 return None; 341 if (UnaryOp->getOpcode() == UO_LNot) 342 if (Optional<bool> Res = getAsBoolLiteral( 343 UnaryOp->getSubExpr()->IgnoreImplicit(), FilterMacro)) 344 return !*Res; 345 } 346 return llvm::None; 347 } 348 349 template <typename Node> struct NodeAndBool { 350 const Node *Item = nullptr; 351 bool Bool = false; 352 353 operator bool() const { return Item != nullptr; } 354 }; 355 356 using ExprAndBool = NodeAndBool<Expr>; 357 using DeclAndBool = NodeAndBool<Decl>; 358 359 /// Detect's return (true|false|!true|!false); 360 static ExprAndBool parseReturnLiteralBool(const Stmt *S) { 361 const auto *RS = dyn_cast<ReturnStmt>(S); 362 if (!RS || !RS->getRetValue()) 363 return {}; 364 if (Optional<bool> Ret = 365 getAsBoolLiteral(RS->getRetValue()->IgnoreImplicit(), false)) { 366 return {RS->getRetValue(), *Ret}; 367 } 368 return {}; 369 } 370 371 /// If \p S is not a \c CompoundStmt, applies F on \p S, otherwise if there is 372 /// only 1 statement in the \c CompoundStmt, applies F on that single 373 /// statement. 374 template <typename Functor> 375 static auto checkSingleStatement(Stmt *S, Functor F) -> decltype(F(S)) { 376 if (auto *CS = dyn_cast<CompoundStmt>(S)) { 377 if (CS->size() == 1) 378 return F(CS->body_front()); 379 return {}; 380 } 381 return F(S); 382 } 383 384 Stmt *parent() const { 385 return StmtStack.size() < 2 ? nullptr : StmtStack[StmtStack.size() - 2]; 386 } 387 388 bool VisitIfStmt(IfStmt *If) { 389 /* 390 * if (true) ThenStmt(); -> ThenStmt(); 391 * if (false) ThenStmt(); -> <Empty>; 392 * if (false) ThenStmt(); else ElseStmt() -> ElseStmt(); 393 */ 394 Expr *Cond = If->getCond()->IgnoreImplicit(); 395 if (Optional<bool> Bool = getAsBoolLiteral(Cond, true)) { 396 if (*Bool) 397 Check->replaceWithThenStatement(Context, If, Cond); 398 else 399 Check->replaceWithElseStatement(Context, If, Cond); 400 } 401 402 if (If->getElse()) { 403 /* 404 * if (Cond) return true; else return false; -> return Cond; 405 * if (Cond) return false; else return true; -> return !Cond; 406 */ 407 if (ExprAndBool ThenReturnBool = 408 checkSingleStatement(If->getThen(), parseReturnLiteralBool)) { 409 ExprAndBool ElseReturnBool = 410 checkSingleStatement(If->getElse(), parseReturnLiteralBool); 411 if (ElseReturnBool && ThenReturnBool.Bool != ElseReturnBool.Bool) { 412 if (Check->ChainedConditionalReturn || 413 !isa_and_nonnull<IfStmt>(parent())) { 414 Check->replaceWithReturnCondition(Context, If, ThenReturnBool.Item, 415 ElseReturnBool.Bool); 416 } 417 } 418 } else { 419 /* 420 * if (Cond) A = true; else A = false; -> A = Cond; 421 * if (Cond) A = false; else A = true; -> A = !Cond; 422 */ 423 Expr *Var = nullptr; 424 SourceLocation Loc; 425 auto VarBoolAssignmentMatcher = [&Var, 426 &Loc](const Stmt *S) -> DeclAndBool { 427 const auto *BO = dyn_cast<BinaryOperator>(S); 428 if (!BO || BO->getOpcode() != BO_Assign) 429 return {}; 430 Optional<bool> RightasBool = 431 getAsBoolLiteral(BO->getRHS()->IgnoreImplicit(), false); 432 if (!RightasBool) 433 return {}; 434 Expr *IgnImp = BO->getLHS()->IgnoreImplicit(); 435 if (!Var) { 436 // We only need to track these for the Then branch. 437 Loc = BO->getRHS()->getBeginLoc(); 438 Var = IgnImp; 439 } 440 if (auto *DRE = dyn_cast<DeclRefExpr>(IgnImp)) 441 return {DRE->getDecl(), *RightasBool}; 442 if (auto *ME = dyn_cast<MemberExpr>(IgnImp)) 443 return {ME->getMemberDecl(), *RightasBool}; 444 return {}; 445 }; 446 if (DeclAndBool ThenAssignment = 447 checkSingleStatement(If->getThen(), VarBoolAssignmentMatcher)) { 448 DeclAndBool ElseAssignment = 449 checkSingleStatement(If->getElse(), VarBoolAssignmentMatcher); 450 if (ElseAssignment.Item == ThenAssignment.Item && 451 ElseAssignment.Bool != ThenAssignment.Bool) { 452 if (Check->ChainedConditionalAssignment || 453 !isa_and_nonnull<IfStmt>(parent())) { 454 Check->replaceWithAssignment(Context, If, Var, Loc, 455 ElseAssignment.Bool); 456 } 457 } 458 } 459 } 460 } 461 return true; 462 } 463 464 bool VisitConditionalOperator(ConditionalOperator *Cond) { 465 /* 466 * Condition ? true : false; -> Condition 467 * Condition ? false : true; -> !Condition; 468 */ 469 if (Optional<bool> Then = 470 getAsBoolLiteral(Cond->getTrueExpr()->IgnoreImplicit(), false)) { 471 if (Optional<bool> Else = 472 getAsBoolLiteral(Cond->getFalseExpr()->IgnoreImplicit(), false)) { 473 if (*Then != *Else) 474 Check->replaceWithCondition(Context, Cond, *Else); 475 } 476 } 477 return true; 478 } 479 480 bool VisitCompoundStmt(CompoundStmt *CS) { 481 if (CS->size() < 2) 482 return true; 483 bool CurIf = false, PrevIf = false; 484 for (auto First = CS->body_begin(), Second = std::next(First), 485 End = CS->body_end(); 486 Second != End; ++Second, ++First) { 487 PrevIf = CurIf; 488 CurIf = isa<IfStmt>(*First); 489 ExprAndBool TrailingReturnBool = parseReturnLiteralBool(*Second); 490 if (!TrailingReturnBool) 491 continue; 492 493 if (CurIf) { 494 /* 495 * if (Cond) return true; return false; -> return Cond; 496 * if (Cond) return false; return true; -> return !Cond; 497 */ 498 auto *If = cast<IfStmt>(*First); 499 ExprAndBool ThenReturnBool = 500 checkSingleStatement(If->getThen(), parseReturnLiteralBool); 501 if (ThenReturnBool && ThenReturnBool.Bool != TrailingReturnBool.Bool) { 502 if (Check->ChainedConditionalReturn || 503 (!PrevIf && If->getElse() == nullptr)) { 504 Check->replaceCompoundReturnWithCondition( 505 Context, cast<ReturnStmt>(*Second), TrailingReturnBool.Bool, 506 If); 507 } 508 } 509 } else if (isa<LabelStmt, CaseStmt, DefaultStmt>(*First)) { 510 /* 511 * (case X|label_X|default): if (Cond) return BoolLiteral; 512 * return !BoolLiteral 513 */ 514 Stmt *SubStmt = 515 isa<LabelStmt>(*First) ? cast<LabelStmt>(*First)->getSubStmt() 516 : isa<CaseStmt>(*First) ? cast<CaseStmt>(*First)->getSubStmt() 517 : cast<DefaultStmt>(*First)->getSubStmt(); 518 auto *SubIf = dyn_cast<IfStmt>(SubStmt); 519 if (SubIf && !SubIf->getElse()) { 520 ExprAndBool ThenReturnBool = 521 checkSingleStatement(SubIf->getThen(), parseReturnLiteralBool); 522 if (ThenReturnBool && 523 ThenReturnBool.Bool != TrailingReturnBool.Bool) { 524 Check->replaceCompoundReturnWithCondition( 525 Context, cast<ReturnStmt>(*Second), TrailingReturnBool.Bool, 526 SubIf); 527 } 528 } 529 } 530 } 531 return true; 532 } 533 534 private: 535 SimplifyBooleanExprCheck *Check; 536 SmallVector<Stmt *, 32> StmtStack; 537 ASTContext &Context; 538 }; 539 540 SimplifyBooleanExprCheck::SimplifyBooleanExprCheck(StringRef Name, 541 ClangTidyContext *Context) 542 : ClangTidyCheck(Name, Context), 543 ChainedConditionalReturn(Options.get("ChainedConditionalReturn", false)), 544 ChainedConditionalAssignment( 545 Options.get("ChainedConditionalAssignment", false)) {} 546 547 static bool containsBoolLiteral(const Expr *E) { 548 if (!E) 549 return false; 550 E = E->IgnoreParenImpCasts(); 551 if (isa<CXXBoolLiteralExpr>(E)) 552 return true; 553 if (const auto *BinOp = dyn_cast<BinaryOperator>(E)) 554 return containsBoolLiteral(BinOp->getLHS()) || 555 containsBoolLiteral(BinOp->getRHS()); 556 if (const auto *UnaryOp = dyn_cast<UnaryOperator>(E)) 557 return containsBoolLiteral(UnaryOp->getSubExpr()); 558 return false; 559 } 560 561 void SimplifyBooleanExprCheck::reportBinOp(const ASTContext &Context, 562 const BinaryOperator *Op) { 563 const auto *LHS = Op->getLHS()->IgnoreParenImpCasts(); 564 const auto *RHS = Op->getRHS()->IgnoreParenImpCasts(); 565 566 const CXXBoolLiteralExpr *Bool; 567 const Expr *Other; 568 if ((Bool = dyn_cast<CXXBoolLiteralExpr>(LHS)) != nullptr) 569 Other = RHS; 570 else if ((Bool = dyn_cast<CXXBoolLiteralExpr>(RHS)) != nullptr) 571 Other = LHS; 572 else 573 return; 574 575 if (Bool->getBeginLoc().isMacroID()) 576 return; 577 578 // FIXME: why do we need this? 579 if (!isa<CXXBoolLiteralExpr>(Other) && containsBoolLiteral(Other)) 580 return; 581 582 bool BoolValue = Bool->getValue(); 583 584 auto ReplaceWithExpression = [this, &Context, LHS, RHS, 585 Bool](const Expr *ReplaceWith, bool Negated) { 586 std::string Replacement = 587 replacementExpression(Context, Negated, ReplaceWith); 588 SourceRange Range(LHS->getBeginLoc(), RHS->getEndLoc()); 589 issueDiag(Context, Bool->getBeginLoc(), SimplifyOperatorDiagnostic, Range, 590 Replacement); 591 }; 592 593 switch (Op->getOpcode()) { 594 case BO_LAnd: 595 if (BoolValue) 596 // expr && true -> expr 597 ReplaceWithExpression(Other, /*Negated=*/false); 598 else 599 // expr && false -> false 600 ReplaceWithExpression(Bool, /*Negated=*/false); 601 break; 602 case BO_LOr: 603 if (BoolValue) 604 // expr || true -> true 605 ReplaceWithExpression(Bool, /*Negated=*/false); 606 else 607 // expr || false -> expr 608 ReplaceWithExpression(Other, /*Negated=*/false); 609 break; 610 case BO_EQ: 611 // expr == true -> expr, expr == false -> !expr 612 ReplaceWithExpression(Other, /*Negated=*/!BoolValue); 613 break; 614 case BO_NE: 615 // expr != true -> !expr, expr != false -> expr 616 ReplaceWithExpression(Other, /*Negated=*/BoolValue); 617 break; 618 default: 619 break; 620 } 621 } 622 623 void SimplifyBooleanExprCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { 624 Options.store(Opts, "ChainedConditionalReturn", ChainedConditionalReturn); 625 Options.store(Opts, "ChainedConditionalAssignment", 626 ChainedConditionalAssignment); 627 } 628 629 void SimplifyBooleanExprCheck::registerMatchers(MatchFinder *Finder) { 630 Finder->addMatcher(translationUnitDecl(), this); 631 } 632 633 void SimplifyBooleanExprCheck::check(const MatchFinder::MatchResult &Result) { 634 Visitor(this, *Result.Context).traverse(); 635 } 636 637 void SimplifyBooleanExprCheck::issueDiag(const ASTContext &Context, 638 SourceLocation Loc, 639 StringRef Description, 640 SourceRange ReplacementRange, 641 StringRef Replacement) { 642 CharSourceRange CharRange = 643 Lexer::makeFileCharRange(CharSourceRange::getTokenRange(ReplacementRange), 644 Context.getSourceManager(), getLangOpts()); 645 646 DiagnosticBuilder Diag = diag(Loc, Description); 647 if (!containsDiscardedTokens(Context, CharRange)) 648 Diag << FixItHint::CreateReplacement(CharRange, Replacement); 649 } 650 651 void SimplifyBooleanExprCheck::replaceWithThenStatement( 652 const ASTContext &Context, const IfStmt *IfStatement, 653 const Expr *BoolLiteral) { 654 issueDiag(Context, BoolLiteral->getBeginLoc(), SimplifyConditionDiagnostic, 655 IfStatement->getSourceRange(), 656 getText(Context, *IfStatement->getThen())); 657 } 658 659 void SimplifyBooleanExprCheck::replaceWithElseStatement( 660 const ASTContext &Context, const IfStmt *IfStatement, 661 const Expr *BoolLiteral) { 662 const Stmt *ElseStatement = IfStatement->getElse(); 663 issueDiag(Context, BoolLiteral->getBeginLoc(), SimplifyConditionDiagnostic, 664 IfStatement->getSourceRange(), 665 ElseStatement ? getText(Context, *ElseStatement) : ""); 666 } 667 668 void SimplifyBooleanExprCheck::replaceWithCondition( 669 const ASTContext &Context, const ConditionalOperator *Ternary, 670 bool Negated) { 671 std::string Replacement = 672 replacementExpression(Context, Negated, Ternary->getCond()); 673 issueDiag(Context, Ternary->getTrueExpr()->getBeginLoc(), 674 "redundant boolean literal in ternary expression result", 675 Ternary->getSourceRange(), Replacement); 676 } 677 678 void SimplifyBooleanExprCheck::replaceWithReturnCondition( 679 const ASTContext &Context, const IfStmt *If, const Expr *BoolLiteral, 680 bool Negated) { 681 StringRef Terminator = isa<CompoundStmt>(If->getElse()) ? ";" : ""; 682 std::string Condition = 683 replacementExpression(Context, Negated, If->getCond()); 684 std::string Replacement = ("return " + Condition + Terminator).str(); 685 SourceLocation Start = BoolLiteral->getBeginLoc(); 686 issueDiag(Context, Start, SimplifyConditionalReturnDiagnostic, 687 If->getSourceRange(), Replacement); 688 } 689 690 void SimplifyBooleanExprCheck::replaceCompoundReturnWithCondition( 691 const ASTContext &Context, const ReturnStmt *Ret, bool Negated, 692 const IfStmt *If) { 693 const auto *Lit = stmtReturnsBool(If, Negated); 694 const std::string Replacement = 695 "return " + replacementExpression(Context, Negated, If->getCond()); 696 issueDiag(Context, Lit->getBeginLoc(), SimplifyConditionalReturnDiagnostic, 697 SourceRange(If->getBeginLoc(), Ret->getEndLoc()), Replacement); 698 } 699 700 void SimplifyBooleanExprCheck::replaceWithAssignment(const ASTContext &Context, 701 const IfStmt *IfAssign, 702 const Expr *Var, 703 SourceLocation Loc, 704 bool Negated) { 705 SourceRange Range = IfAssign->getSourceRange(); 706 StringRef VariableName = getText(Context, *Var); 707 StringRef Terminator = isa<CompoundStmt>(IfAssign->getElse()) ? ";" : ""; 708 std::string Condition = 709 replacementExpression(Context, Negated, IfAssign->getCond()); 710 std::string Replacement = 711 (VariableName + " = " + Condition + Terminator).str(); 712 issueDiag(Context, Loc, "redundant boolean literal in conditional assignment", 713 Range, Replacement); 714 } 715 716 } // namespace readability 717 } // namespace tidy 718 } // namespace clang 719