clang-tools  3.8.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/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 RightExpressionId[] = "bool-op-expr-yields-expr";
37 const char LeftExpressionId[] = "expr-op-bool-yields-expr";
38 const char NegatedRightExpressionId[] = "bool-op-expr-yields-not-expr";
39 const char NegatedLeftExpressionId[] = "expr-op-bool-yields-not-expr";
40 const char ConditionThenStmtId[] = "if-bool-yields-then";
41 const char ConditionElseStmtId[] = "if-bool-yields-else";
42 const char TernaryId[] = "ternary-bool-yields-condition";
43 const char TernaryNegatedId[] = "ternary-bool-yields-not-condition";
44 const char IfReturnsBoolId[] = "if-return";
45 const char IfReturnsNotBoolId[] = "if-not-return";
46 const char ThenLiteralId[] = "then-literal";
47 const char IfAssignVariableId[] = "if-assign-lvalue";
48 const char IfAssignLocId[] = "if-assign-loc";
49 const char IfAssignBoolId[] = "if-assign";
50 const char IfAssignNotBoolId[] = "if-assign-not";
51 const char IfAssignObjId[] = "if-assign-obj";
52 const char CompoundReturnId[] = "compound-return";
53 const char CompoundBoolId[] = "compound-bool";
54 const char CompoundNotBoolId[] = "compound-bool-not";
55 
56 const char IfStmtId[] = "if";
57 const char LHSId[] = "lhs-expr";
58 const char RHSId[] = "rhs-expr";
59 
60 const char SimplifyOperatorDiagnostic[] =
61  "redundant boolean literal supplied to boolean operator";
62 const char SimplifyConditionDiagnostic[] =
63  "redundant boolean literal in if statement condition";
64 const char SimplifyConditionalReturnDiagnostic[] =
65  "redundant boolean literal in conditional return statement";
66 
67 const CXXBoolLiteralExpr *getBoolLiteral(const MatchFinder::MatchResult &Result,
68  StringRef Id) {
69  const auto *Literal = Result.Nodes.getNodeAs<CXXBoolLiteralExpr>(Id);
70  return (Literal &&
71  Result.SourceManager->isMacroBodyExpansion(Literal->getLocStart()))
72  ? nullptr
73  : Literal;
74 }
75 
76 internal::Matcher<Stmt> returnsBool(bool Value, StringRef Id = "ignored") {
77  auto SimpleReturnsBool =
78  returnStmt(has(cxxBoolLiteral(equals(Value)).bind(Id)))
79  .bind("returns-bool");
80  return anyOf(SimpleReturnsBool,
81  compoundStmt(statementCountIs(1), has(SimpleReturnsBool)));
82 }
83 
84 bool needsParensAfterUnaryNegation(const Expr *E) {
85  E = E->IgnoreImpCasts();
86  if (isa<BinaryOperator>(E) || isa<ConditionalOperator>(E))
87  return true;
88  if (const auto *Op = dyn_cast<CXXOperatorCallExpr>(E)) {
89  return Op->getNumArgs() == 2 && Op->getOperator() != OO_Call &&
90  Op->getOperator() != OO_Subscript;
91  }
92  return false;
93 }
94 
95 std::pair<BinaryOperatorKind, BinaryOperatorKind> Opposites[] = {
96  {BO_LT, BO_GE}, {BO_GT, BO_LE}, {BO_EQ, BO_NE}};
97 
98 StringRef negatedOperator(const BinaryOperator *BinOp) {
99  const BinaryOperatorKind Opcode = BinOp->getOpcode();
100  for (auto NegatableOp : Opposites) {
101  if (Opcode == NegatableOp.first)
102  return BinOp->getOpcodeStr(NegatableOp.second);
103  if (Opcode == NegatableOp.second)
104  return BinOp->getOpcodeStr(NegatableOp.first);
105  }
106  return StringRef();
107 }
108 
109 std::pair<OverloadedOperatorKind, StringRef> OperatorNames[] = {
110  {OO_EqualEqual, "=="},
111  {OO_ExclaimEqual, "!="},
112  {OO_Less, "<"},
113  {OO_GreaterEqual, ">="},
114  {OO_Greater, ">"},
115  {OO_LessEqual, "<="}};
116 
117 StringRef getOperatorName(OverloadedOperatorKind OpKind) {
118  for (auto Name : OperatorNames) {
119  if (Name.first == OpKind)
120  return Name.second;
121  }
122 
123  return StringRef();
124 }
125 
126 std::pair<OverloadedOperatorKind, OverloadedOperatorKind> OppositeOverloads[] =
127  {{OO_EqualEqual, OO_ExclaimEqual},
128  {OO_Less, OO_GreaterEqual},
129  {OO_Greater, OO_LessEqual}};
130 
131 StringRef negatedOperator(const CXXOperatorCallExpr *OpCall) {
132  const OverloadedOperatorKind Opcode = OpCall->getOperator();
133  for (auto NegatableOp : OppositeOverloads) {
134  if (Opcode == NegatableOp.first)
135  return getOperatorName(NegatableOp.second);
136  if (Opcode == NegatableOp.second)
137  return getOperatorName(NegatableOp.first);
138  }
139  return StringRef();
140 }
141 
142 std::string asBool(StringRef text, bool NeedsStaticCast) {
143  if (NeedsStaticCast)
144  return ("static_cast<bool>(" + text + ")").str();
145 
146  return text;
147 }
148 
149 bool needsNullPtrComparison(const Expr *E) {
150  if (const auto *ImpCast = dyn_cast<ImplicitCastExpr>(E))
151  return ImpCast->getCastKind() == CK_PointerToBoolean;
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 replacementExpression(const MatchFinder::MatchResult &Result,
176  bool Negated, const Expr *E) {
177  E = E->ignoreParenBaseCasts();
178  const bool NeedsStaticCast = needsStaticCast(E);
179  if (Negated) {
180  if (const auto *UnOp = dyn_cast<UnaryOperator>(E)) {
181  if (UnOp->getOpcode() == UO_LNot) {
182  if (needsNullPtrComparison(UnOp->getSubExpr()))
183  return (getText(Result, *UnOp->getSubExpr()) + " != nullptr").str();
184 
185  return replacementExpression(Result, false, UnOp->getSubExpr());
186  }
187  }
188 
189  if (needsNullPtrComparison(E))
190  return (getText(Result, *E) + " == nullptr").str();
191 
192  StringRef NegatedOperator;
193  const Expr *LHS = nullptr;
194  const Expr *RHS = nullptr;
195  if (const auto *BinOp = dyn_cast<BinaryOperator>(E)) {
196  NegatedOperator = negatedOperator(BinOp);
197  LHS = BinOp->getLHS();
198  RHS = BinOp->getRHS();
199  } else if (const auto *OpExpr = dyn_cast<CXXOperatorCallExpr>(E)) {
200  if (OpExpr->getNumArgs() == 2) {
201  NegatedOperator = negatedOperator(OpExpr);
202  LHS = OpExpr->getArg(0);
203  RHS = OpExpr->getArg(1);
204  }
205  }
206  if (!NegatedOperator.empty() && LHS && RHS) {
207  return (asBool((getText(Result, *LHS) + " " + NegatedOperator + " " +
208  getText(Result, *RHS)).str(),
209  NeedsStaticCast));
210  }
211 
212  StringRef Text = getText(Result, *E);
213  if (!NeedsStaticCast && needsParensAfterUnaryNegation(E))
214  return ("!(" + Text + ")").str();
215 
216  if (needsNullPtrComparison(E))
217  return (getText(Result, *E) + " == nullptr").str();
218 
219  return ("!" + asBool(Text, NeedsStaticCast));
220  }
221 
222  if (const auto *UnOp = dyn_cast<UnaryOperator>(E)) {
223  if (UnOp->getOpcode() == UO_LNot) {
224  if (needsNullPtrComparison(UnOp->getSubExpr()))
225  return (getText(Result, *UnOp->getSubExpr()) + " == nullptr").str();
226  }
227  }
228 
229  if (needsNullPtrComparison(E))
230  return (getText(Result, *E) + " != nullptr").str();
231 
232  return asBool(getText(Result, *E), NeedsStaticCast);
233 }
234 
235 const CXXBoolLiteralExpr *stmtReturnsBool(const ReturnStmt *Ret, bool Negated) {
236  if (const auto *Bool = dyn_cast<CXXBoolLiteralExpr>(Ret->getRetValue())) {
237  if (Bool->getValue() == !Negated)
238  return Bool;
239  }
240 
241  return nullptr;
242 }
243 
244 const CXXBoolLiteralExpr *stmtReturnsBool(const IfStmt *IfRet, bool Negated) {
245  if (IfRet->getElse() != nullptr)
246  return nullptr;
247 
248  if (const auto *Ret = dyn_cast<ReturnStmt>(IfRet->getThen()))
249  return stmtReturnsBool(Ret, Negated);
250 
251  if (const auto *Compound = dyn_cast<CompoundStmt>(IfRet->getThen())) {
252  if (Compound->size() == 1) {
253  if (const auto *CompoundRet = dyn_cast<ReturnStmt>(Compound->body_back()))
254  return stmtReturnsBool(CompoundRet, Negated);
255  }
256  }
257 
258  return nullptr;
259 }
260 
261 } // namespace
262 
263 SimplifyBooleanExprCheck::SimplifyBooleanExprCheck(StringRef Name,
265  : ClangTidyCheck(Name, Context),
266  ChainedConditionalReturn(Options.get("ChainedConditionalReturn", 0U)),
267  ChainedConditionalAssignment(
268  Options.get("ChainedConditionalAssignment", 0U)) {}
269 
270 void SimplifyBooleanExprCheck::matchBoolBinOpExpr(MatchFinder *Finder,
271  bool Value,
272  StringRef OperatorName,
273  StringRef BooleanId) {
274  Finder->addMatcher(
275  binaryOperator(
276  isExpansionInMainFile(), hasOperatorName(OperatorName),
277  hasLHS(allOf(expr().bind(LHSId),
278  cxxBoolLiteral(equals(Value)).bind(BooleanId))),
279  hasRHS(expr().bind(RHSId)),
280  unless(hasRHS(hasDescendant(cxxBoolLiteral())))),
281  this);
282 }
283 
284 void SimplifyBooleanExprCheck::matchExprBinOpBool(MatchFinder *Finder,
285  bool Value,
286  StringRef OperatorName,
287  StringRef BooleanId) {
288  Finder->addMatcher(
289  binaryOperator(
290  isExpansionInMainFile(), hasOperatorName(OperatorName),
291  hasLHS(expr().bind(LHSId)),
292  unless(
293  hasLHS(anyOf(cxxBoolLiteral(), hasDescendant(cxxBoolLiteral())))),
294  hasRHS(allOf(expr().bind(RHSId),
295  cxxBoolLiteral(equals(Value)).bind(BooleanId)))),
296  this);
297 }
298 
299 void SimplifyBooleanExprCheck::matchBoolCompOpExpr(MatchFinder *Finder,
300  bool Value,
301  StringRef OperatorName,
302  StringRef BooleanId) {
303  Finder->addMatcher(
304  binaryOperator(isExpansionInMainFile(), hasOperatorName(OperatorName),
305  hasLHS(allOf(expr().bind(LHSId),
306  ignoringImpCasts(cxxBoolLiteral(equals(Value))
307  .bind(BooleanId)))),
308  hasRHS(expr().bind(RHSId)),
309  unless(hasRHS(hasDescendant(cxxBoolLiteral())))),
310  this);
311 }
312 
313 void SimplifyBooleanExprCheck::matchExprCompOpBool(MatchFinder *Finder,
314  bool Value,
315  StringRef OperatorName,
316  StringRef BooleanId) {
317  Finder->addMatcher(
318  binaryOperator(isExpansionInMainFile(), hasOperatorName(OperatorName),
319  unless(hasLHS(hasDescendant(cxxBoolLiteral()))),
320  hasLHS(expr().bind(LHSId)),
321  hasRHS(allOf(expr().bind(RHSId),
322  ignoringImpCasts(cxxBoolLiteral(equals(Value))
323  .bind(BooleanId))))),
324  this);
325 }
326 
327 void SimplifyBooleanExprCheck::matchBoolCondition(MatchFinder *Finder,
328  bool Value,
329  StringRef BooleanId) {
330  Finder->addMatcher(ifStmt(isExpansionInMainFile(),
331  hasCondition(cxxBoolLiteral(equals(Value))
332  .bind(BooleanId))).bind(IfStmtId),
333  this);
334 }
335 
336 void SimplifyBooleanExprCheck::matchTernaryResult(MatchFinder *Finder,
337  bool Value,
338  StringRef TernaryId) {
339  Finder->addMatcher(
340  conditionalOperator(isExpansionInMainFile(),
341  hasTrueExpression(cxxBoolLiteral(equals(Value))),
342  hasFalseExpression(cxxBoolLiteral(equals(!Value))))
343  .bind(TernaryId),
344  this);
345 }
346 
347 void SimplifyBooleanExprCheck::matchIfReturnsBool(MatchFinder *Finder,
348  bool Value, StringRef Id) {
349  if (ChainedConditionalReturn) {
350  Finder->addMatcher(ifStmt(isExpansionInMainFile(),
351  hasThen(returnsBool(Value, ThenLiteralId)),
352  hasElse(returnsBool(!Value))).bind(Id),
353  this);
354  } else {
355  Finder->addMatcher(ifStmt(isExpansionInMainFile(),
356  unless(hasParent(ifStmt())),
357  hasThen(returnsBool(Value, ThenLiteralId)),
358  hasElse(returnsBool(!Value))).bind(Id),
359  this);
360  }
361 }
362 
363 void SimplifyBooleanExprCheck::matchIfAssignsBool(MatchFinder *Finder,
364  bool Value, StringRef Id) {
365  auto SimpleThen = binaryOperator(
366  hasOperatorName("="),
367  hasLHS(declRefExpr(hasDeclaration(decl().bind(IfAssignObjId)))),
368  hasLHS(expr().bind(IfAssignVariableId)),
369  hasRHS(cxxBoolLiteral(equals(Value)).bind(IfAssignLocId)));
370  auto Then = anyOf(SimpleThen, compoundStmt(statementCountIs(1),
371  hasAnySubstatement(SimpleThen)));
372  auto SimpleElse = binaryOperator(
373  hasOperatorName("="),
374  hasLHS(declRefExpr(hasDeclaration(equalsBoundNode(IfAssignObjId)))),
375  hasRHS(cxxBoolLiteral(equals(!Value))));
376  auto Else = anyOf(SimpleElse, compoundStmt(statementCountIs(1),
377  hasAnySubstatement(SimpleElse)));
378  if (ChainedConditionalAssignment) {
379  Finder->addMatcher(
380  ifStmt(isExpansionInMainFile(), hasThen(Then), hasElse(Else)).bind(Id),
381  this);
382  } else {
383  Finder->addMatcher(ifStmt(isExpansionInMainFile(),
384  unless(hasParent(ifStmt())), hasThen(Then),
385  hasElse(Else)).bind(Id),
386  this);
387  }
388 }
389 
390 void SimplifyBooleanExprCheck::matchCompoundIfReturnsBool(MatchFinder *Finder,
391  bool Value,
392  StringRef Id) {
393  Finder->addMatcher(
394  compoundStmt(allOf(hasAnySubstatement(ifStmt(hasThen(returnsBool(Value)),
395  unless(hasElse(stmt())))),
396  hasAnySubstatement(
397  returnStmt(has(cxxBoolLiteral(equals(!Value))))
398  .bind(CompoundReturnId)))).bind(Id),
399  this);
400 }
401 
403  Options.store(Opts, "ChainedConditionalReturn", ChainedConditionalReturn);
404  Options.store(Opts, "ChainedConditionalAssignment",
405  ChainedConditionalAssignment);
406 }
407 
409  matchBoolBinOpExpr(Finder, true, "&&", RightExpressionId);
410  matchBoolBinOpExpr(Finder, false, "||", RightExpressionId);
411  matchExprBinOpBool(Finder, false, "&&", RightExpressionId);
412  matchExprBinOpBool(Finder, true, "||", RightExpressionId);
413  matchBoolCompOpExpr(Finder, true, "==", RightExpressionId);
414  matchBoolCompOpExpr(Finder, false, "!=", RightExpressionId);
415 
416  matchExprBinOpBool(Finder, true, "&&", LeftExpressionId);
417  matchExprBinOpBool(Finder, false, "||", LeftExpressionId);
418  matchBoolBinOpExpr(Finder, false, "&&", LeftExpressionId);
419  matchBoolBinOpExpr(Finder, true, "||", LeftExpressionId);
420  matchExprCompOpBool(Finder, true, "==", LeftExpressionId);
421  matchExprCompOpBool(Finder, false, "!=", LeftExpressionId);
422 
423  matchBoolCompOpExpr(Finder, false, "==", NegatedRightExpressionId);
424  matchBoolCompOpExpr(Finder, true, "!=", NegatedRightExpressionId);
425 
426  matchExprCompOpBool(Finder, false, "==", NegatedLeftExpressionId);
427  matchExprCompOpBool(Finder, true, "!=", NegatedLeftExpressionId);
428 
429  matchBoolCondition(Finder, true, ConditionThenStmtId);
430  matchBoolCondition(Finder, false, ConditionElseStmtId);
431 
432  matchTernaryResult(Finder, true, TernaryId);
433  matchTernaryResult(Finder, false, TernaryNegatedId);
434 
435  matchIfReturnsBool(Finder, true, IfReturnsBoolId);
436  matchIfReturnsBool(Finder, false, IfReturnsNotBoolId);
437 
438  matchIfAssignsBool(Finder, true, IfAssignBoolId);
439  matchIfAssignsBool(Finder, false, IfAssignNotBoolId);
440 
441  matchCompoundIfReturnsBool(Finder, true, CompoundBoolId);
442  matchCompoundIfReturnsBool(Finder, false, CompoundNotBoolId);
443 }
444 
445 void SimplifyBooleanExprCheck::check(const MatchFinder::MatchResult &Result) {
446  if (const CXXBoolLiteralExpr *LeftRemoved =
447  getBoolLiteral(Result, RightExpressionId)) {
448  replaceWithExpression(Result, LeftRemoved, false);
449  } else if (const CXXBoolLiteralExpr *RightRemoved =
450  getBoolLiteral(Result, LeftExpressionId)) {
451  replaceWithExpression(Result, RightRemoved, true);
452  } else if (const CXXBoolLiteralExpr *NegatedLeftRemoved =
453  getBoolLiteral(Result, NegatedRightExpressionId)) {
454  replaceWithExpression(Result, NegatedLeftRemoved, false, true);
455  } else if (const CXXBoolLiteralExpr *NegatedRightRemoved =
456  getBoolLiteral(Result, NegatedLeftExpressionId)) {
457  replaceWithExpression(Result, NegatedRightRemoved, true, true);
458  } else if (const CXXBoolLiteralExpr *TrueConditionRemoved =
459  getBoolLiteral(Result, ConditionThenStmtId)) {
460  replaceWithThenStatement(Result, TrueConditionRemoved);
461  } else if (const CXXBoolLiteralExpr *FalseConditionRemoved =
462  getBoolLiteral(Result, ConditionElseStmtId)) {
463  replaceWithElseStatement(Result, FalseConditionRemoved);
464  } else if (const auto *Ternary =
465  Result.Nodes.getNodeAs<ConditionalOperator>(TernaryId)) {
466  replaceWithCondition(Result, Ternary);
467  } else if (const auto *TernaryNegated =
468  Result.Nodes.getNodeAs<ConditionalOperator>(
469  TernaryNegatedId)) {
470  replaceWithCondition(Result, TernaryNegated, true);
471  } else if (const auto *If = Result.Nodes.getNodeAs<IfStmt>(IfReturnsBoolId)) {
472  replaceWithReturnCondition(Result, If);
473  } else if (const auto *IfNot =
474  Result.Nodes.getNodeAs<IfStmt>(IfReturnsNotBoolId)) {
475  replaceWithReturnCondition(Result, IfNot, true);
476  } else if (const auto *IfAssign =
477  Result.Nodes.getNodeAs<IfStmt>(IfAssignBoolId)) {
478  replaceWithAssignment(Result, IfAssign);
479  } else if (const auto *IfAssignNot =
480  Result.Nodes.getNodeAs<IfStmt>(IfAssignNotBoolId)) {
481  replaceWithAssignment(Result, IfAssignNot, true);
482  } else if (const auto *Compound =
483  Result.Nodes.getNodeAs<CompoundStmt>(CompoundBoolId)) {
484  replaceCompoundReturnWithCondition(Result, Compound);
485  } else if (const auto *Compound =
486  Result.Nodes.getNodeAs<CompoundStmt>(CompoundNotBoolId)) {
487  replaceCompoundReturnWithCondition(Result, Compound, true);
488  }
489 }
490 
492  const ast_matchers::MatchFinder::MatchResult &Result,
493  CharSourceRange CharRange) {
494  std::string ReplacementText =
495  Lexer::getSourceText(CharRange, *Result.SourceManager,
496  Result.Context->getLangOpts())
497  .str();
498  Lexer Lex(CharRange.getBegin(), Result.Context->getLangOpts(),
499  ReplacementText.data(), ReplacementText.data(),
500  ReplacementText.data() + ReplacementText.size());
501  Lex.SetCommentRetentionState(true);
502  Token Tok;
503 
504  while (!Lex.LexFromRawLexer(Tok)) {
505  if (Tok.is(tok::TokenKind::comment) || Tok.is(tok::TokenKind::hash))
506  return true;
507  }
508 
509  return false;
510 }
511 
512 void SimplifyBooleanExprCheck::issueDiag(
513  const ast_matchers::MatchFinder::MatchResult &Result, SourceLocation Loc,
514  StringRef Description, SourceRange ReplacementRange,
515  StringRef Replacement) {
516  CharSourceRange CharRange = Lexer::makeFileCharRange(
517  CharSourceRange::getTokenRange(ReplacementRange), *Result.SourceManager,
518  Result.Context->getLangOpts());
519 
520  DiagnosticBuilder Diag = diag(Loc, Description);
521  if (!containsDiscardedTokens(Result, CharRange))
522  Diag << FixItHint::CreateReplacement(CharRange, Replacement);
523 }
524 
525 void SimplifyBooleanExprCheck::replaceWithExpression(
526  const ast_matchers::MatchFinder::MatchResult &Result,
527  const CXXBoolLiteralExpr *BoolLiteral, bool UseLHS, bool Negated) {
528  const auto *LHS = Result.Nodes.getNodeAs<Expr>(LHSId);
529  const auto *RHS = Result.Nodes.getNodeAs<Expr>(RHSId);
530  std::string Replacement =
531  replacementExpression(Result, Negated, UseLHS ? LHS : RHS);
532  SourceRange Range(LHS->getLocStart(), RHS->getLocEnd());
533  issueDiag(Result, BoolLiteral->getLocStart(), SimplifyOperatorDiagnostic,
534  Range, Replacement);
535 }
536 
537 void SimplifyBooleanExprCheck::replaceWithThenStatement(
538  const MatchFinder::MatchResult &Result,
539  const CXXBoolLiteralExpr *TrueConditionRemoved) {
540  const auto *IfStatement = Result.Nodes.getNodeAs<IfStmt>(IfStmtId);
541  issueDiag(Result, TrueConditionRemoved->getLocStart(),
542  SimplifyConditionDiagnostic, IfStatement->getSourceRange(),
543  getText(Result, *IfStatement->getThen()));
544 }
545 
546 void SimplifyBooleanExprCheck::replaceWithElseStatement(
547  const MatchFinder::MatchResult &Result,
548  const CXXBoolLiteralExpr *FalseConditionRemoved) {
549  const auto *IfStatement = Result.Nodes.getNodeAs<IfStmt>(IfStmtId);
550  const Stmt *ElseStatement = IfStatement->getElse();
551  issueDiag(Result, FalseConditionRemoved->getLocStart(),
552  SimplifyConditionDiagnostic, IfStatement->getSourceRange(),
553  ElseStatement ? getText(Result, *ElseStatement) : "");
554 }
555 
556 void SimplifyBooleanExprCheck::replaceWithCondition(
557  const MatchFinder::MatchResult &Result, const ConditionalOperator *Ternary,
558  bool Negated) {
559  std::string Replacement =
560  replacementExpression(Result, Negated, Ternary->getCond());
561  issueDiag(Result, Ternary->getTrueExpr()->getLocStart(),
562  "redundant boolean literal in ternary expression result",
563  Ternary->getSourceRange(), Replacement);
564 }
565 
566 void SimplifyBooleanExprCheck::replaceWithReturnCondition(
567  const MatchFinder::MatchResult &Result, const IfStmt *If, bool Negated) {
568  StringRef Terminator = isa<CompoundStmt>(If->getElse()) ? ";" : "";
569  std::string Condition = replacementExpression(Result, Negated, If->getCond());
570  std::string Replacement = ("return " + Condition + Terminator).str();
571  SourceLocation Start =
572  Result.Nodes.getNodeAs<CXXBoolLiteralExpr>(ThenLiteralId)->getLocStart();
573  issueDiag(Result, Start, SimplifyConditionalReturnDiagnostic,
574  If->getSourceRange(), Replacement);
575 }
576 
577 void SimplifyBooleanExprCheck::replaceCompoundReturnWithCondition(
578  const MatchFinder::MatchResult &Result, const CompoundStmt *Compound,
579  bool Negated) {
580  const auto *Ret = Result.Nodes.getNodeAs<ReturnStmt>(CompoundReturnId);
581 
582  // The body shouldn't be empty because the matcher ensures that it must
583  // contain at least two statements:
584  // 1) A `return` statement returning a boolean literal `false` or `true`
585  // 2) An `if` statement with no `else` clause that consists fo a single
586  // `return` statement returning the opposite boolean literal `true` or
587  // `false`.
588  assert(Compound->size() >= 2);
589  const IfStmt *BeforeIf = nullptr;
590  CompoundStmt::const_body_iterator Current = Compound->body_begin();
591  CompoundStmt::const_body_iterator After = Compound->body_begin();
592  for (++After; After != Compound->body_end() && *Current != Ret;
593  ++Current, ++After) {
594  if (const auto *If = dyn_cast<IfStmt>(*Current)) {
595  if (const CXXBoolLiteralExpr *Lit = stmtReturnsBool(If, Negated)) {
596  if (*After == Ret) {
597  if (!ChainedConditionalReturn && BeforeIf)
598  continue;
599 
600  const Expr *Condition = If->getCond();
601  std::string Replacement =
602  "return " + replacementExpression(Result, Negated, Condition);
603  issueDiag(
604  Result, Lit->getLocStart(), SimplifyConditionalReturnDiagnostic,
605  SourceRange(If->getLocStart(), Ret->getLocEnd()), Replacement);
606  return;
607  }
608 
609  BeforeIf = If;
610  }
611  } else {
612  BeforeIf = nullptr;
613  }
614  }
615 }
616 
617 void SimplifyBooleanExprCheck::replaceWithAssignment(
618  const MatchFinder::MatchResult &Result, const IfStmt *IfAssign,
619  bool Negated) {
620  SourceRange Range = IfAssign->getSourceRange();
621  StringRef VariableName =
622  getText(Result, *Result.Nodes.getNodeAs<Expr>(IfAssignVariableId));
623  StringRef Terminator = isa<CompoundStmt>(IfAssign->getElse()) ? ";" : "";
624  std::string Condition =
625  replacementExpression(Result, Negated, IfAssign->getCond());
626  std::string Replacement =
627  (VariableName + " = " + Condition + Terminator).str();
628  SourceLocation Location =
629  Result.Nodes.getNodeAs<CXXBoolLiteralExpr>(IfAssignLocId)->getLocStart();
630  issueDiag(Result, Location,
631  "redundant boolean literal in conditional assignment", Range,
632  Replacement);
633 }
634 
635 } // namespace readability
636 } // namespace tidy
637 } // namespace clang
SourceLocation Loc
'#' location in the include directive
StringHandle Name
std::unique_ptr< ast_matchers::MatchFinder > Finder
Definition: ClangTidy.cpp:188
void storeOptions(ClangTidyOptions::OptionMap &Options) override
Should store all options supported by this check with their current values or default values for opti...
Base class for all clang-tidy checks.
Definition: ClangTidy.h:102
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
bool containsDiscardedTokens(const ast_matchers::MatchFinder::MatchResult &Result, CharSourceRange CharRange)
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:344
std::map< std::string, std::string > OptionMap
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register ASTMatchers with Finder.
CharSourceRange Range
SourceRange for the file name.
ClangTidyContext & Context
Definition: ClangTidy.cpp:93
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:323
const NamedDecl * Result
Definition: USRFinder.cpp:121