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