clang-tools  8.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->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  public:
324  const MatchFinder::MatchResult &Result)
325  : Check(Check), Result(Result) {}
326 
327  bool VisitBinaryOperator(BinaryOperator *Op) {
328  Check->reportBinOp(Result, Op);
329  return true;
330  }
331 
332  private:
334  const MatchFinder::MatchResult &Result;
335 };
336 
337 
338 SimplifyBooleanExprCheck::SimplifyBooleanExprCheck(StringRef Name,
339  ClangTidyContext *Context)
340  : ClangTidyCheck(Name, Context),
341  ChainedConditionalReturn(Options.get("ChainedConditionalReturn", 0U)),
342  ChainedConditionalAssignment(
343  Options.get("ChainedConditionalAssignment", 0U)) {}
344 
345 bool containsBoolLiteral(const Expr *E) {
346  if (!E)
347  return false;
348  E = E->IgnoreParenImpCasts();
349  if (isa<CXXBoolLiteralExpr>(E))
350  return true;
351  if (const auto *BinOp = dyn_cast<BinaryOperator>(E))
352  return containsBoolLiteral(BinOp->getLHS()) ||
353  containsBoolLiteral(BinOp->getRHS());
354  if (const auto *UnaryOp = dyn_cast<UnaryOperator>(E))
355  return containsBoolLiteral(UnaryOp->getSubExpr());
356  return false;
357 }
358 
359 void SimplifyBooleanExprCheck::reportBinOp(
360  const MatchFinder::MatchResult &Result, const BinaryOperator *Op) {
361  const auto *LHS = Op->getLHS()->IgnoreParenImpCasts();
362  const auto *RHS = Op->getRHS()->IgnoreParenImpCasts();
363 
364  const CXXBoolLiteralExpr *Bool = nullptr;
365  const Expr *Other = nullptr;
366  if ((Bool = dyn_cast<CXXBoolLiteralExpr>(LHS)))
367  Other = RHS;
368  else if ((Bool = dyn_cast<CXXBoolLiteralExpr>(RHS)))
369  Other = LHS;
370  else
371  return;
372 
373  if (Bool->getBeginLoc().isMacroID())
374  return;
375 
376  // FIXME: why do we need this?
377  if (!isa<CXXBoolLiteralExpr>(Other) && containsBoolLiteral(Other))
378  return;
379 
380  bool BoolValue = Bool->getValue();
381 
382  auto replaceWithExpression = [this, &Result, LHS, RHS, Bool](
383  const Expr *ReplaceWith, bool Negated) {
384  std::string Replacement =
385  replacementExpression(Result, Negated, ReplaceWith);
386  SourceRange Range(LHS->getBeginLoc(), RHS->getEndLoc());
387  issueDiag(Result, Bool->getBeginLoc(), SimplifyOperatorDiagnostic, Range,
388  Replacement);
389  };
390 
391  switch (Op->getOpcode()) {
392  case BO_LAnd:
393  if (BoolValue) {
394  // expr && true -> expr
395  replaceWithExpression(Other, /*Negated=*/false);
396  } else {
397  // expr && false -> false
398  replaceWithExpression(Bool, /*Negated=*/false);
399  }
400  break;
401  case BO_LOr:
402  if (BoolValue) {
403  // expr || true -> true
404  replaceWithExpression(Bool, /*Negated=*/false);
405  } else {
406  // expr || false -> expr
407  replaceWithExpression(Other, /*Negated=*/false);
408  }
409  break;
410  case BO_EQ:
411  // expr == true -> expr, expr == false -> !expr
412  replaceWithExpression(Other, /*Negated=*/!BoolValue);
413  break;
414  case BO_NE:
415  // expr != true -> !expr, expr != false -> expr
416  replaceWithExpression(Other, /*Negated=*/BoolValue);
417  break;
418  default:
419  break;
420  }
421 }
422 
423 void SimplifyBooleanExprCheck::matchBoolCondition(MatchFinder *Finder,
424  bool Value,
425  StringRef BooleanId) {
426  Finder->addMatcher(
427  ifStmt(isExpansionInMainFile(),
428  hasCondition(cxxBoolLiteral(equals(Value)).bind(BooleanId)))
429  .bind(IfStmtId),
430  this);
431 }
432 
433 void SimplifyBooleanExprCheck::matchTernaryResult(MatchFinder *Finder,
434  bool Value,
435  StringRef TernaryId) {
436  Finder->addMatcher(
437  conditionalOperator(isExpansionInMainFile(),
438  hasTrueExpression(cxxBoolLiteral(equals(Value))),
439  hasFalseExpression(cxxBoolLiteral(equals(!Value))))
440  .bind(TernaryId),
441  this);
442 }
443 
444 void SimplifyBooleanExprCheck::matchIfReturnsBool(MatchFinder *Finder,
445  bool Value, StringRef Id) {
446  if (ChainedConditionalReturn)
447  Finder->addMatcher(ifStmt(isExpansionInMainFile(),
448  hasThen(returnsBool(Value, ThenLiteralId)),
449  hasElse(returnsBool(!Value)))
450  .bind(Id),
451  this);
452  else
453  Finder->addMatcher(ifStmt(isExpansionInMainFile(),
454  unless(hasParent(ifStmt())),
455  hasThen(returnsBool(Value, ThenLiteralId)),
456  hasElse(returnsBool(!Value)))
457  .bind(Id),
458  this);
459 }
460 
461 void SimplifyBooleanExprCheck::matchIfAssignsBool(MatchFinder *Finder,
462  bool Value, StringRef Id) {
463  auto SimpleThen = binaryOperator(
464  hasOperatorName("="),
465  hasLHS(declRefExpr(hasDeclaration(decl().bind(IfAssignObjId)))),
466  hasLHS(expr().bind(IfAssignVariableId)),
467  hasRHS(cxxBoolLiteral(equals(Value)).bind(IfAssignLocId)));
468  auto Then = anyOf(SimpleThen, compoundStmt(statementCountIs(1),
469  hasAnySubstatement(SimpleThen)));
470  auto SimpleElse = binaryOperator(
471  hasOperatorName("="),
472  hasLHS(declRefExpr(hasDeclaration(equalsBoundNode(IfAssignObjId)))),
473  hasRHS(cxxBoolLiteral(equals(!Value))));
474  auto Else = anyOf(SimpleElse, compoundStmt(statementCountIs(1),
475  hasAnySubstatement(SimpleElse)));
476  if (ChainedConditionalAssignment)
477  Finder->addMatcher(
478  ifStmt(isExpansionInMainFile(), hasThen(Then), hasElse(Else)).bind(Id),
479  this);
480  else
481  Finder->addMatcher(ifStmt(isExpansionInMainFile(),
482  unless(hasParent(ifStmt())), hasThen(Then),
483  hasElse(Else))
484  .bind(Id),
485  this);
486 }
487 
488 void SimplifyBooleanExprCheck::matchCompoundIfReturnsBool(MatchFinder *Finder,
489  bool Value,
490  StringRef Id) {
491  Finder->addMatcher(
492  compoundStmt(
493  hasAnySubstatement(
494  ifStmt(hasThen(returnsBool(Value)), unless(hasElse(stmt())))),
495  hasAnySubstatement(returnStmt(has(ignoringParenImpCasts(
496  cxxBoolLiteral(equals(!Value)))))
497  .bind(CompoundReturnId)))
498  .bind(Id),
499  this);
500 }
501 
503  Options.store(Opts, "ChainedConditionalReturn", ChainedConditionalReturn);
504  Options.store(Opts, "ChainedConditionalAssignment",
505  ChainedConditionalAssignment);
506 }
507 
509  Finder->addMatcher(translationUnitDecl().bind("top"), this);
510 
511  matchBoolCondition(Finder, true, ConditionThenStmtId);
512  matchBoolCondition(Finder, false, ConditionElseStmtId);
513 
514  matchTernaryResult(Finder, true, TernaryId);
515  matchTernaryResult(Finder, false, TernaryNegatedId);
516 
517  matchIfReturnsBool(Finder, true, IfReturnsBoolId);
518  matchIfReturnsBool(Finder, false, IfReturnsNotBoolId);
519 
520  matchIfAssignsBool(Finder, true, IfAssignBoolId);
521  matchIfAssignsBool(Finder, false, IfAssignNotBoolId);
522 
523  matchCompoundIfReturnsBool(Finder, true, CompoundBoolId);
524  matchCompoundIfReturnsBool(Finder, false, CompoundNotBoolId);
525 }
526 
527 void SimplifyBooleanExprCheck::check(const MatchFinder::MatchResult &Result) {
528  if (Result.Nodes.getNodeAs<TranslationUnitDecl>("top"))
529  Visitor(this, Result).TraverseAST(*Result.Context);
530  else 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 }
560 
561 void SimplifyBooleanExprCheck::issueDiag(
562  const ast_matchers::MatchFinder::MatchResult &Result, SourceLocation Loc,
563  StringRef Description, SourceRange ReplacementRange,
564  StringRef Replacement) {
565  CharSourceRange CharRange =
566  Lexer::makeFileCharRange(CharSourceRange::getTokenRange(ReplacementRange),
567  *Result.SourceManager, getLangOpts());
568 
569  DiagnosticBuilder Diag = diag(Loc, Description);
570  if (!containsDiscardedTokens(Result, CharRange))
571  Diag << FixItHint::CreateReplacement(CharRange, Replacement);
572 }
573 
574 void SimplifyBooleanExprCheck::replaceWithThenStatement(
575  const MatchFinder::MatchResult &Result,
576  const CXXBoolLiteralExpr *TrueConditionRemoved) {
577  const auto *IfStatement = Result.Nodes.getNodeAs<IfStmt>(IfStmtId);
578  issueDiag(Result, TrueConditionRemoved->getBeginLoc(),
579  SimplifyConditionDiagnostic, IfStatement->getSourceRange(),
580  getText(Result, *IfStatement->getThen()));
581 }
582 
583 void SimplifyBooleanExprCheck::replaceWithElseStatement(
584  const MatchFinder::MatchResult &Result,
585  const CXXBoolLiteralExpr *FalseConditionRemoved) {
586  const auto *IfStatement = Result.Nodes.getNodeAs<IfStmt>(IfStmtId);
587  const Stmt *ElseStatement = IfStatement->getElse();
588  issueDiag(Result, FalseConditionRemoved->getBeginLoc(),
589  SimplifyConditionDiagnostic, IfStatement->getSourceRange(),
590  ElseStatement ? getText(Result, *ElseStatement) : "");
591 }
592 
593 void SimplifyBooleanExprCheck::replaceWithCondition(
594  const MatchFinder::MatchResult &Result, const ConditionalOperator *Ternary,
595  bool Negated) {
596  std::string Replacement =
597  replacementExpression(Result, Negated, Ternary->getCond());
598  issueDiag(Result, Ternary->getTrueExpr()->getBeginLoc(),
599  "redundant boolean literal in ternary expression result",
600  Ternary->getSourceRange(), Replacement);
601 }
602 
603 void SimplifyBooleanExprCheck::replaceWithReturnCondition(
604  const MatchFinder::MatchResult &Result, const IfStmt *If, bool Negated) {
605  StringRef Terminator = isa<CompoundStmt>(If->getElse()) ? ";" : "";
606  std::string Condition = replacementExpression(Result, Negated, If->getCond());
607  std::string Replacement = ("return " + Condition + Terminator).str();
608  SourceLocation Start =
609  Result.Nodes.getNodeAs<CXXBoolLiteralExpr>(ThenLiteralId)->getBeginLoc();
610  issueDiag(Result, Start, SimplifyConditionalReturnDiagnostic,
611  If->getSourceRange(), Replacement);
612 }
613 
614 void SimplifyBooleanExprCheck::replaceCompoundReturnWithCondition(
615  const MatchFinder::MatchResult &Result, const CompoundStmt *Compound,
616  bool Negated) {
617  const auto *Ret = Result.Nodes.getNodeAs<ReturnStmt>(CompoundReturnId);
618 
619  // The body shouldn't be empty because the matcher ensures that it must
620  // contain at least two statements:
621  // 1) A `return` statement returning a boolean literal `false` or `true`
622  // 2) An `if` statement with no `else` clause that consists of a single
623  // `return` statement returning the opposite boolean literal `true` or
624  // `false`.
625  assert(Compound->size() >= 2);
626  const IfStmt *BeforeIf = nullptr;
627  CompoundStmt::const_body_iterator Current = Compound->body_begin();
628  CompoundStmt::const_body_iterator After = Compound->body_begin();
629  for (++After; After != Compound->body_end() && *Current != Ret;
630  ++Current, ++After) {
631  if (const auto *If = dyn_cast<IfStmt>(*Current)) {
632  if (const CXXBoolLiteralExpr *Lit = stmtReturnsBool(If, Negated)) {
633  if (*After == Ret) {
634  if (!ChainedConditionalReturn && BeforeIf)
635  continue;
636 
637  const Expr *Condition = If->getCond();
638  std::string Replacement =
639  "return " + replacementExpression(Result, Negated, Condition);
640  issueDiag(
641  Result, Lit->getBeginLoc(), SimplifyConditionalReturnDiagnostic,
642  SourceRange(If->getBeginLoc(), Ret->getEndLoc()), Replacement);
643  return;
644  }
645 
646  BeforeIf = If;
647  }
648  } else {
649  BeforeIf = nullptr;
650  }
651  }
652 }
653 
654 void SimplifyBooleanExprCheck::replaceWithAssignment(
655  const MatchFinder::MatchResult &Result, const IfStmt *IfAssign,
656  bool Negated) {
657  SourceRange Range = IfAssign->getSourceRange();
658  StringRef VariableName =
659  getText(Result, *Result.Nodes.getNodeAs<Expr>(IfAssignVariableId));
660  StringRef Terminator = isa<CompoundStmt>(IfAssign->getElse()) ? ";" : "";
661  std::string Condition =
662  replacementExpression(Result, Negated, IfAssign->getCond());
663  std::string Replacement =
664  (VariableName + " = " + Condition + Terminator).str();
665  SourceLocation Location =
666  Result.Nodes.getNodeAs<CXXBoolLiteralExpr>(IfAssignLocId)->getBeginLoc();
667  issueDiag(Result, Location,
668  "redundant boolean literal in conditional assignment", Range,
669  Replacement);
670 }
671 
672 } // namespace readability
673 } // namespace tidy
674 } // 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:473
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.
static constexpr llvm::StringLiteral Name
std::map< std::string, std::string > OptionMap
llvm::Optional< llvm::Expected< tooling::AtomicChanges > > Result
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
std::string Condition
Condition used after the preprocessor directive.
CharSourceRange Range
SourceRange for the file name.
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
const char * Description
Definition: Dexp.cpp:258
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check&#39;s name.
Definition: ClangTidy.cpp:438