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 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 337 SimplifyBooleanExprCheck::SimplifyBooleanExprCheck(StringRef Name, 338 ClangTidyContext *Context) 339 : ClangTidyCheck(Name, Context), 340 ChainedConditionalReturn(Options.get("ChainedConditionalReturn", 0U)), 341 ChainedConditionalAssignment( 342 Options.get("ChainedConditionalAssignment", 0U)) {} 343 344 bool containsBoolLiteral(const Expr *E) { 345 if (!E) 346 return false; 347 E = E->IgnoreParenImpCasts(); 348 if (isa<CXXBoolLiteralExpr>(E)) 349 return true; 350 if (const auto *BinOp = dyn_cast<BinaryOperator>(E)) 351 return containsBoolLiteral(BinOp->getLHS()) || 352 containsBoolLiteral(BinOp->getRHS()); 353 if (const auto *UnaryOp = dyn_cast<UnaryOperator>(E)) 354 return containsBoolLiteral(UnaryOp->getSubExpr()); 355 return false; 356 } 357 358 void SimplifyBooleanExprCheck::reportBinOp( 359 const MatchFinder::MatchResult &Result, const BinaryOperator *Op) { 360 const auto *LHS = Op->getLHS()->IgnoreParenImpCasts(); 361 const auto *RHS = Op->getRHS()->IgnoreParenImpCasts(); 362 363 const CXXBoolLiteralExpr *Bool; 364 const Expr *Other = nullptr; 365 if ((Bool = dyn_cast<CXXBoolLiteralExpr>(LHS))) 366 Other = RHS; 367 else if ((Bool = dyn_cast<CXXBoolLiteralExpr>(RHS))) 368 Other = LHS; 369 else 370 return; 371 372 if (Bool->getBeginLoc().isMacroID()) 373 return; 374 375 // FIXME: why do we need this? 376 if (!isa<CXXBoolLiteralExpr>(Other) && containsBoolLiteral(Other)) 377 return; 378 379 bool BoolValue = Bool->getValue(); 380 381 auto replaceWithExpression = [this, &Result, LHS, RHS, Bool]( 382 const Expr *ReplaceWith, bool Negated) { 383 std::string Replacement = 384 replacementExpression(Result, Negated, ReplaceWith); 385 SourceRange Range(LHS->getBeginLoc(), RHS->getEndLoc()); 386 issueDiag(Result, Bool->getBeginLoc(), SimplifyOperatorDiagnostic, Range, 387 Replacement); 388 }; 389 390 switch (Op->getOpcode()) { 391 case BO_LAnd: 392 if (BoolValue) { 393 // expr && true -> expr 394 replaceWithExpression(Other, /*Negated=*/false); 395 } else { 396 // expr && false -> false 397 replaceWithExpression(Bool, /*Negated=*/false); 398 } 399 break; 400 case BO_LOr: 401 if (BoolValue) { 402 // expr || true -> true 403 replaceWithExpression(Bool, /*Negated=*/false); 404 } else { 405 // expr || false -> expr 406 replaceWithExpression(Other, /*Negated=*/false); 407 } 408 break; 409 case BO_EQ: 410 // expr == true -> expr, expr == false -> !expr 411 replaceWithExpression(Other, /*Negated=*/!BoolValue); 412 break; 413 case BO_NE: 414 // expr != true -> !expr, expr != false -> expr 415 replaceWithExpression(Other, /*Negated=*/BoolValue); 416 break; 417 default: 418 break; 419 } 420 } 421 422 void SimplifyBooleanExprCheck::matchBoolCondition(MatchFinder *Finder, 423 bool Value, 424 StringRef BooleanId) { 425 Finder->addMatcher( 426 ifStmt(isExpansionInMainFile(), 427 hasCondition(cxxBoolLiteral(equals(Value)).bind(BooleanId))) 428 .bind(IfStmtId), 429 this); 430 } 431 432 void SimplifyBooleanExprCheck::matchTernaryResult(MatchFinder *Finder, 433 bool Value, 434 StringRef TernaryId) { 435 Finder->addMatcher( 436 conditionalOperator(isExpansionInMainFile(), 437 hasTrueExpression(cxxBoolLiteral(equals(Value))), 438 hasFalseExpression(cxxBoolLiteral(equals(!Value)))) 439 .bind(TernaryId), 440 this); 441 } 442 443 void SimplifyBooleanExprCheck::matchIfReturnsBool(MatchFinder *Finder, 444 bool Value, StringRef Id) { 445 if (ChainedConditionalReturn) 446 Finder->addMatcher(ifStmt(isExpansionInMainFile(), 447 hasThen(returnsBool(Value, ThenLiteralId)), 448 hasElse(returnsBool(!Value))) 449 .bind(Id), 450 this); 451 else 452 Finder->addMatcher(ifStmt(isExpansionInMainFile(), 453 unless(hasParent(ifStmt())), 454 hasThen(returnsBool(Value, ThenLiteralId)), 455 hasElse(returnsBool(!Value))) 456 .bind(Id), 457 this); 458 } 459 460 void SimplifyBooleanExprCheck::matchIfAssignsBool(MatchFinder *Finder, 461 bool Value, StringRef Id) { 462 auto VarAssign = declRefExpr(hasDeclaration(decl().bind(IfAssignVarId))); 463 auto VarRef = declRefExpr(hasDeclaration(equalsBoundNode(IfAssignVarId))); 464 auto MemAssign = memberExpr(hasDeclaration(decl().bind(IfAssignVarId))); 465 auto MemRef = memberExpr(hasDeclaration(equalsBoundNode(IfAssignVarId))); 466 auto SimpleThen = 467 binaryOperator(hasOperatorName("="), hasLHS(anyOf(VarAssign, MemAssign)), 468 hasLHS(expr().bind(IfAssignVariableId)), 469 hasRHS(cxxBoolLiteral(equals(Value)).bind(IfAssignLocId))); 470 auto Then = anyOf(SimpleThen, compoundStmt(statementCountIs(1), 471 hasAnySubstatement(SimpleThen))); 472 auto SimpleElse = 473 binaryOperator(hasOperatorName("="), hasLHS(anyOf(VarRef, MemRef)), 474 hasRHS(cxxBoolLiteral(equals(!Value)))); 475 auto Else = anyOf(SimpleElse, compoundStmt(statementCountIs(1), 476 hasAnySubstatement(SimpleElse))); 477 if (ChainedConditionalAssignment) 478 Finder->addMatcher(ifStmt(hasThen(Then), hasElse(Else)).bind(Id), this); 479 else 480 Finder->addMatcher( 481 ifStmt(unless(hasParent(ifStmt())), hasThen(Then), hasElse(Else)) 482 .bind(Id), 483 this); 484 } 485 486 void SimplifyBooleanExprCheck::matchCompoundIfReturnsBool(MatchFinder *Finder, 487 bool Value, 488 StringRef Id) { 489 Finder->addMatcher( 490 compoundStmt( 491 hasAnySubstatement( 492 ifStmt(hasThen(returnsBool(Value)), unless(hasElse(stmt())))), 493 hasAnySubstatement(returnStmt(has(ignoringParenImpCasts( 494 cxxBoolLiteral(equals(!Value))))) 495 .bind(CompoundReturnId))) 496 .bind(Id), 497 this); 498 } 499 500 void SimplifyBooleanExprCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { 501 Options.store(Opts, "ChainedConditionalReturn", ChainedConditionalReturn); 502 Options.store(Opts, "ChainedConditionalAssignment", 503 ChainedConditionalAssignment); 504 } 505 506 void SimplifyBooleanExprCheck::registerMatchers(MatchFinder *Finder) { 507 Finder->addMatcher(translationUnitDecl().bind("top"), this); 508 509 matchBoolCondition(Finder, true, ConditionThenStmtId); 510 matchBoolCondition(Finder, false, ConditionElseStmtId); 511 512 matchTernaryResult(Finder, true, TernaryId); 513 matchTernaryResult(Finder, false, TernaryNegatedId); 514 515 matchIfReturnsBool(Finder, true, IfReturnsBoolId); 516 matchIfReturnsBool(Finder, false, IfReturnsNotBoolId); 517 518 matchIfAssignsBool(Finder, true, IfAssignBoolId); 519 matchIfAssignsBool(Finder, false, IfAssignNotBoolId); 520 521 matchCompoundIfReturnsBool(Finder, true, CompoundBoolId); 522 matchCompoundIfReturnsBool(Finder, false, CompoundNotBoolId); 523 } 524 525 void SimplifyBooleanExprCheck::check(const MatchFinder::MatchResult &Result) { 526 if (Result.Nodes.getNodeAs<TranslationUnitDecl>("top")) 527 Visitor(this, Result).TraverseAST(*Result.Context); 528 else if (const CXXBoolLiteralExpr *TrueConditionRemoved = 529 getBoolLiteral(Result, ConditionThenStmtId)) 530 replaceWithThenStatement(Result, TrueConditionRemoved); 531 else if (const CXXBoolLiteralExpr *FalseConditionRemoved = 532 getBoolLiteral(Result, ConditionElseStmtId)) 533 replaceWithElseStatement(Result, FalseConditionRemoved); 534 else if (const auto *Ternary = 535 Result.Nodes.getNodeAs<ConditionalOperator>(TernaryId)) 536 replaceWithCondition(Result, Ternary); 537 else if (const auto *TernaryNegated = 538 Result.Nodes.getNodeAs<ConditionalOperator>(TernaryNegatedId)) 539 replaceWithCondition(Result, TernaryNegated, true); 540 else if (const auto *If = Result.Nodes.getNodeAs<IfStmt>(IfReturnsBoolId)) 541 replaceWithReturnCondition(Result, If); 542 else if (const auto *IfNot = 543 Result.Nodes.getNodeAs<IfStmt>(IfReturnsNotBoolId)) 544 replaceWithReturnCondition(Result, IfNot, true); 545 else if (const auto *IfAssign = 546 Result.Nodes.getNodeAs<IfStmt>(IfAssignBoolId)) 547 replaceWithAssignment(Result, IfAssign); 548 else if (const auto *IfAssignNot = 549 Result.Nodes.getNodeAs<IfStmt>(IfAssignNotBoolId)) 550 replaceWithAssignment(Result, IfAssignNot, true); 551 else if (const auto *Compound = 552 Result.Nodes.getNodeAs<CompoundStmt>(CompoundBoolId)) 553 replaceCompoundReturnWithCondition(Result, Compound); 554 else if (const auto *Compound = 555 Result.Nodes.getNodeAs<CompoundStmt>(CompoundNotBoolId)) 556 replaceCompoundReturnWithCondition(Result, Compound, true); 557 } 558 559 void SimplifyBooleanExprCheck::issueDiag( 560 const ast_matchers::MatchFinder::MatchResult &Result, SourceLocation Loc, 561 StringRef Description, SourceRange ReplacementRange, 562 StringRef Replacement) { 563 CharSourceRange CharRange = 564 Lexer::makeFileCharRange(CharSourceRange::getTokenRange(ReplacementRange), 565 *Result.SourceManager, getLangOpts()); 566 567 DiagnosticBuilder Diag = diag(Loc, Description); 568 if (!containsDiscardedTokens(Result, CharRange)) 569 Diag << FixItHint::CreateReplacement(CharRange, Replacement); 570 } 571 572 void SimplifyBooleanExprCheck::replaceWithThenStatement( 573 const MatchFinder::MatchResult &Result, 574 const CXXBoolLiteralExpr *TrueConditionRemoved) { 575 const auto *IfStatement = Result.Nodes.getNodeAs<IfStmt>(IfStmtId); 576 issueDiag(Result, TrueConditionRemoved->getBeginLoc(), 577 SimplifyConditionDiagnostic, IfStatement->getSourceRange(), 578 getText(Result, *IfStatement->getThen())); 579 } 580 581 void SimplifyBooleanExprCheck::replaceWithElseStatement( 582 const MatchFinder::MatchResult &Result, 583 const CXXBoolLiteralExpr *FalseConditionRemoved) { 584 const auto *IfStatement = Result.Nodes.getNodeAs<IfStmt>(IfStmtId); 585 const Stmt *ElseStatement = IfStatement->getElse(); 586 issueDiag(Result, FalseConditionRemoved->getBeginLoc(), 587 SimplifyConditionDiagnostic, IfStatement->getSourceRange(), 588 ElseStatement ? getText(Result, *ElseStatement) : ""); 589 } 590 591 void SimplifyBooleanExprCheck::replaceWithCondition( 592 const MatchFinder::MatchResult &Result, const ConditionalOperator *Ternary, 593 bool Negated) { 594 std::string Replacement = 595 replacementExpression(Result, Negated, Ternary->getCond()); 596 issueDiag(Result, Ternary->getTrueExpr()->getBeginLoc(), 597 "redundant boolean literal in ternary expression result", 598 Ternary->getSourceRange(), Replacement); 599 } 600 601 void SimplifyBooleanExprCheck::replaceWithReturnCondition( 602 const MatchFinder::MatchResult &Result, const IfStmt *If, bool Negated) { 603 StringRef Terminator = isa<CompoundStmt>(If->getElse()) ? ";" : ""; 604 std::string Condition = replacementExpression(Result, Negated, If->getCond()); 605 std::string Replacement = ("return " + Condition + Terminator).str(); 606 SourceLocation Start = 607 Result.Nodes.getNodeAs<CXXBoolLiteralExpr>(ThenLiteralId)->getBeginLoc(); 608 issueDiag(Result, Start, SimplifyConditionalReturnDiagnostic, 609 If->getSourceRange(), Replacement); 610 } 611 612 void SimplifyBooleanExprCheck::replaceCompoundReturnWithCondition( 613 const MatchFinder::MatchResult &Result, const CompoundStmt *Compound, 614 bool Negated) { 615 const auto *Ret = Result.Nodes.getNodeAs<ReturnStmt>(CompoundReturnId); 616 617 // The body shouldn't be empty because the matcher ensures that it must 618 // contain at least two statements: 619 // 1) A `return` statement returning a boolean literal `false` or `true` 620 // 2) An `if` statement with no `else` clause that consists of a single 621 // `return` statement returning the opposite boolean literal `true` or 622 // `false`. 623 assert(Compound->size() >= 2); 624 const IfStmt *BeforeIf = nullptr; 625 CompoundStmt::const_body_iterator Current = Compound->body_begin(); 626 CompoundStmt::const_body_iterator After = Compound->body_begin(); 627 for (++After; After != Compound->body_end() && *Current != Ret; 628 ++Current, ++After) { 629 if (const auto *If = dyn_cast<IfStmt>(*Current)) { 630 if (const CXXBoolLiteralExpr *Lit = stmtReturnsBool(If, Negated)) { 631 if (*After == Ret) { 632 if (!ChainedConditionalReturn && BeforeIf) 633 continue; 634 635 const Expr *Condition = If->getCond(); 636 std::string Replacement = 637 "return " + replacementExpression(Result, Negated, Condition); 638 issueDiag( 639 Result, Lit->getBeginLoc(), SimplifyConditionalReturnDiagnostic, 640 SourceRange(If->getBeginLoc(), Ret->getEndLoc()), Replacement); 641 return; 642 } 643 644 BeforeIf = If; 645 } 646 } else { 647 BeforeIf = nullptr; 648 } 649 } 650 } 651 652 void SimplifyBooleanExprCheck::replaceWithAssignment( 653 const MatchFinder::MatchResult &Result, const IfStmt *IfAssign, 654 bool Negated) { 655 SourceRange Range = IfAssign->getSourceRange(); 656 StringRef VariableName = 657 getText(Result, *Result.Nodes.getNodeAs<Expr>(IfAssignVariableId)); 658 StringRef Terminator = isa<CompoundStmt>(IfAssign->getElse()) ? ";" : ""; 659 std::string Condition = 660 replacementExpression(Result, Negated, IfAssign->getCond()); 661 std::string Replacement = 662 (VariableName + " = " + Condition + Terminator).str(); 663 SourceLocation Location = 664 Result.Nodes.getNodeAs<CXXBoolLiteralExpr>(IfAssignLocId)->getBeginLoc(); 665 issueDiag(Result, Location, 666 "redundant boolean literal in conditional assignment", Range, 667 Replacement); 668 } 669 670 } // namespace readability 671 } // namespace tidy 672 } // namespace clang 673