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