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