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