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 <cassert> 14 #include <string> 15 #include <utility> 16 17 using namespace clang::ast_matchers; 18 19 namespace clang { 20 namespace tidy { 21 namespace readability { 22 23 namespace { 24 25 StringRef getText(const MatchFinder::MatchResult &Result, SourceRange Range) { 26 return Lexer::getSourceText(CharSourceRange::getTokenRange(Range), 27 *Result.SourceManager, 28 Result.Context->getLangOpts()); 29 } 30 31 template <typename T> 32 StringRef getText(const MatchFinder::MatchResult &Result, T &Node) { 33 return getText(Result, Node.getSourceRange()); 34 } 35 36 const char ConditionThenStmtId[] = "if-bool-yields-then"; 37 const char ConditionElseStmtId[] = "if-bool-yields-else"; 38 const char TernaryId[] = "ternary-bool-yields-condition"; 39 const char TernaryNegatedId[] = "ternary-bool-yields-not-condition"; 40 const char IfReturnsBoolId[] = "if-return"; 41 const char IfReturnsNotBoolId[] = "if-not-return"; 42 const char ThenLiteralId[] = "then-literal"; 43 const char IfAssignVariableId[] = "if-assign-lvalue"; 44 const char IfAssignLocId[] = "if-assign-loc"; 45 const char IfAssignBoolId[] = "if-assign"; 46 const char IfAssignNotBoolId[] = "if-assign-not"; 47 const char IfAssignVarId[] = "if-assign-var"; 48 const char CompoundReturnId[] = "compound-return"; 49 const char CompoundBoolId[] = "compound-bool"; 50 const char CompoundNotBoolId[] = "compound-bool-not"; 51 52 const char IfStmtId[] = "if"; 53 54 const char SimplifyOperatorDiagnostic[] = 55 "redundant boolean literal supplied to boolean operator"; 56 const char SimplifyConditionDiagnostic[] = 57 "redundant boolean literal in if statement condition"; 58 const char SimplifyConditionalReturnDiagnostic[] = 59 "redundant boolean literal in conditional return statement"; 60 61 const Expr *getBoolLiteral(const MatchFinder::MatchResult &Result, 62 StringRef Id) { 63 if (const Expr *Literal = Result.Nodes.getNodeAs<CXXBoolLiteralExpr>(Id)) 64 return Literal->getBeginLoc().isMacroID() ? nullptr : Literal; 65 if (const auto *Negated = Result.Nodes.getNodeAs<UnaryOperator>(Id)) { 66 if (Negated->getOpcode() == UO_LNot && 67 isa<CXXBoolLiteralExpr>(Negated->getSubExpr())) 68 return Negated->getBeginLoc().isMacroID() ? nullptr : Negated; 69 } 70 return nullptr; 71 } 72 73 internal::BindableMatcher<Stmt> literalOrNegatedBool(bool Value) { 74 return expr( 75 anyOf(cxxBoolLiteral(equals(Value)), 76 unaryOperator(hasUnaryOperand(cxxBoolLiteral(equals(!Value))), 77 hasOperatorName("!")))); 78 } 79 80 internal::Matcher<Stmt> returnsBool(bool Value, StringRef Id = "ignored") { 81 auto SimpleReturnsBool = returnStmt(has(literalOrNegatedBool(Value).bind(Id))) 82 .bind("returns-bool"); 83 return anyOf(SimpleReturnsBool, 84 compoundStmt(statementCountIs(1), has(SimpleReturnsBool))); 85 } 86 87 bool needsParensAfterUnaryNegation(const Expr *E) { 88 E = E->IgnoreImpCasts(); 89 if (isa<BinaryOperator>(E) || isa<ConditionalOperator>(E)) 90 return true; 91 92 if (const auto *Op = dyn_cast<CXXOperatorCallExpr>(E)) 93 return Op->getNumArgs() == 2 && Op->getOperator() != OO_Call && 94 Op->getOperator() != OO_Subscript; 95 96 return false; 97 } 98 99 std::pair<BinaryOperatorKind, BinaryOperatorKind> Opposites[] = { 100 {BO_LT, BO_GE}, {BO_GT, BO_LE}, {BO_EQ, BO_NE}}; 101 102 StringRef negatedOperator(const BinaryOperator *BinOp) { 103 const BinaryOperatorKind Opcode = BinOp->getOpcode(); 104 for (auto NegatableOp : Opposites) { 105 if (Opcode == NegatableOp.first) 106 return BinOp->getOpcodeStr(NegatableOp.second); 107 if (Opcode == NegatableOp.second) 108 return BinOp->getOpcodeStr(NegatableOp.first); 109 } 110 return StringRef(); 111 } 112 113 std::pair<OverloadedOperatorKind, StringRef> OperatorNames[] = { 114 {OO_EqualEqual, "=="}, {OO_ExclaimEqual, "!="}, {OO_Less, "<"}, 115 {OO_GreaterEqual, ">="}, {OO_Greater, ">"}, {OO_LessEqual, "<="}}; 116 117 StringRef getOperatorName(OverloadedOperatorKind OpKind) { 118 for (auto Name : OperatorNames) { 119 if (Name.first == OpKind) 120 return Name.second; 121 } 122 123 return StringRef(); 124 } 125 126 std::pair<OverloadedOperatorKind, OverloadedOperatorKind> OppositeOverloads[] = 127 {{OO_EqualEqual, OO_ExclaimEqual}, 128 {OO_Less, OO_GreaterEqual}, 129 {OO_Greater, OO_LessEqual}}; 130 131 StringRef negatedOperator(const CXXOperatorCallExpr *OpCall) { 132 const OverloadedOperatorKind Opcode = OpCall->getOperator(); 133 for (auto NegatableOp : OppositeOverloads) { 134 if (Opcode == NegatableOp.first) 135 return getOperatorName(NegatableOp.second); 136 if (Opcode == NegatableOp.second) 137 return getOperatorName(NegatableOp.first); 138 } 139 return StringRef(); 140 } 141 142 std::string asBool(StringRef Text, bool NeedsStaticCast) { 143 if (NeedsStaticCast) 144 return ("static_cast<bool>(" + Text + ")").str(); 145 146 return std::string(Text); 147 } 148 149 bool needsNullPtrComparison(const Expr *E) { 150 if (const auto *ImpCast = dyn_cast<ImplicitCastExpr>(E)) 151 return ImpCast->getCastKind() == CK_PointerToBoolean || 152 ImpCast->getCastKind() == CK_MemberPointerToBoolean; 153 154 return false; 155 } 156 157 bool needsZeroComparison(const Expr *E) { 158 if (const auto *ImpCast = dyn_cast<ImplicitCastExpr>(E)) 159 return ImpCast->getCastKind() == CK_IntegralToBoolean; 160 161 return false; 162 } 163 164 bool needsStaticCast(const Expr *E) { 165 if (const auto *ImpCast = dyn_cast<ImplicitCastExpr>(E)) { 166 if (ImpCast->getCastKind() == CK_UserDefinedConversion && 167 ImpCast->getSubExpr()->getType()->isBooleanType()) { 168 if (const auto *MemCall = 169 dyn_cast<CXXMemberCallExpr>(ImpCast->getSubExpr())) { 170 if (const auto *MemDecl = 171 dyn_cast<CXXConversionDecl>(MemCall->getMethodDecl())) { 172 if (MemDecl->isExplicit()) 173 return true; 174 } 175 } 176 } 177 } 178 179 E = E->IgnoreImpCasts(); 180 return !E->getType()->isBooleanType(); 181 } 182 183 std::string compareExpressionToConstant(const MatchFinder::MatchResult &Result, 184 const Expr *E, bool Negated, 185 const char *Constant) { 186 E = E->IgnoreImpCasts(); 187 const std::string ExprText = 188 (isa<BinaryOperator>(E) ? ("(" + getText(Result, *E) + ")") 189 : getText(Result, *E)) 190 .str(); 191 return ExprText + " " + (Negated ? "!=" : "==") + " " + Constant; 192 } 193 194 std::string compareExpressionToNullPtr(const MatchFinder::MatchResult &Result, 195 const Expr *E, bool Negated) { 196 const char *NullPtr = 197 Result.Context->getLangOpts().CPlusPlus11 ? "nullptr" : "NULL"; 198 return compareExpressionToConstant(Result, E, Negated, NullPtr); 199 } 200 201 std::string compareExpressionToZero(const MatchFinder::MatchResult &Result, 202 const Expr *E, bool Negated) { 203 return compareExpressionToConstant(Result, E, Negated, "0"); 204 } 205 206 std::string replacementExpression(const MatchFinder::MatchResult &Result, 207 bool Negated, const Expr *E) { 208 E = E->IgnoreParenBaseCasts(); 209 if (const auto *EC = dyn_cast<ExprWithCleanups>(E)) 210 E = EC->getSubExpr(); 211 212 const bool NeedsStaticCast = needsStaticCast(E); 213 if (Negated) { 214 if (const auto *UnOp = dyn_cast<UnaryOperator>(E)) { 215 if (UnOp->getOpcode() == UO_LNot) { 216 if (needsNullPtrComparison(UnOp->getSubExpr())) 217 return compareExpressionToNullPtr(Result, UnOp->getSubExpr(), true); 218 219 if (needsZeroComparison(UnOp->getSubExpr())) 220 return compareExpressionToZero(Result, UnOp->getSubExpr(), true); 221 222 return replacementExpression(Result, false, UnOp->getSubExpr()); 223 } 224 } 225 226 if (needsNullPtrComparison(E)) 227 return compareExpressionToNullPtr(Result, E, false); 228 229 if (needsZeroComparison(E)) 230 return compareExpressionToZero(Result, E, false); 231 232 StringRef NegatedOperator; 233 const Expr *LHS = nullptr; 234 const Expr *RHS = nullptr; 235 if (const auto *BinOp = dyn_cast<BinaryOperator>(E)) { 236 NegatedOperator = negatedOperator(BinOp); 237 LHS = BinOp->getLHS(); 238 RHS = BinOp->getRHS(); 239 } else if (const auto *OpExpr = dyn_cast<CXXOperatorCallExpr>(E)) { 240 if (OpExpr->getNumArgs() == 2) { 241 NegatedOperator = negatedOperator(OpExpr); 242 LHS = OpExpr->getArg(0); 243 RHS = OpExpr->getArg(1); 244 } 245 } 246 if (!NegatedOperator.empty() && LHS && RHS) 247 return (asBool((getText(Result, *LHS) + " " + NegatedOperator + " " + 248 getText(Result, *RHS)) 249 .str(), 250 NeedsStaticCast)); 251 252 StringRef Text = getText(Result, *E); 253 if (!NeedsStaticCast && needsParensAfterUnaryNegation(E)) 254 return ("!(" + Text + ")").str(); 255 256 if (needsNullPtrComparison(E)) 257 return compareExpressionToNullPtr(Result, E, false); 258 259 if (needsZeroComparison(E)) 260 return compareExpressionToZero(Result, E, false); 261 262 return ("!" + asBool(Text, NeedsStaticCast)); 263 } 264 265 if (const auto *UnOp = dyn_cast<UnaryOperator>(E)) { 266 if (UnOp->getOpcode() == UO_LNot) { 267 if (needsNullPtrComparison(UnOp->getSubExpr())) 268 return compareExpressionToNullPtr(Result, UnOp->getSubExpr(), false); 269 270 if (needsZeroComparison(UnOp->getSubExpr())) 271 return compareExpressionToZero(Result, UnOp->getSubExpr(), false); 272 } 273 } 274 275 if (needsNullPtrComparison(E)) 276 return compareExpressionToNullPtr(Result, E, true); 277 278 if (needsZeroComparison(E)) 279 return compareExpressionToZero(Result, E, true); 280 281 return asBool(getText(Result, *E), NeedsStaticCast); 282 } 283 284 const Expr *stmtReturnsBool(const ReturnStmt *Ret, bool Negated) { 285 if (const auto *Bool = dyn_cast<CXXBoolLiteralExpr>(Ret->getRetValue())) { 286 if (Bool->getValue() == !Negated) 287 return Bool; 288 } 289 if (const auto *Unary = dyn_cast<UnaryOperator>(Ret->getRetValue())) { 290 if (Unary->getOpcode() == UO_LNot) { 291 if (const auto *Bool = 292 dyn_cast<CXXBoolLiteralExpr>(Unary->getSubExpr())) { 293 if (Bool->getValue() == Negated) 294 return Bool; 295 } 296 } 297 } 298 299 return nullptr; 300 } 301 302 const Expr *stmtReturnsBool(const IfStmt *IfRet, bool Negated) { 303 if (IfRet->getElse() != nullptr) 304 return nullptr; 305 306 if (const auto *Ret = dyn_cast<ReturnStmt>(IfRet->getThen())) 307 return stmtReturnsBool(Ret, Negated); 308 309 if (const auto *Compound = dyn_cast<CompoundStmt>(IfRet->getThen())) { 310 if (Compound->size() == 1) { 311 if (const auto *CompoundRet = dyn_cast<ReturnStmt>(Compound->body_back())) 312 return stmtReturnsBool(CompoundRet, Negated); 313 } 314 } 315 316 return nullptr; 317 } 318 319 bool containsDiscardedTokens(const MatchFinder::MatchResult &Result, 320 CharSourceRange CharRange) { 321 std::string ReplacementText = 322 Lexer::getSourceText(CharRange, *Result.SourceManager, 323 Result.Context->getLangOpts()) 324 .str(); 325 Lexer Lex(CharRange.getBegin(), Result.Context->getLangOpts(), 326 ReplacementText.data(), ReplacementText.data(), 327 ReplacementText.data() + ReplacementText.size()); 328 Lex.SetCommentRetentionState(true); 329 330 Token Tok; 331 while (!Lex.LexFromRawLexer(Tok)) { 332 if (Tok.is(tok::TokenKind::comment) || Tok.is(tok::TokenKind::hash)) 333 return true; 334 } 335 336 return false; 337 } 338 339 } // namespace 340 341 class SimplifyBooleanExprCheck::Visitor : public RecursiveASTVisitor<Visitor> { 342 public: 343 Visitor(SimplifyBooleanExprCheck *Check, 344 const MatchFinder::MatchResult &Result) 345 : Check(Check), Result(Result) {} 346 347 bool VisitBinaryOperator(BinaryOperator *Op) { 348 Check->reportBinOp(Result, Op); 349 return true; 350 } 351 352 private: 353 SimplifyBooleanExprCheck *Check; 354 const MatchFinder::MatchResult &Result; 355 }; 356 357 SimplifyBooleanExprCheck::SimplifyBooleanExprCheck(StringRef Name, 358 ClangTidyContext *Context) 359 : ClangTidyCheck(Name, Context), 360 ChainedConditionalReturn(Options.get("ChainedConditionalReturn", false)), 361 ChainedConditionalAssignment( 362 Options.get("ChainedConditionalAssignment", false)) {} 363 364 bool containsBoolLiteral(const Expr *E) { 365 if (!E) 366 return false; 367 E = E->IgnoreParenImpCasts(); 368 if (isa<CXXBoolLiteralExpr>(E)) 369 return true; 370 if (const auto *BinOp = dyn_cast<BinaryOperator>(E)) 371 return containsBoolLiteral(BinOp->getLHS()) || 372 containsBoolLiteral(BinOp->getRHS()); 373 if (const auto *UnaryOp = dyn_cast<UnaryOperator>(E)) 374 return containsBoolLiteral(UnaryOp->getSubExpr()); 375 return false; 376 } 377 378 void SimplifyBooleanExprCheck::reportBinOp( 379 const MatchFinder::MatchResult &Result, const BinaryOperator *Op) { 380 const auto *LHS = Op->getLHS()->IgnoreParenImpCasts(); 381 const auto *RHS = Op->getRHS()->IgnoreParenImpCasts(); 382 383 const CXXBoolLiteralExpr *Bool; 384 const Expr *Other = nullptr; 385 if ((Bool = dyn_cast<CXXBoolLiteralExpr>(LHS))) 386 Other = RHS; 387 else if ((Bool = dyn_cast<CXXBoolLiteralExpr>(RHS))) 388 Other = LHS; 389 else 390 return; 391 392 if (Bool->getBeginLoc().isMacroID()) 393 return; 394 395 // FIXME: why do we need this? 396 if (!isa<CXXBoolLiteralExpr>(Other) && containsBoolLiteral(Other)) 397 return; 398 399 bool BoolValue = Bool->getValue(); 400 401 auto ReplaceWithExpression = [this, &Result, LHS, RHS, 402 Bool](const Expr *ReplaceWith, bool Negated) { 403 std::string Replacement = 404 replacementExpression(Result, Negated, ReplaceWith); 405 SourceRange Range(LHS->getBeginLoc(), RHS->getEndLoc()); 406 issueDiag(Result, Bool->getBeginLoc(), SimplifyOperatorDiagnostic, Range, 407 Replacement); 408 }; 409 410 switch (Op->getOpcode()) { 411 case BO_LAnd: 412 if (BoolValue) { 413 // expr && true -> expr 414 ReplaceWithExpression(Other, /*Negated=*/false); 415 } else { 416 // expr && false -> false 417 ReplaceWithExpression(Bool, /*Negated=*/false); 418 } 419 break; 420 case BO_LOr: 421 if (BoolValue) { 422 // expr || true -> true 423 ReplaceWithExpression(Bool, /*Negated=*/false); 424 } else { 425 // expr || false -> expr 426 ReplaceWithExpression(Other, /*Negated=*/false); 427 } 428 break; 429 case BO_EQ: 430 // expr == true -> expr, expr == false -> !expr 431 ReplaceWithExpression(Other, /*Negated=*/!BoolValue); 432 break; 433 case BO_NE: 434 // expr != true -> !expr, expr != false -> expr 435 ReplaceWithExpression(Other, /*Negated=*/BoolValue); 436 break; 437 default: 438 break; 439 } 440 } 441 442 void SimplifyBooleanExprCheck::matchBoolCondition(MatchFinder *Finder, 443 bool Value, 444 StringRef BooleanId) { 445 Finder->addMatcher( 446 ifStmt(hasCondition(literalOrNegatedBool(Value).bind(BooleanId))) 447 .bind(IfStmtId), 448 this); 449 } 450 451 void SimplifyBooleanExprCheck::matchTernaryResult(MatchFinder *Finder, 452 bool Value, 453 StringRef TernaryId) { 454 Finder->addMatcher( 455 conditionalOperator(hasTrueExpression(literalOrNegatedBool(Value)), 456 hasFalseExpression(literalOrNegatedBool(!Value))) 457 .bind(TernaryId), 458 this); 459 } 460 461 void SimplifyBooleanExprCheck::matchIfReturnsBool(MatchFinder *Finder, 462 bool Value, StringRef Id) { 463 if (ChainedConditionalReturn) 464 Finder->addMatcher(ifStmt(hasThen(returnsBool(Value, ThenLiteralId)), 465 hasElse(returnsBool(!Value))) 466 .bind(Id), 467 this); 468 else 469 Finder->addMatcher(ifStmt(unless(hasParent(ifStmt())), 470 hasThen(returnsBool(Value, ThenLiteralId)), 471 hasElse(returnsBool(!Value))) 472 .bind(Id), 473 this); 474 } 475 476 void SimplifyBooleanExprCheck::matchIfAssignsBool(MatchFinder *Finder, 477 bool Value, StringRef Id) { 478 auto VarAssign = declRefExpr(hasDeclaration(decl().bind(IfAssignVarId))); 479 auto VarRef = declRefExpr(hasDeclaration(equalsBoundNode(IfAssignVarId))); 480 auto MemAssign = memberExpr(hasDeclaration(decl().bind(IfAssignVarId))); 481 auto MemRef = memberExpr(hasDeclaration(equalsBoundNode(IfAssignVarId))); 482 auto SimpleThen = 483 binaryOperator(hasOperatorName("="), hasLHS(anyOf(VarAssign, MemAssign)), 484 hasLHS(expr().bind(IfAssignVariableId)), 485 hasRHS(literalOrNegatedBool(Value).bind(IfAssignLocId))); 486 auto Then = anyOf(SimpleThen, compoundStmt(statementCountIs(1), 487 hasAnySubstatement(SimpleThen))); 488 auto SimpleElse = 489 binaryOperator(hasOperatorName("="), hasLHS(anyOf(VarRef, MemRef)), 490 hasRHS(literalOrNegatedBool(!Value))); 491 auto Else = anyOf(SimpleElse, compoundStmt(statementCountIs(1), 492 hasAnySubstatement(SimpleElse))); 493 if (ChainedConditionalAssignment) 494 Finder->addMatcher(ifStmt(hasThen(Then), hasElse(Else)).bind(Id), this); 495 else 496 Finder->addMatcher( 497 ifStmt(unless(hasParent(ifStmt())), hasThen(Then), hasElse(Else)) 498 .bind(Id), 499 this); 500 } 501 502 void SimplifyBooleanExprCheck::matchCompoundIfReturnsBool(MatchFinder *Finder, 503 bool Value, 504 StringRef Id) { 505 Finder->addMatcher( 506 compoundStmt( 507 hasAnySubstatement( 508 ifStmt(hasThen(returnsBool(Value)), unless(hasElse(stmt())))), 509 hasAnySubstatement(returnStmt(has(literalOrNegatedBool(!Value))) 510 .bind(CompoundReturnId))) 511 .bind(Id), 512 this); 513 } 514 515 void SimplifyBooleanExprCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { 516 Options.store(Opts, "ChainedConditionalReturn", ChainedConditionalReturn); 517 Options.store(Opts, "ChainedConditionalAssignment", 518 ChainedConditionalAssignment); 519 } 520 521 void SimplifyBooleanExprCheck::registerMatchers(MatchFinder *Finder) { 522 Finder->addMatcher(translationUnitDecl().bind("top"), this); 523 524 matchBoolCondition(Finder, true, ConditionThenStmtId); 525 matchBoolCondition(Finder, false, ConditionElseStmtId); 526 527 matchTernaryResult(Finder, true, TernaryId); 528 matchTernaryResult(Finder, false, TernaryNegatedId); 529 530 matchIfReturnsBool(Finder, true, IfReturnsBoolId); 531 matchIfReturnsBool(Finder, false, IfReturnsNotBoolId); 532 533 matchIfAssignsBool(Finder, true, IfAssignBoolId); 534 matchIfAssignsBool(Finder, false, IfAssignNotBoolId); 535 536 matchCompoundIfReturnsBool(Finder, true, CompoundBoolId); 537 matchCompoundIfReturnsBool(Finder, false, CompoundNotBoolId); 538 } 539 540 void SimplifyBooleanExprCheck::check(const MatchFinder::MatchResult &Result) { 541 if (Result.Nodes.getNodeAs<TranslationUnitDecl>("top")) 542 Visitor(this, Result).TraverseAST(*Result.Context); 543 else if (const Expr *TrueConditionRemoved = 544 getBoolLiteral(Result, ConditionThenStmtId)) 545 replaceWithThenStatement(Result, TrueConditionRemoved); 546 else if (const Expr *FalseConditionRemoved = 547 getBoolLiteral(Result, ConditionElseStmtId)) 548 replaceWithElseStatement(Result, FalseConditionRemoved); 549 else if (const auto *Ternary = 550 Result.Nodes.getNodeAs<ConditionalOperator>(TernaryId)) 551 replaceWithCondition(Result, Ternary); 552 else if (const auto *TernaryNegated = 553 Result.Nodes.getNodeAs<ConditionalOperator>(TernaryNegatedId)) 554 replaceWithCondition(Result, TernaryNegated, true); 555 else if (const auto *If = Result.Nodes.getNodeAs<IfStmt>(IfReturnsBoolId)) 556 replaceWithReturnCondition(Result, If); 557 else if (const auto *IfNot = 558 Result.Nodes.getNodeAs<IfStmt>(IfReturnsNotBoolId)) 559 replaceWithReturnCondition(Result, IfNot, true); 560 else if (const auto *IfAssign = 561 Result.Nodes.getNodeAs<IfStmt>(IfAssignBoolId)) 562 replaceWithAssignment(Result, IfAssign); 563 else if (const auto *IfAssignNot = 564 Result.Nodes.getNodeAs<IfStmt>(IfAssignNotBoolId)) 565 replaceWithAssignment(Result, IfAssignNot, true); 566 else if (const auto *Compound = 567 Result.Nodes.getNodeAs<CompoundStmt>(CompoundBoolId)) 568 replaceCompoundReturnWithCondition(Result, Compound); 569 else if (const auto *Compound = 570 Result.Nodes.getNodeAs<CompoundStmt>(CompoundNotBoolId)) 571 replaceCompoundReturnWithCondition(Result, Compound, true); 572 } 573 574 void SimplifyBooleanExprCheck::issueDiag( 575 const ast_matchers::MatchFinder::MatchResult &Result, SourceLocation Loc, 576 StringRef Description, SourceRange ReplacementRange, 577 StringRef Replacement) { 578 CharSourceRange CharRange = 579 Lexer::makeFileCharRange(CharSourceRange::getTokenRange(ReplacementRange), 580 *Result.SourceManager, getLangOpts()); 581 582 DiagnosticBuilder Diag = diag(Loc, Description); 583 if (!containsDiscardedTokens(Result, CharRange)) 584 Diag << FixItHint::CreateReplacement(CharRange, Replacement); 585 } 586 587 void SimplifyBooleanExprCheck::replaceWithThenStatement( 588 const MatchFinder::MatchResult &Result, const Expr *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, const Expr *FalseConditionRemoved) { 597 const auto *IfStatement = Result.Nodes.getNodeAs<IfStmt>(IfStmtId); 598 const Stmt *ElseStatement = IfStatement->getElse(); 599 issueDiag(Result, FalseConditionRemoved->getBeginLoc(), 600 SimplifyConditionDiagnostic, IfStatement->getSourceRange(), 601 ElseStatement ? getText(Result, *ElseStatement) : ""); 602 } 603 604 void SimplifyBooleanExprCheck::replaceWithCondition( 605 const MatchFinder::MatchResult &Result, const ConditionalOperator *Ternary, 606 bool Negated) { 607 std::string Replacement = 608 replacementExpression(Result, Negated, Ternary->getCond()); 609 issueDiag(Result, Ternary->getTrueExpr()->getBeginLoc(), 610 "redundant boolean literal in ternary expression result", 611 Ternary->getSourceRange(), Replacement); 612 } 613 614 void SimplifyBooleanExprCheck::replaceWithReturnCondition( 615 const MatchFinder::MatchResult &Result, const IfStmt *If, bool Negated) { 616 StringRef Terminator = isa<CompoundStmt>(If->getElse()) ? ";" : ""; 617 std::string Condition = replacementExpression(Result, Negated, If->getCond()); 618 std::string Replacement = ("return " + Condition + Terminator).str(); 619 SourceLocation Start = 620 Result.Nodes.getNodeAs<CXXBoolLiteralExpr>(ThenLiteralId)->getBeginLoc(); 621 issueDiag(Result, Start, SimplifyConditionalReturnDiagnostic, 622 If->getSourceRange(), Replacement); 623 } 624 625 void SimplifyBooleanExprCheck::replaceCompoundReturnWithCondition( 626 const MatchFinder::MatchResult &Result, const CompoundStmt *Compound, 627 bool Negated) { 628 const auto *Ret = Result.Nodes.getNodeAs<ReturnStmt>(CompoundReturnId); 629 630 // The body shouldn't be empty because the matcher ensures that it must 631 // contain at least two statements: 632 // 1) A `return` statement returning a boolean literal `false` or `true` 633 // 2) An `if` statement with no `else` clause that consists of a single 634 // `return` statement returning the opposite boolean literal `true` or 635 // `false`. 636 assert(Compound->size() >= 2); 637 const IfStmt *BeforeIf = nullptr; 638 CompoundStmt::const_body_iterator Current = Compound->body_begin(); 639 CompoundStmt::const_body_iterator After = Compound->body_begin(); 640 for (++After; After != Compound->body_end() && *Current != Ret; 641 ++Current, ++After) { 642 if (const auto *If = dyn_cast<IfStmt>(*Current)) { 643 if (const Expr *Lit = stmtReturnsBool(If, Negated)) { 644 if (*After == Ret) { 645 if (!ChainedConditionalReturn && BeforeIf) 646 continue; 647 648 const Expr *Condition = If->getCond(); 649 std::string Replacement = 650 "return " + replacementExpression(Result, Negated, Condition); 651 issueDiag( 652 Result, Lit->getBeginLoc(), SimplifyConditionalReturnDiagnostic, 653 SourceRange(If->getBeginLoc(), Ret->getEndLoc()), Replacement); 654 return; 655 } 656 657 BeforeIf = If; 658 } 659 } else { 660 BeforeIf = nullptr; 661 } 662 } 663 } 664 665 void SimplifyBooleanExprCheck::replaceWithAssignment( 666 const MatchFinder::MatchResult &Result, const IfStmt *IfAssign, 667 bool Negated) { 668 SourceRange Range = IfAssign->getSourceRange(); 669 StringRef VariableName = 670 getText(Result, *Result.Nodes.getNodeAs<Expr>(IfAssignVariableId)); 671 StringRef Terminator = isa<CompoundStmt>(IfAssign->getElse()) ? ";" : ""; 672 std::string Condition = 673 replacementExpression(Result, Negated, IfAssign->getCond()); 674 std::string Replacement = 675 (VariableName + " = " + Condition + Terminator).str(); 676 SourceLocation Location = 677 Result.Nodes.getNodeAs<CXXBoolLiteralExpr>(IfAssignLocId)->getBeginLoc(); 678 issueDiag(Result, Location, 679 "redundant boolean literal in conditional assignment", Range, 680 Replacement); 681 } 682 683 } // namespace readability 684 } // namespace tidy 685 } // namespace clang 686