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