clang-tools  6.0.0
SimplifyBooleanExprCheck.cpp
Go to the documentation of this file.
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 
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->getLocStart().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  const bool NeedsStaticCast = needsStaticCast(E);
199  if (Negated) {
200  if (const auto *UnOp = dyn_cast<UnaryOperator>(E)) {
201  if (UnOp->getOpcode() == UO_LNot) {
202  if (needsNullPtrComparison(UnOp->getSubExpr()))
203  return compareExpressionToNullPtr(Result, UnOp->getSubExpr(), true);
204 
205  if (needsZeroComparison(UnOp->getSubExpr()))
206  return compareExpressionToZero(Result, UnOp->getSubExpr(), true);
207 
208  return replacementExpression(Result, false, UnOp->getSubExpr());
209  }
210  }
211 
212  if (needsNullPtrComparison(E))
213  return compareExpressionToNullPtr(Result, E, false);
214 
215  if (needsZeroComparison(E))
216  return compareExpressionToZero(Result, E, false);
217 
218  StringRef NegatedOperator;
219  const Expr *LHS = nullptr;
220  const Expr *RHS = nullptr;
221  if (const auto *BinOp = dyn_cast<BinaryOperator>(E)) {
222  NegatedOperator = negatedOperator(BinOp);
223  LHS = BinOp->getLHS();
224  RHS = BinOp->getRHS();
225  } else if (const auto *OpExpr = dyn_cast<CXXOperatorCallExpr>(E)) {
226  if (OpExpr->getNumArgs() == 2) {
227  NegatedOperator = negatedOperator(OpExpr);
228  LHS = OpExpr->getArg(0);
229  RHS = OpExpr->getArg(1);
230  }
231  }
232  if (!NegatedOperator.empty() && LHS && RHS)
233  return (asBool((getText(Result, *LHS) + " " + NegatedOperator + " " +
234  getText(Result, *RHS))
235  .str(),
236  NeedsStaticCast));
237 
238  StringRef Text = getText(Result, *E);
239  if (!NeedsStaticCast && needsParensAfterUnaryNegation(E))
240  return ("!(" + Text + ")").str();
241 
242  if (needsNullPtrComparison(E))
243  return compareExpressionToNullPtr(Result, E, false);
244 
245  if (needsZeroComparison(E))
246  return compareExpressionToZero(Result, E, false);
247 
248  return ("!" + asBool(Text, NeedsStaticCast));
249  }
250 
251  if (const auto *UnOp = dyn_cast<UnaryOperator>(E)) {
252  if (UnOp->getOpcode() == UO_LNot) {
253  if (needsNullPtrComparison(UnOp->getSubExpr()))
254  return compareExpressionToNullPtr(Result, UnOp->getSubExpr(), false);
255 
256  if (needsZeroComparison(UnOp->getSubExpr()))
257  return compareExpressionToZero(Result, UnOp->getSubExpr(), false);
258  }
259  }
260 
261  if (needsNullPtrComparison(E))
262  return compareExpressionToNullPtr(Result, E, true);
263 
264  if (needsZeroComparison(E))
265  return compareExpressionToZero(Result, E, true);
266 
267  return asBool(getText(Result, *E), NeedsStaticCast);
268 }
269 
270 const CXXBoolLiteralExpr *stmtReturnsBool(const ReturnStmt *Ret, bool Negated) {
271  if (const auto *Bool = dyn_cast<CXXBoolLiteralExpr>(Ret->getRetValue())) {
272  if (Bool->getValue() == !Negated)
273  return Bool;
274  }
275 
276  return nullptr;
277 }
278 
279 const CXXBoolLiteralExpr *stmtReturnsBool(const IfStmt *IfRet, bool Negated) {
280  if (IfRet->getElse() != nullptr)
281  return nullptr;
282 
283  if (const auto *Ret = dyn_cast<ReturnStmt>(IfRet->getThen()))
284  return stmtReturnsBool(Ret, Negated);
285 
286  if (const auto *Compound = dyn_cast<CompoundStmt>(IfRet->getThen())) {
287  if (Compound->size() == 1) {
288  if (const auto *CompoundRet = dyn_cast<ReturnStmt>(Compound->body_back()))
289  return stmtReturnsBool(CompoundRet, Negated);
290  }
291  }
292 
293  return nullptr;
294 }
295 
296 bool containsDiscardedTokens(const MatchFinder::MatchResult &Result,
297  CharSourceRange CharRange) {
298  std::string ReplacementText =
299  Lexer::getSourceText(CharRange, *Result.SourceManager,
300  Result.Context->getLangOpts())
301  .str();
302  Lexer Lex(CharRange.getBegin(), Result.Context->getLangOpts(),
303  ReplacementText.data(), ReplacementText.data(),
304  ReplacementText.data() + ReplacementText.size());
305  Lex.SetCommentRetentionState(true);
306 
307  Token Tok;
308  while (!Lex.LexFromRawLexer(Tok)) {
309  if (Tok.is(tok::TokenKind::comment) || Tok.is(tok::TokenKind::hash))
310  return true;
311  }
312 
313  return false;
314 }
315 
316 } // namespace
317 
318 class SimplifyBooleanExprCheck::Visitor : public RecursiveASTVisitor<Visitor> {
319  using Base = RecursiveASTVisitor<Visitor>;
320 
321  public:
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:
333  const MatchFinder::MatchResult &Result;
334 };
335 
336 
337 SimplifyBooleanExprCheck::SimplifyBooleanExprCheck(StringRef Name,
338  ClangTidyContext *Context)
339  : ClangTidyCheck(Name, Context),
340  ChainedConditionalReturn(Options.get("ChainedConditionalReturn", 0U)),
341  ChainedConditionalAssignment(
342  Options.get("ChainedConditionalAssignment", 0U)) {}
343 
344 bool containsBoolLiteral(const Expr *E) {
345  if (!E)
346  return false;
347  E = E->IgnoreParenImpCasts();
348  if (isa<CXXBoolLiteralExpr>(E))
349  return true;
350  if (const auto *BinOp = dyn_cast<BinaryOperator>(E))
351  return containsBoolLiteral(BinOp->getLHS()) ||
352  containsBoolLiteral(BinOp->getRHS());
353  if (const auto *UnaryOp = dyn_cast<UnaryOperator>(E))
354  return containsBoolLiteral(UnaryOp->getSubExpr());
355  return false;
356 }
357 
358 void SimplifyBooleanExprCheck::reportBinOp(
359  const MatchFinder::MatchResult &Result, const BinaryOperator *Op) {
360  const auto *LHS = Op->getLHS()->IgnoreParenImpCasts();
361  const auto *RHS = Op->getRHS()->IgnoreParenImpCasts();
362 
363  const CXXBoolLiteralExpr *Bool = nullptr;
364  const Expr *Other = nullptr;
365  if ((Bool = dyn_cast<CXXBoolLiteralExpr>(LHS)))
366  Other = RHS;
367  else if ((Bool = dyn_cast<CXXBoolLiteralExpr>(RHS)))
368  Other = LHS;
369  else
370  return;
371 
372  if (Bool->getLocStart().isMacroID())
373  return;
374 
375  // FIXME: why do we need this?
376  if (!isa<CXXBoolLiteralExpr>(Other) && containsBoolLiteral(Other))
377  return;
378 
379  bool BoolValue = Bool->getValue();
380 
381  auto replaceWithExpression = [this, &Result, LHS, RHS, Bool](
382  const Expr *ReplaceWith, bool Negated) {
383  std::string Replacement =
384  replacementExpression(Result, Negated, ReplaceWith);
385  SourceRange Range(LHS->getLocStart(), RHS->getLocEnd());
386  issueDiag(Result, Bool->getLocStart(), SimplifyOperatorDiagnostic, Range,
387  Replacement);
388  };
389 
390  switch (Op->getOpcode()) {
391  case BO_LAnd:
392  if (BoolValue) {
393  // expr && true -> expr
394  replaceWithExpression(Other, /*Negated=*/false);
395  } else {
396  // expr && false -> false
397  replaceWithExpression(Bool, /*Negated=*/false);
398  }
399  break;
400  case BO_LOr:
401  if (BoolValue) {
402  // expr || true -> true
403  replaceWithExpression(Bool, /*Negated=*/false);
404  } else {
405  // expr || false -> expr
406  replaceWithExpression(Other, /*Negated=*/false);
407  }
408  break;
409  case BO_EQ:
410  // expr == true -> expr, expr == false -> !expr
411  replaceWithExpression(Other, /*Negated=*/!BoolValue);
412  break;
413  case BO_NE:
414  // expr != true -> !expr, expr != false -> expr
415  replaceWithExpression(Other, /*Negated=*/BoolValue);
416  break;
417  default:
418  break;
419  }
420 }
421 
422 void SimplifyBooleanExprCheck::matchBoolCondition(MatchFinder *Finder,
423  bool Value,
424  StringRef BooleanId) {
425  Finder->addMatcher(
426  ifStmt(isExpansionInMainFile(),
427  hasCondition(cxxBoolLiteral(equals(Value)).bind(BooleanId)))
428  .bind(IfStmtId),
429  this);
430 }
431 
432 void SimplifyBooleanExprCheck::matchTernaryResult(MatchFinder *Finder,
433  bool Value,
434  StringRef TernaryId) {
435  Finder->addMatcher(
436  conditionalOperator(isExpansionInMainFile(),
437  hasTrueExpression(cxxBoolLiteral(equals(Value))),
438  hasFalseExpression(cxxBoolLiteral(equals(!Value))))
439  .bind(TernaryId),
440  this);
441 }
442 
443 void SimplifyBooleanExprCheck::matchIfReturnsBool(MatchFinder *Finder,
444  bool Value, StringRef Id) {
445  if (ChainedConditionalReturn)
446  Finder->addMatcher(ifStmt(isExpansionInMainFile(),
447  hasThen(returnsBool(Value, ThenLiteralId)),
448  hasElse(returnsBool(!Value)))
449  .bind(Id),
450  this);
451  else
452  Finder->addMatcher(ifStmt(isExpansionInMainFile(),
453  unless(hasParent(ifStmt())),
454  hasThen(returnsBool(Value, ThenLiteralId)),
455  hasElse(returnsBool(!Value)))
456  .bind(Id),
457  this);
458 }
459 
460 void SimplifyBooleanExprCheck::matchIfAssignsBool(MatchFinder *Finder,
461  bool Value, StringRef Id) {
462  auto SimpleThen = binaryOperator(
463  hasOperatorName("="),
464  hasLHS(declRefExpr(hasDeclaration(decl().bind(IfAssignObjId)))),
465  hasLHS(expr().bind(IfAssignVariableId)),
466  hasRHS(cxxBoolLiteral(equals(Value)).bind(IfAssignLocId)));
467  auto Then = anyOf(SimpleThen, compoundStmt(statementCountIs(1),
468  hasAnySubstatement(SimpleThen)));
469  auto SimpleElse = binaryOperator(
470  hasOperatorName("="),
471  hasLHS(declRefExpr(hasDeclaration(equalsBoundNode(IfAssignObjId)))),
472  hasRHS(cxxBoolLiteral(equals(!Value))));
473  auto Else = anyOf(SimpleElse, compoundStmt(statementCountIs(1),
474  hasAnySubstatement(SimpleElse)));
475  if (ChainedConditionalAssignment)
476  Finder->addMatcher(
477  ifStmt(isExpansionInMainFile(), hasThen(Then), hasElse(Else)).bind(Id),
478  this);
479  else
480  Finder->addMatcher(ifStmt(isExpansionInMainFile(),
481  unless(hasParent(ifStmt())), hasThen(Then),
482  hasElse(Else))
483  .bind(Id),
484  this);
485 }
486 
487 void SimplifyBooleanExprCheck::matchCompoundIfReturnsBool(MatchFinder *Finder,
488  bool Value,
489  StringRef Id) {
490  Finder->addMatcher(
491  compoundStmt(allOf(hasAnySubstatement(ifStmt(hasThen(returnsBool(Value)),
492  unless(hasElse(stmt())))),
493  hasAnySubstatement(
494  returnStmt(has(ignoringParenImpCasts(
495  cxxBoolLiteral(equals(!Value)))))
496  .bind(CompoundReturnId))))
497  .bind(Id),
498  this);
499 }
500 
502  Options.store(Opts, "ChainedConditionalReturn", ChainedConditionalReturn);
503  Options.store(Opts, "ChainedConditionalAssignment",
504  ChainedConditionalAssignment);
505 }
506 
508  Finder->addMatcher(translationUnitDecl().bind("top"), this);
509 
510  matchBoolCondition(Finder, true, ConditionThenStmtId);
511  matchBoolCondition(Finder, false, ConditionElseStmtId);
512 
513  matchTernaryResult(Finder, true, TernaryId);
514  matchTernaryResult(Finder, false, TernaryNegatedId);
515 
516  matchIfReturnsBool(Finder, true, IfReturnsBoolId);
517  matchIfReturnsBool(Finder, false, IfReturnsNotBoolId);
518 
519  matchIfAssignsBool(Finder, true, IfAssignBoolId);
520  matchIfAssignsBool(Finder, false, IfAssignNotBoolId);
521 
522  matchCompoundIfReturnsBool(Finder, true, CompoundBoolId);
523  matchCompoundIfReturnsBool(Finder, false, CompoundNotBoolId);
524 }
525 
526 void SimplifyBooleanExprCheck::check(const MatchFinder::MatchResult &Result) {
527  if (const CXXBoolLiteralExpr *TrueConditionRemoved =
528  getBoolLiteral(Result, ConditionThenStmtId))
529  replaceWithThenStatement(Result, TrueConditionRemoved);
530  else if (const CXXBoolLiteralExpr *FalseConditionRemoved =
531  getBoolLiteral(Result, ConditionElseStmtId))
532  replaceWithElseStatement(Result, FalseConditionRemoved);
533  else if (const auto *Ternary =
534  Result.Nodes.getNodeAs<ConditionalOperator>(TernaryId))
535  replaceWithCondition(Result, Ternary);
536  else if (const auto *TernaryNegated =
537  Result.Nodes.getNodeAs<ConditionalOperator>(TernaryNegatedId))
538  replaceWithCondition(Result, TernaryNegated, true);
539  else if (const auto *If = Result.Nodes.getNodeAs<IfStmt>(IfReturnsBoolId))
540  replaceWithReturnCondition(Result, If);
541  else if (const auto *IfNot =
542  Result.Nodes.getNodeAs<IfStmt>(IfReturnsNotBoolId))
543  replaceWithReturnCondition(Result, IfNot, true);
544  else if (const auto *IfAssign =
545  Result.Nodes.getNodeAs<IfStmt>(IfAssignBoolId))
546  replaceWithAssignment(Result, IfAssign);
547  else if (const auto *IfAssignNot =
548  Result.Nodes.getNodeAs<IfStmt>(IfAssignNotBoolId))
549  replaceWithAssignment(Result, IfAssignNot, true);
550  else if (const auto *Compound =
551  Result.Nodes.getNodeAs<CompoundStmt>(CompoundBoolId))
552  replaceCompoundReturnWithCondition(Result, Compound);
553  else if (const auto *Compound =
554  Result.Nodes.getNodeAs<CompoundStmt>(CompoundNotBoolId))
555  replaceCompoundReturnWithCondition(Result, Compound, true);
556  else if (const auto TU = Result.Nodes.getNodeAs<Decl>("top"))
557  Visitor(this, Result).TraverseDecl(const_cast<Decl*>(TU));
558 }
559 
560 void SimplifyBooleanExprCheck::issueDiag(
561  const ast_matchers::MatchFinder::MatchResult &Result, SourceLocation Loc,
562  StringRef Description, SourceRange ReplacementRange,
563  StringRef Replacement) {
564  CharSourceRange CharRange =
565  Lexer::makeFileCharRange(CharSourceRange::getTokenRange(ReplacementRange),
566  *Result.SourceManager, getLangOpts());
567 
568  DiagnosticBuilder Diag = diag(Loc, Description);
569  if (!containsDiscardedTokens(Result, CharRange))
570  Diag << FixItHint::CreateReplacement(CharRange, Replacement);
571 }
572 
573 void SimplifyBooleanExprCheck::replaceWithThenStatement(
574  const MatchFinder::MatchResult &Result,
575  const CXXBoolLiteralExpr *TrueConditionRemoved) {
576  const auto *IfStatement = Result.Nodes.getNodeAs<IfStmt>(IfStmtId);
577  issueDiag(Result, TrueConditionRemoved->getLocStart(),
578  SimplifyConditionDiagnostic, IfStatement->getSourceRange(),
579  getText(Result, *IfStatement->getThen()));
580 }
581 
582 void SimplifyBooleanExprCheck::replaceWithElseStatement(
583  const MatchFinder::MatchResult &Result,
584  const CXXBoolLiteralExpr *FalseConditionRemoved) {
585  const auto *IfStatement = Result.Nodes.getNodeAs<IfStmt>(IfStmtId);
586  const Stmt *ElseStatement = IfStatement->getElse();
587  issueDiag(Result, FalseConditionRemoved->getLocStart(),
588  SimplifyConditionDiagnostic, IfStatement->getSourceRange(),
589  ElseStatement ? getText(Result, *ElseStatement) : "");
590 }
591 
592 void SimplifyBooleanExprCheck::replaceWithCondition(
593  const MatchFinder::MatchResult &Result, const ConditionalOperator *Ternary,
594  bool Negated) {
595  std::string Replacement =
596  replacementExpression(Result, Negated, Ternary->getCond());
597  issueDiag(Result, Ternary->getTrueExpr()->getLocStart(),
598  "redundant boolean literal in ternary expression result",
599  Ternary->getSourceRange(), Replacement);
600 }
601 
602 void SimplifyBooleanExprCheck::replaceWithReturnCondition(
603  const MatchFinder::MatchResult &Result, const IfStmt *If, bool Negated) {
604  StringRef Terminator = isa<CompoundStmt>(If->getElse()) ? ";" : "";
605  std::string Condition = replacementExpression(Result, Negated, If->getCond());
606  std::string Replacement = ("return " + Condition + Terminator).str();
607  SourceLocation Start =
608  Result.Nodes.getNodeAs<CXXBoolLiteralExpr>(ThenLiteralId)->getLocStart();
609  issueDiag(Result, Start, SimplifyConditionalReturnDiagnostic,
610  If->getSourceRange(), Replacement);
611 }
612 
613 void SimplifyBooleanExprCheck::replaceCompoundReturnWithCondition(
614  const MatchFinder::MatchResult &Result, const CompoundStmt *Compound,
615  bool Negated) {
616  const auto *Ret = Result.Nodes.getNodeAs<ReturnStmt>(CompoundReturnId);
617 
618  // The body shouldn't be empty because the matcher ensures that it must
619  // contain at least two statements:
620  // 1) A `return` statement returning a boolean literal `false` or `true`
621  // 2) An `if` statement with no `else` clause that consists of a single
622  // `return` statement returning the opposite boolean literal `true` or
623  // `false`.
624  assert(Compound->size() >= 2);
625  const IfStmt *BeforeIf = nullptr;
626  CompoundStmt::const_body_iterator Current = Compound->body_begin();
627  CompoundStmt::const_body_iterator After = Compound->body_begin();
628  for (++After; After != Compound->body_end() && *Current != Ret;
629  ++Current, ++After) {
630  if (const auto *If = dyn_cast<IfStmt>(*Current)) {
631  if (const CXXBoolLiteralExpr *Lit = stmtReturnsBool(If, Negated)) {
632  if (*After == Ret) {
633  if (!ChainedConditionalReturn && BeforeIf)
634  continue;
635 
636  const Expr *Condition = If->getCond();
637  std::string Replacement =
638  "return " + replacementExpression(Result, Negated, Condition);
639  issueDiag(
640  Result, Lit->getLocStart(), SimplifyConditionalReturnDiagnostic,
641  SourceRange(If->getLocStart(), Ret->getLocEnd()), Replacement);
642  return;
643  }
644 
645  BeforeIf = If;
646  }
647  } else {
648  BeforeIf = nullptr;
649  }
650  }
651 }
652 
653 void SimplifyBooleanExprCheck::replaceWithAssignment(
654  const MatchFinder::MatchResult &Result, const IfStmt *IfAssign,
655  bool Negated) {
656  SourceRange Range = IfAssign->getSourceRange();
657  StringRef VariableName =
658  getText(Result, *Result.Nodes.getNodeAs<Expr>(IfAssignVariableId));
659  StringRef Terminator = isa<CompoundStmt>(IfAssign->getElse()) ? ";" : "";
660  std::string Condition =
661  replacementExpression(Result, Negated, IfAssign->getCond());
662  std::string Replacement =
663  (VariableName + " = " + Condition + Terminator).str();
664  SourceLocation Location =
665  Result.Nodes.getNodeAs<CXXBoolLiteralExpr>(IfAssignLocId)->getLocStart();
666  issueDiag(Result, Location,
667  "redundant boolean literal in conditional assignment", Range,
668  Replacement);
669 }
670 
671 } // namespace readability
672 } // namespace tidy
673 } // namespace clang
SourceLocation Loc
&#39;#&#39; location in the include directive
void store(ClangTidyOptions::OptionMap &Options, StringRef LocalName, StringRef Value) const
Stores an option with the check-local name LocalName with string value Value to Options.
Definition: ClangTidy.cpp:449
StringHandle Name
LangOptions getLangOpts() const
Returns the language options from the context.
Definition: ClangTidy.h:187
Visitor(SimplifyBooleanExprCheck *Check, const MatchFinder::MatchResult &Result)
void storeOptions(ClangTidyOptions::OptionMap &Options) override
Should store all options supported by this check with their current values or default values for opti...
Looks for boolean expressions involving boolean constants and simplifies them to use the appropriate ...
Base class for all clang-tidy checks.
Definition: ClangTidy.h:127
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
std::map< std::string, std::string > OptionMap
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
CharSourceRange Range
SourceRange for the file name.
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check&#39;s name.
Definition: ClangTidy.cpp:416