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(unless(isInTemplateInstantiation()), 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(unless(isInTemplateInstantiation()), 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(unless(isInTemplateInstantiation()), 446 hasThen(returnsBool(Value, ThenLiteralId)), 447 hasElse(returnsBool(!Value))) 448 .bind(Id), 449 this); 450 else 451 Finder->addMatcher(ifStmt(unless(isInTemplateInstantiation()), 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(unless(isInTemplateInstantiation()), 478 hasThen(Then), hasElse(Else)) 479 .bind(Id), 480 this); 481 else 482 Finder->addMatcher(ifStmt(unless(isInTemplateInstantiation()), 483 unless(hasParent(ifStmt())), hasThen(Then), 484 hasElse(Else)) 485 .bind(Id), 486 this); 487 } 488 489 void SimplifyBooleanExprCheck::matchCompoundIfReturnsBool(MatchFinder *Finder, 490 bool Value, 491 StringRef Id) { 492 Finder->addMatcher( 493 compoundStmt( 494 unless(isInTemplateInstantiation()), 495 hasAnySubstatement( 496 ifStmt(hasThen(returnsBool(Value)), unless(hasElse(stmt())))), 497 hasAnySubstatement(returnStmt(has(ignoringParenImpCasts( 498 cxxBoolLiteral(equals(!Value))))) 499 .bind(CompoundReturnId))) 500 .bind(Id), 501 this); 502 } 503 504 void SimplifyBooleanExprCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { 505 Options.store(Opts, "ChainedConditionalReturn", ChainedConditionalReturn); 506 Options.store(Opts, "ChainedConditionalAssignment", 507 ChainedConditionalAssignment); 508 } 509 510 void SimplifyBooleanExprCheck::registerMatchers(MatchFinder *Finder) { 511 Finder->addMatcher(translationUnitDecl().bind("top"), this); 512 513 matchBoolCondition(Finder, true, ConditionThenStmtId); 514 matchBoolCondition(Finder, false, ConditionElseStmtId); 515 516 matchTernaryResult(Finder, true, TernaryId); 517 matchTernaryResult(Finder, false, TernaryNegatedId); 518 519 matchIfReturnsBool(Finder, true, IfReturnsBoolId); 520 matchIfReturnsBool(Finder, false, IfReturnsNotBoolId); 521 522 matchIfAssignsBool(Finder, true, IfAssignBoolId); 523 matchIfAssignsBool(Finder, false, IfAssignNotBoolId); 524 525 matchCompoundIfReturnsBool(Finder, true, CompoundBoolId); 526 matchCompoundIfReturnsBool(Finder, false, CompoundNotBoolId); 527 } 528 529 void SimplifyBooleanExprCheck::check(const MatchFinder::MatchResult &Result) { 530 if (Result.Nodes.getNodeAs<TranslationUnitDecl>("top")) 531 Visitor(this, Result).TraverseAST(*Result.Context); 532 else if (const CXXBoolLiteralExpr *TrueConditionRemoved = 533 getBoolLiteral(Result, ConditionThenStmtId)) 534 replaceWithThenStatement(Result, TrueConditionRemoved); 535 else if (const CXXBoolLiteralExpr *FalseConditionRemoved = 536 getBoolLiteral(Result, ConditionElseStmtId)) 537 replaceWithElseStatement(Result, FalseConditionRemoved); 538 else if (const auto *Ternary = 539 Result.Nodes.getNodeAs<ConditionalOperator>(TernaryId)) 540 replaceWithCondition(Result, Ternary); 541 else if (const auto *TernaryNegated = 542 Result.Nodes.getNodeAs<ConditionalOperator>(TernaryNegatedId)) 543 replaceWithCondition(Result, TernaryNegated, true); 544 else if (const auto *If = Result.Nodes.getNodeAs<IfStmt>(IfReturnsBoolId)) 545 replaceWithReturnCondition(Result, If); 546 else if (const auto *IfNot = 547 Result.Nodes.getNodeAs<IfStmt>(IfReturnsNotBoolId)) 548 replaceWithReturnCondition(Result, IfNot, true); 549 else if (const auto *IfAssign = 550 Result.Nodes.getNodeAs<IfStmt>(IfAssignBoolId)) 551 replaceWithAssignment(Result, IfAssign); 552 else if (const auto *IfAssignNot = 553 Result.Nodes.getNodeAs<IfStmt>(IfAssignNotBoolId)) 554 replaceWithAssignment(Result, IfAssignNot, true); 555 else if (const auto *Compound = 556 Result.Nodes.getNodeAs<CompoundStmt>(CompoundBoolId)) 557 replaceCompoundReturnWithCondition(Result, Compound); 558 else if (const auto *Compound = 559 Result.Nodes.getNodeAs<CompoundStmt>(CompoundNotBoolId)) 560 replaceCompoundReturnWithCondition(Result, Compound, true); 561 } 562 563 void SimplifyBooleanExprCheck::issueDiag( 564 const ast_matchers::MatchFinder::MatchResult &Result, SourceLocation Loc, 565 StringRef Description, SourceRange ReplacementRange, 566 StringRef Replacement) { 567 CharSourceRange CharRange = 568 Lexer::makeFileCharRange(CharSourceRange::getTokenRange(ReplacementRange), 569 *Result.SourceManager, getLangOpts()); 570 571 DiagnosticBuilder Diag = diag(Loc, Description); 572 if (!containsDiscardedTokens(Result, CharRange)) 573 Diag << FixItHint::CreateReplacement(CharRange, Replacement); 574 } 575 576 void SimplifyBooleanExprCheck::replaceWithThenStatement( 577 const MatchFinder::MatchResult &Result, 578 const CXXBoolLiteralExpr *TrueConditionRemoved) { 579 const auto *IfStatement = Result.Nodes.getNodeAs<IfStmt>(IfStmtId); 580 issueDiag(Result, TrueConditionRemoved->getBeginLoc(), 581 SimplifyConditionDiagnostic, IfStatement->getSourceRange(), 582 getText(Result, *IfStatement->getThen())); 583 } 584 585 void SimplifyBooleanExprCheck::replaceWithElseStatement( 586 const MatchFinder::MatchResult &Result, 587 const CXXBoolLiteralExpr *FalseConditionRemoved) { 588 const auto *IfStatement = Result.Nodes.getNodeAs<IfStmt>(IfStmtId); 589 const Stmt *ElseStatement = IfStatement->getElse(); 590 issueDiag(Result, FalseConditionRemoved->getBeginLoc(), 591 SimplifyConditionDiagnostic, IfStatement->getSourceRange(), 592 ElseStatement ? getText(Result, *ElseStatement) : ""); 593 } 594 595 void SimplifyBooleanExprCheck::replaceWithCondition( 596 const MatchFinder::MatchResult &Result, const ConditionalOperator *Ternary, 597 bool Negated) { 598 std::string Replacement = 599 replacementExpression(Result, Negated, Ternary->getCond()); 600 issueDiag(Result, Ternary->getTrueExpr()->getBeginLoc(), 601 "redundant boolean literal in ternary expression result", 602 Ternary->getSourceRange(), Replacement); 603 } 604 605 void SimplifyBooleanExprCheck::replaceWithReturnCondition( 606 const MatchFinder::MatchResult &Result, const IfStmt *If, bool Negated) { 607 StringRef Terminator = isa<CompoundStmt>(If->getElse()) ? ";" : ""; 608 std::string Condition = replacementExpression(Result, Negated, If->getCond()); 609 std::string Replacement = ("return " + Condition + Terminator).str(); 610 SourceLocation Start = 611 Result.Nodes.getNodeAs<CXXBoolLiteralExpr>(ThenLiteralId)->getBeginLoc(); 612 issueDiag(Result, Start, SimplifyConditionalReturnDiagnostic, 613 If->getSourceRange(), Replacement); 614 } 615 616 void SimplifyBooleanExprCheck::replaceCompoundReturnWithCondition( 617 const MatchFinder::MatchResult &Result, const CompoundStmt *Compound, 618 bool Negated) { 619 const auto *Ret = Result.Nodes.getNodeAs<ReturnStmt>(CompoundReturnId); 620 621 // The body shouldn't be empty because the matcher ensures that it must 622 // contain at least two statements: 623 // 1) A `return` statement returning a boolean literal `false` or `true` 624 // 2) An `if` statement with no `else` clause that consists of a single 625 // `return` statement returning the opposite boolean literal `true` or 626 // `false`. 627 assert(Compound->size() >= 2); 628 const IfStmt *BeforeIf = nullptr; 629 CompoundStmt::const_body_iterator Current = Compound->body_begin(); 630 CompoundStmt::const_body_iterator After = Compound->body_begin(); 631 for (++After; After != Compound->body_end() && *Current != Ret; 632 ++Current, ++After) { 633 if (const auto *If = dyn_cast<IfStmt>(*Current)) { 634 if (const CXXBoolLiteralExpr *Lit = stmtReturnsBool(If, Negated)) { 635 if (*After == Ret) { 636 if (!ChainedConditionalReturn && BeforeIf) 637 continue; 638 639 const Expr *Condition = If->getCond(); 640 std::string Replacement = 641 "return " + replacementExpression(Result, Negated, Condition); 642 issueDiag( 643 Result, Lit->getBeginLoc(), SimplifyConditionalReturnDiagnostic, 644 SourceRange(If->getBeginLoc(), Ret->getEndLoc()), Replacement); 645 return; 646 } 647 648 BeforeIf = If; 649 } 650 } else { 651 BeforeIf = nullptr; 652 } 653 } 654 } 655 656 void SimplifyBooleanExprCheck::replaceWithAssignment( 657 const MatchFinder::MatchResult &Result, const IfStmt *IfAssign, 658 bool Negated) { 659 SourceRange Range = IfAssign->getSourceRange(); 660 StringRef VariableName = 661 getText(Result, *Result.Nodes.getNodeAs<Expr>(IfAssignVariableId)); 662 StringRef Terminator = isa<CompoundStmt>(IfAssign->getElse()) ? ";" : ""; 663 std::string Condition = 664 replacementExpression(Result, Negated, IfAssign->getCond()); 665 std::string Replacement = 666 (VariableName + " = " + Condition + Terminator).str(); 667 SourceLocation Location = 668 Result.Nodes.getNodeAs<CXXBoolLiteralExpr>(IfAssignLocId)->getBeginLoc(); 669 issueDiag(Result, Location, 670 "redundant boolean literal in conditional assignment", Range, 671 Replacement); 672 } 673 674 } // namespace readability 675 } // namespace tidy 676 } // namespace clang 677