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(cxxBoolLiteral(equals(!Value))))
465                                  .bind(CompoundReturnId))))
466           .bind(Id),
467       this);
468 }
469 
470 void SimplifyBooleanExprCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
471   Options.store(Opts, "ChainedConditionalReturn", ChainedConditionalReturn);
472   Options.store(Opts, "ChainedConditionalAssignment",
473                 ChainedConditionalAssignment);
474 }
475 
476 void SimplifyBooleanExprCheck::registerMatchers(MatchFinder *Finder) {
477   matchBoolBinOpExpr(Finder, true, "&&", RightExpressionId);
478   matchBoolBinOpExpr(Finder, false, "||", RightExpressionId);
479   matchExprBinOpBool(Finder, false, "&&", RightExpressionId);
480   matchExprBinOpBool(Finder, true, "||", RightExpressionId);
481   matchBoolCompOpExpr(Finder, true, "==", RightExpressionId);
482   matchBoolCompOpExpr(Finder, false, "!=", RightExpressionId);
483 
484   matchExprBinOpBool(Finder, true, "&&", LeftExpressionId);
485   matchExprBinOpBool(Finder, false, "||", LeftExpressionId);
486   matchBoolBinOpExpr(Finder, false, "&&", LeftExpressionId);
487   matchBoolBinOpExpr(Finder, true, "||", LeftExpressionId);
488   matchExprCompOpBool(Finder, true, "==", LeftExpressionId);
489   matchExprCompOpBool(Finder, false, "!=", LeftExpressionId);
490 
491   matchBoolCompOpExpr(Finder, false, "==", NegatedRightExpressionId);
492   matchBoolCompOpExpr(Finder, true, "!=", NegatedRightExpressionId);
493 
494   matchExprCompOpBool(Finder, false, "==", NegatedLeftExpressionId);
495   matchExprCompOpBool(Finder, true, "!=", NegatedLeftExpressionId);
496 
497   matchBoolCondition(Finder, true, ConditionThenStmtId);
498   matchBoolCondition(Finder, false, ConditionElseStmtId);
499 
500   matchTernaryResult(Finder, true, TernaryId);
501   matchTernaryResult(Finder, false, TernaryNegatedId);
502 
503   matchIfReturnsBool(Finder, true, IfReturnsBoolId);
504   matchIfReturnsBool(Finder, false, IfReturnsNotBoolId);
505 
506   matchIfAssignsBool(Finder, true, IfAssignBoolId);
507   matchIfAssignsBool(Finder, false, IfAssignNotBoolId);
508 
509   matchCompoundIfReturnsBool(Finder, true, CompoundBoolId);
510   matchCompoundIfReturnsBool(Finder, false, CompoundNotBoolId);
511 }
512 
513 void SimplifyBooleanExprCheck::check(const MatchFinder::MatchResult &Result) {
514   if (const CXXBoolLiteralExpr *LeftRemoved =
515           getBoolLiteral(Result, RightExpressionId))
516     replaceWithExpression(Result, LeftRemoved, false);
517   else if (const CXXBoolLiteralExpr *RightRemoved =
518                getBoolLiteral(Result, LeftExpressionId))
519     replaceWithExpression(Result, RightRemoved, true);
520   else if (const CXXBoolLiteralExpr *NegatedLeftRemoved =
521                getBoolLiteral(Result, NegatedRightExpressionId))
522     replaceWithExpression(Result, NegatedLeftRemoved, false, true);
523   else if (const CXXBoolLiteralExpr *NegatedRightRemoved =
524                getBoolLiteral(Result, NegatedLeftExpressionId))
525     replaceWithExpression(Result, NegatedRightRemoved, true, true);
526   else if (const CXXBoolLiteralExpr *TrueConditionRemoved =
527                getBoolLiteral(Result, ConditionThenStmtId))
528     replaceWithThenStatement(Result, TrueConditionRemoved);
529   else if (const CXXBoolLiteralExpr *FalseConditionRemoved =
530                getBoolLiteral(Result, ConditionElseStmtId))
531     replaceWithElseStatement(Result, FalseConditionRemoved);
532   else if (const auto *Ternary =
533                Result.Nodes.getNodeAs<ConditionalOperator>(TernaryId))
534     replaceWithCondition(Result, Ternary);
535   else if (const auto *TernaryNegated =
536                Result.Nodes.getNodeAs<ConditionalOperator>(TernaryNegatedId))
537     replaceWithCondition(Result, TernaryNegated, true);
538   else if (const auto *If = Result.Nodes.getNodeAs<IfStmt>(IfReturnsBoolId))
539     replaceWithReturnCondition(Result, If);
540   else if (const auto *IfNot =
541                Result.Nodes.getNodeAs<IfStmt>(IfReturnsNotBoolId))
542     replaceWithReturnCondition(Result, IfNot, true);
543   else if (const auto *IfAssign =
544                Result.Nodes.getNodeAs<IfStmt>(IfAssignBoolId))
545     replaceWithAssignment(Result, IfAssign);
546   else if (const auto *IfAssignNot =
547                Result.Nodes.getNodeAs<IfStmt>(IfAssignNotBoolId))
548     replaceWithAssignment(Result, IfAssignNot, true);
549   else if (const auto *Compound =
550                Result.Nodes.getNodeAs<CompoundStmt>(CompoundBoolId))
551     replaceCompoundReturnWithCondition(Result, Compound);
552   else if (const auto *Compound =
553                Result.Nodes.getNodeAs<CompoundStmt>(CompoundNotBoolId))
554     replaceCompoundReturnWithCondition(Result, Compound, true);
555 }
556 
557 void SimplifyBooleanExprCheck::issueDiag(
558     const ast_matchers::MatchFinder::MatchResult &Result, SourceLocation Loc,
559     StringRef Description, SourceRange ReplacementRange,
560     StringRef Replacement) {
561   CharSourceRange CharRange = Lexer::makeFileCharRange(
562       CharSourceRange::getTokenRange(ReplacementRange), *Result.SourceManager,
563       Result.Context->getLangOpts());
564 
565   DiagnosticBuilder Diag = diag(Loc, Description);
566   if (!containsDiscardedTokens(Result, CharRange))
567     Diag << FixItHint::CreateReplacement(CharRange, Replacement);
568 }
569 
570 void SimplifyBooleanExprCheck::replaceWithExpression(
571     const ast_matchers::MatchFinder::MatchResult &Result,
572     const CXXBoolLiteralExpr *BoolLiteral, bool UseLHS, bool Negated) {
573   const auto *LHS = Result.Nodes.getNodeAs<Expr>(LHSId);
574   const auto *RHS = Result.Nodes.getNodeAs<Expr>(RHSId);
575   std::string Replacement =
576       replacementExpression(Result, Negated, UseLHS ? LHS : RHS);
577   SourceRange Range(LHS->getLocStart(), RHS->getLocEnd());
578   issueDiag(Result, BoolLiteral->getLocStart(), SimplifyOperatorDiagnostic,
579             Range, Replacement);
580 }
581 
582 void SimplifyBooleanExprCheck::replaceWithThenStatement(
583     const MatchFinder::MatchResult &Result,
584     const CXXBoolLiteralExpr *TrueConditionRemoved) {
585   const auto *IfStatement = Result.Nodes.getNodeAs<IfStmt>(IfStmtId);
586   issueDiag(Result, TrueConditionRemoved->getLocStart(),
587             SimplifyConditionDiagnostic, IfStatement->getSourceRange(),
588             getText(Result, *IfStatement->getThen()));
589 }
590 
591 void SimplifyBooleanExprCheck::replaceWithElseStatement(
592     const MatchFinder::MatchResult &Result,
593     const CXXBoolLiteralExpr *FalseConditionRemoved) {
594   const auto *IfStatement = Result.Nodes.getNodeAs<IfStmt>(IfStmtId);
595   const Stmt *ElseStatement = IfStatement->getElse();
596   issueDiag(Result, FalseConditionRemoved->getLocStart(),
597             SimplifyConditionDiagnostic, IfStatement->getSourceRange(),
598             ElseStatement ? getText(Result, *ElseStatement) : "");
599 }
600 
601 void SimplifyBooleanExprCheck::replaceWithCondition(
602     const MatchFinder::MatchResult &Result, const ConditionalOperator *Ternary,
603     bool Negated) {
604   std::string Replacement =
605       replacementExpression(Result, Negated, Ternary->getCond());
606   issueDiag(Result, Ternary->getTrueExpr()->getLocStart(),
607             "redundant boolean literal in ternary expression result",
608             Ternary->getSourceRange(), Replacement);
609 }
610 
611 void SimplifyBooleanExprCheck::replaceWithReturnCondition(
612     const MatchFinder::MatchResult &Result, const IfStmt *If, bool Negated) {
613   StringRef Terminator = isa<CompoundStmt>(If->getElse()) ? ";" : "";
614   std::string Condition = replacementExpression(Result, Negated, If->getCond());
615   std::string Replacement = ("return " + Condition + Terminator).str();
616   SourceLocation Start =
617       Result.Nodes.getNodeAs<CXXBoolLiteralExpr>(ThenLiteralId)->getLocStart();
618   issueDiag(Result, Start, SimplifyConditionalReturnDiagnostic,
619             If->getSourceRange(), Replacement);
620 }
621 
622 void SimplifyBooleanExprCheck::replaceCompoundReturnWithCondition(
623     const MatchFinder::MatchResult &Result, const CompoundStmt *Compound,
624     bool Negated) {
625   const auto *Ret = Result.Nodes.getNodeAs<ReturnStmt>(CompoundReturnId);
626 
627   // The body shouldn't be empty because the matcher ensures that it must
628   // contain at least two statements:
629   // 1) A `return` statement returning a boolean literal `false` or `true`
630   // 2) An `if` statement with no `else` clause that consists of a single
631   //    `return` statement returning the opposite boolean literal `true` or
632   //    `false`.
633   assert(Compound->size() >= 2);
634   const IfStmt *BeforeIf = nullptr;
635   CompoundStmt::const_body_iterator Current = Compound->body_begin();
636   CompoundStmt::const_body_iterator After = Compound->body_begin();
637   for (++After; After != Compound->body_end() && *Current != Ret;
638        ++Current, ++After) {
639     if (const auto *If = dyn_cast<IfStmt>(*Current)) {
640       if (const CXXBoolLiteralExpr *Lit = stmtReturnsBool(If, Negated)) {
641         if (*After == Ret) {
642           if (!ChainedConditionalReturn && BeforeIf)
643             continue;
644 
645           const Expr *Condition = If->getCond();
646           std::string Replacement =
647               "return " + replacementExpression(Result, Negated, Condition);
648           issueDiag(
649               Result, Lit->getLocStart(), SimplifyConditionalReturnDiagnostic,
650               SourceRange(If->getLocStart(), Ret->getLocEnd()), Replacement);
651           return;
652         }
653 
654         BeforeIf = If;
655       }
656     } else {
657       BeforeIf = nullptr;
658     }
659   }
660 }
661 
662 void SimplifyBooleanExprCheck::replaceWithAssignment(
663     const MatchFinder::MatchResult &Result, const IfStmt *IfAssign,
664     bool Negated) {
665   SourceRange Range = IfAssign->getSourceRange();
666   StringRef VariableName =
667       getText(Result, *Result.Nodes.getNodeAs<Expr>(IfAssignVariableId));
668   StringRef Terminator = isa<CompoundStmt>(IfAssign->getElse()) ? ";" : "";
669   std::string Condition =
670       replacementExpression(Result, Negated, IfAssign->getCond());
671   std::string Replacement =
672       (VariableName + " = " + Condition + Terminator).str();
673   SourceLocation Location =
674       Result.Nodes.getNodeAs<CXXBoolLiteralExpr>(IfAssignLocId)->getLocStart();
675   issueDiag(Result, Location,
676             "redundant boolean literal in conditional assignment", Range,
677             Replacement);
678 }
679 
680 } // namespace readability
681 } // namespace tidy
682 } // namespace clang
683