clang-tools  5.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 &&
66  Result.SourceManager->isMacroBodyExpansion(Literal->getLocStart()))
67  ? nullptr
68  : Literal;
69 }
70 
71 internal::Matcher<Stmt> returnsBool(bool Value, StringRef Id = "ignored") {
72  auto SimpleReturnsBool =
73  returnStmt(has(cxxBoolLiteral(equals(Value)).bind(Id)))
74  .bind("returns-bool");
75  return anyOf(SimpleReturnsBool,
76  compoundStmt(statementCountIs(1), has(SimpleReturnsBool)));
77 }
78 
79 bool needsParensAfterUnaryNegation(const Expr *E) {
80  E = E->IgnoreImpCasts();
81  if (isa<BinaryOperator>(E) || isa<ConditionalOperator>(E))
82  return true;
83 
84  if (const auto *Op = dyn_cast<CXXOperatorCallExpr>(E))
85  return Op->getNumArgs() == 2 && Op->getOperator() != OO_Call &&
86  Op->getOperator() != OO_Subscript;
87 
88  return false;
89 }
90 
91 std::pair<BinaryOperatorKind, BinaryOperatorKind> Opposites[] = {
92  {BO_LT, BO_GE}, {BO_GT, BO_LE}, {BO_EQ, BO_NE}};
93 
94 StringRef negatedOperator(const BinaryOperator *BinOp) {
95  const BinaryOperatorKind Opcode = BinOp->getOpcode();
96  for (auto NegatableOp : Opposites) {
97  if (Opcode == NegatableOp.first)
98  return BinOp->getOpcodeStr(NegatableOp.second);
99  if (Opcode == NegatableOp.second)
100  return BinOp->getOpcodeStr(NegatableOp.first);
101  }
102  return StringRef();
103 }
104 
105 std::pair<OverloadedOperatorKind, StringRef> OperatorNames[] = {
106  {OO_EqualEqual, "=="}, {OO_ExclaimEqual, "!="}, {OO_Less, "<"},
107  {OO_GreaterEqual, ">="}, {OO_Greater, ">"}, {OO_LessEqual, "<="}};
108 
109 StringRef getOperatorName(OverloadedOperatorKind OpKind) {
110  for (auto Name : OperatorNames) {
111  if (Name.first == OpKind)
112  return Name.second;
113  }
114 
115  return StringRef();
116 }
117 
118 std::pair<OverloadedOperatorKind, OverloadedOperatorKind> OppositeOverloads[] =
119  {{OO_EqualEqual, OO_ExclaimEqual},
120  {OO_Less, OO_GreaterEqual},
121  {OO_Greater, OO_LessEqual}};
122 
123 StringRef negatedOperator(const CXXOperatorCallExpr *OpCall) {
124  const OverloadedOperatorKind Opcode = OpCall->getOperator();
125  for (auto NegatableOp : OppositeOverloads) {
126  if (Opcode == NegatableOp.first)
127  return getOperatorName(NegatableOp.second);
128  if (Opcode == NegatableOp.second)
129  return getOperatorName(NegatableOp.first);
130  }
131  return StringRef();
132 }
133 
134 std::string asBool(StringRef text, bool NeedsStaticCast) {
135  if (NeedsStaticCast)
136  return ("static_cast<bool>(" + text + ")").str();
137 
138  return text;
139 }
140 
141 bool needsNullPtrComparison(const Expr *E) {
142  if (const auto *ImpCast = dyn_cast<ImplicitCastExpr>(E))
143  return ImpCast->getCastKind() == CK_PointerToBoolean ||
144  ImpCast->getCastKind() == CK_MemberPointerToBoolean;
145 
146  return false;
147 }
148 
149 bool needsZeroComparison(const Expr *E) {
150  if (const auto *ImpCast = dyn_cast<ImplicitCastExpr>(E))
151  return ImpCast->getCastKind() == CK_IntegralToBoolean;
152 
153  return false;
154 }
155 
156 bool needsStaticCast(const Expr *E) {
157  if (const auto *ImpCast = dyn_cast<ImplicitCastExpr>(E)) {
158  if (ImpCast->getCastKind() == CK_UserDefinedConversion &&
159  ImpCast->getSubExpr()->getType()->isBooleanType()) {
160  if (const auto *MemCall =
161  dyn_cast<CXXMemberCallExpr>(ImpCast->getSubExpr())) {
162  if (const auto *MemDecl =
163  dyn_cast<CXXConversionDecl>(MemCall->getMethodDecl())) {
164  if (MemDecl->isExplicit())
165  return true;
166  }
167  }
168  }
169  }
170 
171  E = E->IgnoreImpCasts();
172  return !E->getType()->isBooleanType();
173 }
174 
175 std::string compareExpressionToConstant(const MatchFinder::MatchResult &Result,
176  const Expr *E, bool Negated,
177  const char *Constant) {
178  E = E->IgnoreImpCasts();
179  const std::string ExprText =
180  (isa<BinaryOperator>(E) ? ("(" + getText(Result, *E) + ")")
181  : getText(Result, *E))
182  .str();
183  return ExprText + " " + (Negated ? "!=" : "==") + " " + Constant;
184 }
185 
186 std::string compareExpressionToNullPtr(const MatchFinder::MatchResult &Result,
187  const Expr *E, bool Negated) {
188  const char *NullPtr =
189  Result.Context->getLangOpts().CPlusPlus11 ? "nullptr" : "NULL";
190  return compareExpressionToConstant(Result, E, Negated, NullPtr);
191 }
192 
193 std::string compareExpressionToZero(const MatchFinder::MatchResult &Result,
194  const Expr *E, bool Negated) {
195  return compareExpressionToConstant(Result, E, Negated, "0");
196 }
197 
198 std::string replacementExpression(const MatchFinder::MatchResult &Result,
199  bool Negated, const Expr *E) {
200  E = E->ignoreParenBaseCasts();
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:
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:
336  const MatchFinder::MatchResult &Result;
337 };
338 
339 
340 SimplifyBooleanExprCheck::SimplifyBooleanExprCheck(StringRef Name,
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->getLocStart().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->getLocStart(), RHS->getLocEnd());
389  issueDiag(Result, Bool->getLocStart(), 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(allOf(hasAnySubstatement(ifStmt(hasThen(returnsBool(Value)),
495  unless(hasElse(stmt())))),
496  hasAnySubstatement(
497  returnStmt(has(ignoringParenImpCasts(
498  cxxBoolLiteral(equals(!Value)))))
499  .bind(CompoundReturnId))))
500  .bind(Id),
501  this);
502 }
503 
505  Options.store(Opts, "ChainedConditionalReturn", ChainedConditionalReturn);
506  Options.store(Opts, "ChainedConditionalAssignment",
507  ChainedConditionalAssignment);
508 }
509 
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 (const CXXBoolLiteralExpr *TrueConditionRemoved =
531  getBoolLiteral(Result, ConditionThenStmtId))
532  replaceWithThenStatement(Result, TrueConditionRemoved);
533  else if (const CXXBoolLiteralExpr *FalseConditionRemoved =
534  getBoolLiteral(Result, ConditionElseStmtId))
535  replaceWithElseStatement(Result, FalseConditionRemoved);
536  else if (const auto *Ternary =
537  Result.Nodes.getNodeAs<ConditionalOperator>(TernaryId))
538  replaceWithCondition(Result, Ternary);
539  else if (const auto *TernaryNegated =
540  Result.Nodes.getNodeAs<ConditionalOperator>(TernaryNegatedId))
541  replaceWithCondition(Result, TernaryNegated, true);
542  else if (const auto *If = Result.Nodes.getNodeAs<IfStmt>(IfReturnsBoolId))
543  replaceWithReturnCondition(Result, If);
544  else if (const auto *IfNot =
545  Result.Nodes.getNodeAs<IfStmt>(IfReturnsNotBoolId))
546  replaceWithReturnCondition(Result, IfNot, true);
547  else if (const auto *IfAssign =
548  Result.Nodes.getNodeAs<IfStmt>(IfAssignBoolId))
549  replaceWithAssignment(Result, IfAssign);
550  else if (const auto *IfAssignNot =
551  Result.Nodes.getNodeAs<IfStmt>(IfAssignNotBoolId))
552  replaceWithAssignment(Result, IfAssignNot, true);
553  else if (const auto *Compound =
554  Result.Nodes.getNodeAs<CompoundStmt>(CompoundBoolId))
555  replaceCompoundReturnWithCondition(Result, Compound);
556  else if (const auto *Compound =
557  Result.Nodes.getNodeAs<CompoundStmt>(CompoundNotBoolId))
558  replaceCompoundReturnWithCondition(Result, Compound, true);
559  else if (const auto TU = Result.Nodes.getNodeAs<Decl>("top"))
560  Visitor(this, Result).TraverseDecl(const_cast<Decl*>(TU));
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->getLocStart(),
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->getLocStart(),
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()->getLocStart(),
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)->getLocStart();
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->getLocStart(), SimplifyConditionalReturnDiagnostic,
644  SourceRange(If->getLocStart(), Ret->getLocEnd()), 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)->getLocStart();
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
SourceLocation Loc
'#' location in the include directive
SetLongJmpCheck & Check
LangOptions getLangOpts() const
Returns the language options from the context.
Definition: ClangTidy.h:187
StringHandle Name
std::unique_ptr< ast_matchers::MatchFinder > Finder
Definition: ClangTidy.cpp:275
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.
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
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.
ClangTidyContext & Context
Definition: ClangTidy.cpp:87
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's name.
Definition: ClangTidy.cpp:416