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