1 //===--- SimplifyBooleanExpr.cpp clang-tidy ---------------------*- C++ -*-===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 10 #include "SimplifyBooleanExprCheck.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 RightExpressionId[] = "bool-op-expr-yields-expr"; 37 const char LeftExpressionId[] = "expr-op-bool-yields-expr"; 38 const char NegatedRightExpressionId[] = "bool-op-expr-yields-not-expr"; 39 const char NegatedLeftExpressionId[] = "expr-op-bool-yields-not-expr"; 40 const char ConditionThenStmtId[] = "if-bool-yields-then"; 41 const char ConditionElseStmtId[] = "if-bool-yields-else"; 42 const char TernaryId[] = "ternary-bool-yields-condition"; 43 const char TernaryNegatedId[] = "ternary-bool-yields-not-condition"; 44 const char IfReturnsBoolId[] = "if-return"; 45 const char IfReturnsNotBoolId[] = "if-not-return"; 46 const char ThenLiteralId[] = "then-literal"; 47 const char IfAssignVariableId[] = "if-assign-lvalue"; 48 const char IfAssignLocId[] = "if-assign-loc"; 49 const char IfAssignBoolId[] = "if-assign"; 50 const char IfAssignNotBoolId[] = "if-assign-not"; 51 const char IfAssignObjId[] = "if-assign-obj"; 52 const char CompoundReturnId[] = "compound-return"; 53 const char CompoundBoolId[] = "compound-bool"; 54 const char CompoundNotBoolId[] = "compound-bool-not"; 55 56 const char IfStmtId[] = "if"; 57 const char LHSId[] = "lhs-expr"; 58 const char RHSId[] = "rhs-expr"; 59 60 const char SimplifyOperatorDiagnostic[] = 61 "redundant boolean literal supplied to boolean operator"; 62 const char SimplifyConditionDiagnostic[] = 63 "redundant boolean literal in if statement condition"; 64 const char SimplifyConditionalReturnDiagnostic[] = 65 "redundant boolean literal in conditional return statement"; 66 67 const CXXBoolLiteralExpr *getBoolLiteral(const MatchFinder::MatchResult &Result, 68 StringRef Id) { 69 const auto *Literal = Result.Nodes.getNodeAs<CXXBoolLiteralExpr>(Id); 70 return (Literal && 71 Result.SourceManager->isMacroBodyExpansion(Literal->getLocStart())) 72 ? nullptr 73 : Literal; 74 } 75 76 internal::Matcher<Stmt> returnsBool(bool Value, StringRef Id = "ignored") { 77 auto SimpleReturnsBool = 78 returnStmt(has(cxxBoolLiteral(equals(Value)).bind(Id))) 79 .bind("returns-bool"); 80 return anyOf(SimpleReturnsBool, 81 compoundStmt(statementCountIs(1), has(SimpleReturnsBool))); 82 } 83 84 bool needsParensAfterUnaryNegation(const Expr *E) { 85 E = E->IgnoreImpCasts(); 86 if (isa<BinaryOperator>(E) || isa<ConditionalOperator>(E)) 87 return true; 88 if (const auto *Op = dyn_cast<CXXOperatorCallExpr>(E)) { 89 return Op->getNumArgs() == 2 && Op->getOperator() != OO_Call && 90 Op->getOperator() != OO_Subscript; 91 } 92 return false; 93 } 94 95 std::pair<BinaryOperatorKind, BinaryOperatorKind> Opposites[] = { 96 {BO_LT, BO_GE}, {BO_GT, BO_LE}, {BO_EQ, BO_NE}}; 97 98 StringRef negatedOperator(const BinaryOperator *BinOp) { 99 const BinaryOperatorKind Opcode = BinOp->getOpcode(); 100 for (auto NegatableOp : Opposites) { 101 if (Opcode == NegatableOp.first) 102 return BinOp->getOpcodeStr(NegatableOp.second); 103 if (Opcode == NegatableOp.second) 104 return BinOp->getOpcodeStr(NegatableOp.first); 105 } 106 return StringRef(); 107 } 108 109 std::pair<OverloadedOperatorKind, StringRef> OperatorNames[] = { 110 {OO_EqualEqual, "=="}, 111 {OO_ExclaimEqual, "!="}, 112 {OO_Less, "<"}, 113 {OO_GreaterEqual, ">="}, 114 {OO_Greater, ">"}, 115 {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 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 153 return false; 154 } 155 156 bool needsStaticCast(const Expr *E) { 157 if (const auto *ImpCast = dyn_cast<ImplicitCastExpr>(E)) { 158 if (ImpCast->getCastKind() == CK_UserDefinedConversion && 159 ImpCast->getSubExpr()->getType()->isBooleanType()) { 160 if (const auto *MemCall = 161 dyn_cast<CXXMemberCallExpr>(ImpCast->getSubExpr())) { 162 if (const auto *MemDecl = 163 dyn_cast<CXXConversionDecl>(MemCall->getMethodDecl())) { 164 if (MemDecl->isExplicit()) 165 return true; 166 } 167 } 168 } 169 } 170 171 E = E->IgnoreImpCasts(); 172 return !E->getType()->isBooleanType(); 173 } 174 175 std::string replacementExpression(const MatchFinder::MatchResult &Result, 176 bool Negated, const Expr *E) { 177 E = E->ignoreParenBaseCasts(); 178 const bool NeedsStaticCast = needsStaticCast(E); 179 if (Negated) { 180 if (const auto *UnOp = dyn_cast<UnaryOperator>(E)) { 181 if (UnOp->getOpcode() == UO_LNot) { 182 if (needsNullPtrComparison(UnOp->getSubExpr())) 183 return (getText(Result, *UnOp->getSubExpr()) + " != nullptr").str(); 184 185 return replacementExpression(Result, false, UnOp->getSubExpr()); 186 } 187 } 188 189 if (needsNullPtrComparison(E)) 190 return (getText(Result, *E) + " == nullptr").str(); 191 192 StringRef NegatedOperator; 193 const Expr *LHS = nullptr; 194 const Expr *RHS = nullptr; 195 if (const auto *BinOp = dyn_cast<BinaryOperator>(E)) { 196 NegatedOperator = negatedOperator(BinOp); 197 LHS = BinOp->getLHS(); 198 RHS = BinOp->getRHS(); 199 } else if (const auto *OpExpr = dyn_cast<CXXOperatorCallExpr>(E)) { 200 if (OpExpr->getNumArgs() == 2) { 201 NegatedOperator = negatedOperator(OpExpr); 202 LHS = OpExpr->getArg(0); 203 RHS = OpExpr->getArg(1); 204 } 205 } 206 if (!NegatedOperator.empty() && LHS && RHS) { 207 return (asBool((getText(Result, *LHS) + " " + NegatedOperator + " " + 208 getText(Result, *RHS)).str(), 209 NeedsStaticCast)); 210 } 211 212 StringRef Text = getText(Result, *E); 213 if (!NeedsStaticCast && needsParensAfterUnaryNegation(E)) 214 return ("!(" + Text + ")").str(); 215 216 if (needsNullPtrComparison(E)) 217 return (getText(Result, *E) + " == nullptr").str(); 218 219 return ("!" + asBool(Text, NeedsStaticCast)); 220 } 221 222 if (const auto *UnOp = dyn_cast<UnaryOperator>(E)) { 223 if (UnOp->getOpcode() == UO_LNot) { 224 if (needsNullPtrComparison(UnOp->getSubExpr())) 225 return (getText(Result, *UnOp->getSubExpr()) + " == nullptr").str(); 226 } 227 } 228 229 if (needsNullPtrComparison(E)) 230 return (getText(Result, *E) + " != nullptr").str(); 231 232 return asBool(getText(Result, *E), NeedsStaticCast); 233 } 234 235 const CXXBoolLiteralExpr *stmtReturnsBool(const ReturnStmt *Ret, bool Negated) { 236 if (const auto *Bool = dyn_cast<CXXBoolLiteralExpr>(Ret->getRetValue())) { 237 if (Bool->getValue() == !Negated) 238 return Bool; 239 } 240 241 return nullptr; 242 } 243 244 const CXXBoolLiteralExpr *stmtReturnsBool(const IfStmt *IfRet, bool Negated) { 245 if (IfRet->getElse() != nullptr) 246 return nullptr; 247 248 if (const auto *Ret = dyn_cast<ReturnStmt>(IfRet->getThen())) 249 return stmtReturnsBool(Ret, Negated); 250 251 if (const auto *Compound = dyn_cast<CompoundStmt>(IfRet->getThen())) { 252 if (Compound->size() == 1) { 253 if (const auto *CompoundRet = dyn_cast<ReturnStmt>(Compound->body_back())) 254 return stmtReturnsBool(CompoundRet, Negated); 255 } 256 } 257 258 return nullptr; 259 } 260 261 } // namespace 262 263 SimplifyBooleanExprCheck::SimplifyBooleanExprCheck(StringRef Name, 264 ClangTidyContext *Context) 265 : ClangTidyCheck(Name, Context), 266 ChainedConditionalReturn(Options.get("ChainedConditionalReturn", 0U)), 267 ChainedConditionalAssignment( 268 Options.get("ChainedConditionalAssignment", 0U)) {} 269 270 void SimplifyBooleanExprCheck::matchBoolBinOpExpr(MatchFinder *Finder, 271 bool Value, 272 StringRef OperatorName, 273 StringRef BooleanId) { 274 Finder->addMatcher( 275 binaryOperator( 276 isExpansionInMainFile(), hasOperatorName(OperatorName), 277 hasLHS(allOf(expr().bind(LHSId), 278 cxxBoolLiteral(equals(Value)).bind(BooleanId))), 279 hasRHS(expr().bind(RHSId)), 280 unless(hasRHS(hasDescendant(cxxBoolLiteral())))), 281 this); 282 } 283 284 void SimplifyBooleanExprCheck::matchExprBinOpBool(MatchFinder *Finder, 285 bool Value, 286 StringRef OperatorName, 287 StringRef BooleanId) { 288 Finder->addMatcher( 289 binaryOperator( 290 isExpansionInMainFile(), hasOperatorName(OperatorName), 291 hasLHS(expr().bind(LHSId)), 292 unless( 293 hasLHS(anyOf(cxxBoolLiteral(), hasDescendant(cxxBoolLiteral())))), 294 hasRHS(allOf(expr().bind(RHSId), 295 cxxBoolLiteral(equals(Value)).bind(BooleanId)))), 296 this); 297 } 298 299 void SimplifyBooleanExprCheck::matchBoolCompOpExpr(MatchFinder *Finder, 300 bool Value, 301 StringRef OperatorName, 302 StringRef BooleanId) { 303 Finder->addMatcher( 304 binaryOperator(isExpansionInMainFile(), hasOperatorName(OperatorName), 305 hasLHS(allOf(expr().bind(LHSId), 306 ignoringImpCasts(cxxBoolLiteral(equals(Value)) 307 .bind(BooleanId)))), 308 hasRHS(expr().bind(RHSId)), 309 unless(hasRHS(hasDescendant(cxxBoolLiteral())))), 310 this); 311 } 312 313 void SimplifyBooleanExprCheck::matchExprCompOpBool(MatchFinder *Finder, 314 bool Value, 315 StringRef OperatorName, 316 StringRef BooleanId) { 317 Finder->addMatcher( 318 binaryOperator(isExpansionInMainFile(), hasOperatorName(OperatorName), 319 unless(hasLHS(hasDescendant(cxxBoolLiteral()))), 320 hasLHS(expr().bind(LHSId)), 321 hasRHS(allOf(expr().bind(RHSId), 322 ignoringImpCasts(cxxBoolLiteral(equals(Value)) 323 .bind(BooleanId))))), 324 this); 325 } 326 327 void SimplifyBooleanExprCheck::matchBoolCondition(MatchFinder *Finder, 328 bool Value, 329 StringRef BooleanId) { 330 Finder->addMatcher(ifStmt(isExpansionInMainFile(), 331 hasCondition(cxxBoolLiteral(equals(Value)) 332 .bind(BooleanId))).bind(IfStmtId), 333 this); 334 } 335 336 void SimplifyBooleanExprCheck::matchTernaryResult(MatchFinder *Finder, 337 bool Value, 338 StringRef TernaryId) { 339 Finder->addMatcher( 340 conditionalOperator(isExpansionInMainFile(), 341 hasTrueExpression(cxxBoolLiteral(equals(Value))), 342 hasFalseExpression(cxxBoolLiteral(equals(!Value)))) 343 .bind(TernaryId), 344 this); 345 } 346 347 void SimplifyBooleanExprCheck::matchIfReturnsBool(MatchFinder *Finder, 348 bool Value, StringRef Id) { 349 if (ChainedConditionalReturn) { 350 Finder->addMatcher(ifStmt(isExpansionInMainFile(), 351 hasThen(returnsBool(Value, ThenLiteralId)), 352 hasElse(returnsBool(!Value))).bind(Id), 353 this); 354 } else { 355 Finder->addMatcher(ifStmt(isExpansionInMainFile(), 356 unless(hasParent(ifStmt())), 357 hasThen(returnsBool(Value, ThenLiteralId)), 358 hasElse(returnsBool(!Value))).bind(Id), 359 this); 360 } 361 } 362 363 void SimplifyBooleanExprCheck::matchIfAssignsBool(MatchFinder *Finder, 364 bool Value, StringRef Id) { 365 auto SimpleThen = binaryOperator( 366 hasOperatorName("="), 367 hasLHS(declRefExpr(hasDeclaration(decl().bind(IfAssignObjId)))), 368 hasLHS(expr().bind(IfAssignVariableId)), 369 hasRHS(cxxBoolLiteral(equals(Value)).bind(IfAssignLocId))); 370 auto Then = anyOf(SimpleThen, compoundStmt(statementCountIs(1), 371 hasAnySubstatement(SimpleThen))); 372 auto SimpleElse = binaryOperator( 373 hasOperatorName("="), 374 hasLHS(declRefExpr(hasDeclaration(equalsBoundNode(IfAssignObjId)))), 375 hasRHS(cxxBoolLiteral(equals(!Value)))); 376 auto Else = anyOf(SimpleElse, compoundStmt(statementCountIs(1), 377 hasAnySubstatement(SimpleElse))); 378 if (ChainedConditionalAssignment) { 379 Finder->addMatcher( 380 ifStmt(isExpansionInMainFile(), hasThen(Then), hasElse(Else)).bind(Id), 381 this); 382 } else { 383 Finder->addMatcher(ifStmt(isExpansionInMainFile(), 384 unless(hasParent(ifStmt())), hasThen(Then), 385 hasElse(Else)).bind(Id), 386 this); 387 } 388 } 389 390 void SimplifyBooleanExprCheck::matchCompoundIfReturnsBool(MatchFinder *Finder, 391 bool Value, 392 StringRef Id) { 393 Finder->addMatcher( 394 compoundStmt(allOf(hasAnySubstatement(ifStmt(hasThen(returnsBool(Value)), 395 unless(hasElse(stmt())))), 396 hasAnySubstatement( 397 returnStmt(has(cxxBoolLiteral(equals(!Value)))) 398 .bind(CompoundReturnId)))).bind(Id), 399 this); 400 } 401 402 void SimplifyBooleanExprCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { 403 Options.store(Opts, "ChainedConditionalReturn", ChainedConditionalReturn); 404 Options.store(Opts, "ChainedConditionalAssignment", 405 ChainedConditionalAssignment); 406 } 407 408 void SimplifyBooleanExprCheck::registerMatchers(MatchFinder *Finder) { 409 matchBoolBinOpExpr(Finder, true, "&&", RightExpressionId); 410 matchBoolBinOpExpr(Finder, false, "||", RightExpressionId); 411 matchExprBinOpBool(Finder, false, "&&", RightExpressionId); 412 matchExprBinOpBool(Finder, true, "||", RightExpressionId); 413 matchBoolCompOpExpr(Finder, true, "==", RightExpressionId); 414 matchBoolCompOpExpr(Finder, false, "!=", RightExpressionId); 415 416 matchExprBinOpBool(Finder, true, "&&", LeftExpressionId); 417 matchExprBinOpBool(Finder, false, "||", LeftExpressionId); 418 matchBoolBinOpExpr(Finder, false, "&&", LeftExpressionId); 419 matchBoolBinOpExpr(Finder, true, "||", LeftExpressionId); 420 matchExprCompOpBool(Finder, true, "==", LeftExpressionId); 421 matchExprCompOpBool(Finder, false, "!=", LeftExpressionId); 422 423 matchBoolCompOpExpr(Finder, false, "==", NegatedRightExpressionId); 424 matchBoolCompOpExpr(Finder, true, "!=", NegatedRightExpressionId); 425 426 matchExprCompOpBool(Finder, false, "==", NegatedLeftExpressionId); 427 matchExprCompOpBool(Finder, true, "!=", NegatedLeftExpressionId); 428 429 matchBoolCondition(Finder, true, ConditionThenStmtId); 430 matchBoolCondition(Finder, false, ConditionElseStmtId); 431 432 matchTernaryResult(Finder, true, TernaryId); 433 matchTernaryResult(Finder, false, TernaryNegatedId); 434 435 matchIfReturnsBool(Finder, true, IfReturnsBoolId); 436 matchIfReturnsBool(Finder, false, IfReturnsNotBoolId); 437 438 matchIfAssignsBool(Finder, true, IfAssignBoolId); 439 matchIfAssignsBool(Finder, false, IfAssignNotBoolId); 440 441 matchCompoundIfReturnsBool(Finder, true, CompoundBoolId); 442 matchCompoundIfReturnsBool(Finder, false, CompoundNotBoolId); 443 } 444 445 void SimplifyBooleanExprCheck::check(const MatchFinder::MatchResult &Result) { 446 if (const CXXBoolLiteralExpr *LeftRemoved = 447 getBoolLiteral(Result, RightExpressionId)) { 448 replaceWithExpression(Result, LeftRemoved, false); 449 } else if (const CXXBoolLiteralExpr *RightRemoved = 450 getBoolLiteral(Result, LeftExpressionId)) { 451 replaceWithExpression(Result, RightRemoved, true); 452 } else if (const CXXBoolLiteralExpr *NegatedLeftRemoved = 453 getBoolLiteral(Result, NegatedRightExpressionId)) { 454 replaceWithExpression(Result, NegatedLeftRemoved, false, true); 455 } else if (const CXXBoolLiteralExpr *NegatedRightRemoved = 456 getBoolLiteral(Result, NegatedLeftExpressionId)) { 457 replaceWithExpression(Result, NegatedRightRemoved, true, true); 458 } else if (const CXXBoolLiteralExpr *TrueConditionRemoved = 459 getBoolLiteral(Result, ConditionThenStmtId)) { 460 replaceWithThenStatement(Result, TrueConditionRemoved); 461 } else if (const CXXBoolLiteralExpr *FalseConditionRemoved = 462 getBoolLiteral(Result, ConditionElseStmtId)) { 463 replaceWithElseStatement(Result, FalseConditionRemoved); 464 } else if (const auto *Ternary = 465 Result.Nodes.getNodeAs<ConditionalOperator>(TernaryId)) { 466 replaceWithCondition(Result, Ternary); 467 } else if (const auto *TernaryNegated = 468 Result.Nodes.getNodeAs<ConditionalOperator>( 469 TernaryNegatedId)) { 470 replaceWithCondition(Result, TernaryNegated, true); 471 } else if (const auto *If = Result.Nodes.getNodeAs<IfStmt>(IfReturnsBoolId)) { 472 replaceWithReturnCondition(Result, If); 473 } else if (const auto *IfNot = 474 Result.Nodes.getNodeAs<IfStmt>(IfReturnsNotBoolId)) { 475 replaceWithReturnCondition(Result, IfNot, true); 476 } else if (const auto *IfAssign = 477 Result.Nodes.getNodeAs<IfStmt>(IfAssignBoolId)) { 478 replaceWithAssignment(Result, IfAssign); 479 } else if (const auto *IfAssignNot = 480 Result.Nodes.getNodeAs<IfStmt>(IfAssignNotBoolId)) { 481 replaceWithAssignment(Result, IfAssignNot, true); 482 } else if (const auto *Compound = 483 Result.Nodes.getNodeAs<CompoundStmt>(CompoundBoolId)) { 484 replaceCompoundReturnWithCondition(Result, Compound); 485 } else if (const auto *Compound = 486 Result.Nodes.getNodeAs<CompoundStmt>(CompoundNotBoolId)) { 487 replaceCompoundReturnWithCondition(Result, Compound, true); 488 } 489 } 490 491 bool containsDiscardedTokens( 492 const ast_matchers::MatchFinder::MatchResult &Result, 493 CharSourceRange CharRange) { 494 std::string ReplacementText = 495 Lexer::getSourceText(CharRange, *Result.SourceManager, 496 Result.Context->getLangOpts()) 497 .str(); 498 Lexer Lex(CharRange.getBegin(), Result.Context->getLangOpts(), 499 ReplacementText.data(), ReplacementText.data(), 500 ReplacementText.data() + ReplacementText.size()); 501 Lex.SetCommentRetentionState(true); 502 Token Tok; 503 504 while (!Lex.LexFromRawLexer(Tok)) { 505 if (Tok.is(tok::TokenKind::comment) || Tok.is(tok::TokenKind::hash)) 506 return true; 507 } 508 509 return false; 510 } 511 512 void SimplifyBooleanExprCheck::issueDiag( 513 const ast_matchers::MatchFinder::MatchResult &Result, SourceLocation Loc, 514 StringRef Description, SourceRange ReplacementRange, 515 StringRef Replacement) { 516 CharSourceRange CharRange = Lexer::makeFileCharRange( 517 CharSourceRange::getTokenRange(ReplacementRange), *Result.SourceManager, 518 Result.Context->getLangOpts()); 519 520 DiagnosticBuilder Diag = diag(Loc, Description); 521 if (!containsDiscardedTokens(Result, CharRange)) 522 Diag << FixItHint::CreateReplacement(CharRange, Replacement); 523 } 524 525 void SimplifyBooleanExprCheck::replaceWithExpression( 526 const ast_matchers::MatchFinder::MatchResult &Result, 527 const CXXBoolLiteralExpr *BoolLiteral, bool UseLHS, bool Negated) { 528 const auto *LHS = Result.Nodes.getNodeAs<Expr>(LHSId); 529 const auto *RHS = Result.Nodes.getNodeAs<Expr>(RHSId); 530 std::string Replacement = 531 replacementExpression(Result, Negated, UseLHS ? LHS : RHS); 532 SourceRange Range(LHS->getLocStart(), RHS->getLocEnd()); 533 issueDiag(Result, BoolLiteral->getLocStart(), SimplifyOperatorDiagnostic, 534 Range, Replacement); 535 } 536 537 void SimplifyBooleanExprCheck::replaceWithThenStatement( 538 const MatchFinder::MatchResult &Result, 539 const CXXBoolLiteralExpr *TrueConditionRemoved) { 540 const auto *IfStatement = Result.Nodes.getNodeAs<IfStmt>(IfStmtId); 541 issueDiag(Result, TrueConditionRemoved->getLocStart(), 542 SimplifyConditionDiagnostic, IfStatement->getSourceRange(), 543 getText(Result, *IfStatement->getThen())); 544 } 545 546 void SimplifyBooleanExprCheck::replaceWithElseStatement( 547 const MatchFinder::MatchResult &Result, 548 const CXXBoolLiteralExpr *FalseConditionRemoved) { 549 const auto *IfStatement = Result.Nodes.getNodeAs<IfStmt>(IfStmtId); 550 const Stmt *ElseStatement = IfStatement->getElse(); 551 issueDiag(Result, FalseConditionRemoved->getLocStart(), 552 SimplifyConditionDiagnostic, IfStatement->getSourceRange(), 553 ElseStatement ? getText(Result, *ElseStatement) : ""); 554 } 555 556 void SimplifyBooleanExprCheck::replaceWithCondition( 557 const MatchFinder::MatchResult &Result, const ConditionalOperator *Ternary, 558 bool Negated) { 559 std::string Replacement = 560 replacementExpression(Result, Negated, Ternary->getCond()); 561 issueDiag(Result, Ternary->getTrueExpr()->getLocStart(), 562 "redundant boolean literal in ternary expression result", 563 Ternary->getSourceRange(), Replacement); 564 } 565 566 void SimplifyBooleanExprCheck::replaceWithReturnCondition( 567 const MatchFinder::MatchResult &Result, const IfStmt *If, bool Negated) { 568 StringRef Terminator = isa<CompoundStmt>(If->getElse()) ? ";" : ""; 569 std::string Condition = replacementExpression(Result, Negated, If->getCond()); 570 std::string Replacement = ("return " + Condition + Terminator).str(); 571 SourceLocation Start = 572 Result.Nodes.getNodeAs<CXXBoolLiteralExpr>(ThenLiteralId)->getLocStart(); 573 issueDiag(Result, Start, SimplifyConditionalReturnDiagnostic, 574 If->getSourceRange(), Replacement); 575 } 576 577 void SimplifyBooleanExprCheck::replaceCompoundReturnWithCondition( 578 const MatchFinder::MatchResult &Result, const CompoundStmt *Compound, 579 bool Negated) { 580 const auto *Ret = Result.Nodes.getNodeAs<ReturnStmt>(CompoundReturnId); 581 582 // The body shouldn't be empty because the matcher ensures that it must 583 // contain at least two statements: 584 // 1) A `return` statement returning a boolean literal `false` or `true` 585 // 2) An `if` statement with no `else` clause that consists fo a single 586 // `return` statement returning the opposite boolean literal `true` or 587 // `false`. 588 assert(Compound->size() >= 2); 589 const IfStmt *BeforeIf = nullptr; 590 CompoundStmt::const_body_iterator Current = Compound->body_begin(); 591 CompoundStmt::const_body_iterator After = Compound->body_begin(); 592 for (++After; After != Compound->body_end() && *Current != Ret; 593 ++Current, ++After) { 594 if (const auto *If = dyn_cast<IfStmt>(*Current)) { 595 if (const CXXBoolLiteralExpr *Lit = stmtReturnsBool(If, Negated)) { 596 if (*After == Ret) { 597 if (!ChainedConditionalReturn && BeforeIf) 598 continue; 599 600 const Expr *Condition = If->getCond(); 601 std::string Replacement = 602 "return " + replacementExpression(Result, Negated, Condition); 603 issueDiag( 604 Result, Lit->getLocStart(), SimplifyConditionalReturnDiagnostic, 605 SourceRange(If->getLocStart(), Ret->getLocEnd()), Replacement); 606 return; 607 } 608 609 BeforeIf = If; 610 } 611 } else { 612 BeforeIf = nullptr; 613 } 614 } 615 } 616 617 void SimplifyBooleanExprCheck::replaceWithAssignment( 618 const MatchFinder::MatchResult &Result, const IfStmt *IfAssign, 619 bool Negated) { 620 SourceRange Range = IfAssign->getSourceRange(); 621 StringRef VariableName = 622 getText(Result, *Result.Nodes.getNodeAs<Expr>(IfAssignVariableId)); 623 StringRef Terminator = isa<CompoundStmt>(IfAssign->getElse()) ? ";" : ""; 624 std::string Condition = 625 replacementExpression(Result, Negated, IfAssign->getCond()); 626 std::string Replacement = 627 (VariableName + " = " + Condition + Terminator).str(); 628 SourceLocation Location = 629 Result.Nodes.getNodeAs<CXXBoolLiteralExpr>(IfAssignLocId)->getLocStart(); 630 issueDiag(Result, Location, 631 "redundant boolean literal in conditional assignment", Range, 632 Replacement); 633 } 634 635 } // namespace readability 636 } // namespace tidy 637 } // namespace clang 638