clang-tools  9.0.0
SimplifyBooleanExprCheck.cpp
Go to the documentation of this file.
1 //===-- SimplifyBooleanExprCheck.cpp - clang-tidy -------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
10 #include "clang/AST/RecursiveASTVisitor.h"
11 #include "clang/Lex/Lexer.h"
12 
13 #include <cassert>
14 #include <string>
15 #include <utility>
16 
17 using namespace clang::ast_matchers;
18 
19 namespace clang {
20 namespace tidy {
21 namespace readability {
22 
23 namespace {
24 
25 StringRef getText(const MatchFinder::MatchResult &Result, SourceRange Range) {
26  return Lexer::getSourceText(CharSourceRange::getTokenRange(Range),
27  *Result.SourceManager,
28  Result.Context->getLangOpts());
29 }
30 
31 template <typename T>
32 StringRef getText(const MatchFinder::MatchResult &Result, T &Node) {
33  return getText(Result, Node.getSourceRange());
34 }
35 
36 const char ConditionThenStmtId[] = "if-bool-yields-then";
37 const char ConditionElseStmtId[] = "if-bool-yields-else";
38 const char TernaryId[] = "ternary-bool-yields-condition";
39 const char TernaryNegatedId[] = "ternary-bool-yields-not-condition";
40 const char IfReturnsBoolId[] = "if-return";
41 const char IfReturnsNotBoolId[] = "if-not-return";
42 const char ThenLiteralId[] = "then-literal";
43 const char IfAssignVariableId[] = "if-assign-lvalue";
44 const char IfAssignLocId[] = "if-assign-loc";
45 const char IfAssignBoolId[] = "if-assign";
46 const char IfAssignNotBoolId[] = "if-assign-not";
47 const char IfAssignVarId[] = "if-assign-var";
48 const char CompoundReturnId[] = "compound-return";
49 const char CompoundBoolId[] = "compound-bool";
50 const char CompoundNotBoolId[] = "compound-bool-not";
51 
52 const char IfStmtId[] = "if";
53 
54 const char SimplifyOperatorDiagnostic[] =
55  "redundant boolean literal supplied to boolean operator";
56 const char SimplifyConditionDiagnostic[] =
57  "redundant boolean literal in if statement condition";
58 const char SimplifyConditionalReturnDiagnostic[] =
59  "redundant boolean literal in conditional return statement";
60 
61 const CXXBoolLiteralExpr *getBoolLiteral(const MatchFinder::MatchResult &Result,
62  StringRef Id) {
63  const auto *Literal = Result.Nodes.getNodeAs<CXXBoolLiteralExpr>(Id);
64  return (Literal && Literal->getBeginLoc().isMacroID()) ? nullptr : Literal;
65 }
66 
67 internal::Matcher<Stmt> returnsBool(bool Value, StringRef Id = "ignored") {
68  auto SimpleReturnsBool =
69  returnStmt(has(cxxBoolLiteral(equals(Value)).bind(Id)))
70  .bind("returns-bool");
71  return anyOf(SimpleReturnsBool,
72  compoundStmt(statementCountIs(1), has(SimpleReturnsBool)));
73 }
74 
75 bool needsParensAfterUnaryNegation(const Expr *E) {
76  E = E->IgnoreImpCasts();
77  if (isa<BinaryOperator>(E) || isa<ConditionalOperator>(E))
78  return true;
79 
80  if (const auto *Op = dyn_cast<CXXOperatorCallExpr>(E))
81  return Op->getNumArgs() == 2 && Op->getOperator() != OO_Call &&
82  Op->getOperator() != OO_Subscript;
83 
84  return false;
85 }
86 
87 std::pair<BinaryOperatorKind, BinaryOperatorKind> Opposites[] = {
88  {BO_LT, BO_GE}, {BO_GT, BO_LE}, {BO_EQ, BO_NE}};
89 
90 StringRef negatedOperator(const BinaryOperator *BinOp) {
91  const BinaryOperatorKind Opcode = BinOp->getOpcode();
92  for (auto NegatableOp : Opposites) {
93  if (Opcode == NegatableOp.first)
94  return BinOp->getOpcodeStr(NegatableOp.second);
95  if (Opcode == NegatableOp.second)
96  return BinOp->getOpcodeStr(NegatableOp.first);
97  }
98  return StringRef();
99 }
100 
101 std::pair<OverloadedOperatorKind, StringRef> OperatorNames[] = {
102  {OO_EqualEqual, "=="}, {OO_ExclaimEqual, "!="}, {OO_Less, "<"},
103  {OO_GreaterEqual, ">="}, {OO_Greater, ">"}, {OO_LessEqual, "<="}};
104 
105 StringRef getOperatorName(OverloadedOperatorKind OpKind) {
106  for (auto Name : OperatorNames) {
107  if (Name.first == OpKind)
108  return Name.second;
109  }
110 
111  return StringRef();
112 }
113 
114 std::pair<OverloadedOperatorKind, OverloadedOperatorKind> OppositeOverloads[] =
115  {{OO_EqualEqual, OO_ExclaimEqual},
116  {OO_Less, OO_GreaterEqual},
117  {OO_Greater, OO_LessEqual}};
118 
119 StringRef negatedOperator(const CXXOperatorCallExpr *OpCall) {
120  const OverloadedOperatorKind Opcode = OpCall->getOperator();
121  for (auto NegatableOp : OppositeOverloads) {
122  if (Opcode == NegatableOp.first)
123  return getOperatorName(NegatableOp.second);
124  if (Opcode == NegatableOp.second)
125  return getOperatorName(NegatableOp.first);
126  }
127  return StringRef();
128 }
129 
130 std::string asBool(StringRef text, bool NeedsStaticCast) {
131  if (NeedsStaticCast)
132  return ("static_cast<bool>(" + text + ")").str();
133 
134  return text;
135 }
136 
137 bool needsNullPtrComparison(const Expr *E) {
138  if (const auto *ImpCast = dyn_cast<ImplicitCastExpr>(E))
139  return ImpCast->getCastKind() == CK_PointerToBoolean ||
140  ImpCast->getCastKind() == CK_MemberPointerToBoolean;
141 
142  return false;
143 }
144 
145 bool needsZeroComparison(const Expr *E) {
146  if (const auto *ImpCast = dyn_cast<ImplicitCastExpr>(E))
147  return ImpCast->getCastKind() == CK_IntegralToBoolean;
148 
149  return false;
150 }
151 
152 bool needsStaticCast(const Expr *E) {
153  if (const auto *ImpCast = dyn_cast<ImplicitCastExpr>(E)) {
154  if (ImpCast->getCastKind() == CK_UserDefinedConversion &&
155  ImpCast->getSubExpr()->getType()->isBooleanType()) {
156  if (const auto *MemCall =
157  dyn_cast<CXXMemberCallExpr>(ImpCast->getSubExpr())) {
158  if (const auto *MemDecl =
159  dyn_cast<CXXConversionDecl>(MemCall->getMethodDecl())) {
160  if (MemDecl->isExplicit())
161  return true;
162  }
163  }
164  }
165  }
166 
167  E = E->IgnoreImpCasts();
168  return !E->getType()->isBooleanType();
169 }
170 
171 std::string compareExpressionToConstant(const MatchFinder::MatchResult &Result,
172  const Expr *E, bool Negated,
173  const char *Constant) {
174  E = E->IgnoreImpCasts();
175  const std::string ExprText =
176  (isa<BinaryOperator>(E) ? ("(" + getText(Result, *E) + ")")
177  : getText(Result, *E))
178  .str();
179  return ExprText + " " + (Negated ? "!=" : "==") + " " + Constant;
180 }
181 
182 std::string compareExpressionToNullPtr(const MatchFinder::MatchResult &Result,
183  const Expr *E, bool Negated) {
184  const char *NullPtr =
185  Result.Context->getLangOpts().CPlusPlus11 ? "nullptr" : "NULL";
186  return compareExpressionToConstant(Result, E, Negated, NullPtr);
187 }
188 
189 std::string compareExpressionToZero(const MatchFinder::MatchResult &Result,
190  const Expr *E, bool Negated) {
191  return compareExpressionToConstant(Result, E, Negated, "0");
192 }
193 
194 std::string replacementExpression(const MatchFinder::MatchResult &Result,
195  bool Negated, const Expr *E) {
196  E = E->ignoreParenBaseCasts();
197  if (const auto *EC = dyn_cast<ExprWithCleanups>(E))
198  E = EC->getSubExpr();
199 
200  const bool NeedsStaticCast = needsStaticCast(E);
201  if (Negated) {
202  if (const auto *UnOp = dyn_cast<UnaryOperator>(E)) {
203  if (UnOp->getOpcode() == UO_LNot) {
204  if (needsNullPtrComparison(UnOp->getSubExpr()))
205  return compareExpressionToNullPtr(Result, UnOp->getSubExpr(), true);
206 
207  if (needsZeroComparison(UnOp->getSubExpr()))
208  return compareExpressionToZero(Result, UnOp->getSubExpr(), true);
209 
210  return replacementExpression(Result, false, UnOp->getSubExpr());
211  }
212  }
213 
214  if (needsNullPtrComparison(E))
215  return compareExpressionToNullPtr(Result, E, false);
216 
217  if (needsZeroComparison(E))
218  return compareExpressionToZero(Result, E, false);
219 
220  StringRef NegatedOperator;
221  const Expr *LHS = nullptr;
222  const Expr *RHS = nullptr;
223  if (const auto *BinOp = dyn_cast<BinaryOperator>(E)) {
224  NegatedOperator = negatedOperator(BinOp);
225  LHS = BinOp->getLHS();
226  RHS = BinOp->getRHS();
227  } else if (const auto *OpExpr = dyn_cast<CXXOperatorCallExpr>(E)) {
228  if (OpExpr->getNumArgs() == 2) {
229  NegatedOperator = negatedOperator(OpExpr);
230  LHS = OpExpr->getArg(0);
231  RHS = OpExpr->getArg(1);
232  }
233  }
234  if (!NegatedOperator.empty() && LHS && RHS)
235  return (asBool((getText(Result, *LHS) + " " + NegatedOperator + " " +
236  getText(Result, *RHS))
237  .str(),
238  NeedsStaticCast));
239 
240  StringRef Text = getText(Result, *E);
241  if (!NeedsStaticCast && needsParensAfterUnaryNegation(E))
242  return ("!(" + Text + ")").str();
243 
244  if (needsNullPtrComparison(E))
245  return compareExpressionToNullPtr(Result, E, false);
246 
247  if (needsZeroComparison(E))
248  return compareExpressionToZero(Result, E, false);
249 
250  return ("!" + asBool(Text, NeedsStaticCast));
251  }
252 
253  if (const auto *UnOp = dyn_cast<UnaryOperator>(E)) {
254  if (UnOp->getOpcode() == UO_LNot) {
255  if (needsNullPtrComparison(UnOp->getSubExpr()))
256  return compareExpressionToNullPtr(Result, UnOp->getSubExpr(), false);
257 
258  if (needsZeroComparison(UnOp->getSubExpr()))
259  return compareExpressionToZero(Result, UnOp->getSubExpr(), false);
260  }
261  }
262 
263  if (needsNullPtrComparison(E))
264  return compareExpressionToNullPtr(Result, E, true);
265 
266  if (needsZeroComparison(E))
267  return compareExpressionToZero(Result, E, true);
268 
269  return asBool(getText(Result, *E), NeedsStaticCast);
270 }
271 
272 const CXXBoolLiteralExpr *stmtReturnsBool(const ReturnStmt *Ret, bool Negated) {
273  if (const auto *Bool = dyn_cast<CXXBoolLiteralExpr>(Ret->getRetValue())) {
274  if (Bool->getValue() == !Negated)
275  return Bool;
276  }
277 
278  return nullptr;
279 }
280 
281 const CXXBoolLiteralExpr *stmtReturnsBool(const IfStmt *IfRet, bool Negated) {
282  if (IfRet->getElse() != nullptr)
283  return nullptr;
284 
285  if (const auto *Ret = dyn_cast<ReturnStmt>(IfRet->getThen()))
286  return stmtReturnsBool(Ret, Negated);
287 
288  if (const auto *Compound = dyn_cast<CompoundStmt>(IfRet->getThen())) {
289  if (Compound->size() == 1) {
290  if (const auto *CompoundRet = dyn_cast<ReturnStmt>(Compound->body_back()))
291  return stmtReturnsBool(CompoundRet, Negated);
292  }
293  }
294 
295  return nullptr;
296 }
297 
298 bool containsDiscardedTokens(const MatchFinder::MatchResult &Result,
299  CharSourceRange CharRange) {
300  std::string ReplacementText =
301  Lexer::getSourceText(CharRange, *Result.SourceManager,
302  Result.Context->getLangOpts())
303  .str();
304  Lexer Lex(CharRange.getBegin(), Result.Context->getLangOpts(),
305  ReplacementText.data(), ReplacementText.data(),
306  ReplacementText.data() + ReplacementText.size());
307  Lex.SetCommentRetentionState(true);
308 
309  Token Tok;
310  while (!Lex.LexFromRawLexer(Tok)) {
311  if (Tok.is(tok::TokenKind::comment) || Tok.is(tok::TokenKind::hash))
312  return true;
313  }
314 
315  return false;
316 }
317 
318 } // namespace
319 
320 class SimplifyBooleanExprCheck::Visitor : public RecursiveASTVisitor<Visitor> {
321  public:
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;
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->getBeginLoc().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->getBeginLoc(), RHS->getEndLoc());
386  issueDiag(Result, Bool->getBeginLoc(), 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 VarAssign = declRefExpr(hasDeclaration(decl().bind(IfAssignVarId)));
463  auto VarRef = declRefExpr(hasDeclaration(equalsBoundNode(IfAssignVarId)));
464  auto MemAssign = memberExpr(hasDeclaration(decl().bind(IfAssignVarId)));
465  auto MemRef = memberExpr(hasDeclaration(equalsBoundNode(IfAssignVarId)));
466  auto SimpleThen =
467  binaryOperator(hasOperatorName("="), hasLHS(anyOf(VarAssign, MemAssign)),
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 =
473  binaryOperator(hasOperatorName("="), hasLHS(anyOf(VarRef, MemRef)),
474  hasRHS(cxxBoolLiteral(equals(!Value))));
475  auto Else = anyOf(SimpleElse, compoundStmt(statementCountIs(1),
476  hasAnySubstatement(SimpleElse)));
477  if (ChainedConditionalAssignment)
478  Finder->addMatcher(ifStmt(hasThen(Then), hasElse(Else)).bind(Id), this);
479  else
480  Finder->addMatcher(
481  ifStmt(unless(hasParent(ifStmt())), hasThen(Then), hasElse(Else))
482  .bind(Id),
483  this);
484 }
485 
486 void SimplifyBooleanExprCheck::matchCompoundIfReturnsBool(MatchFinder *Finder,
487  bool Value,
488  StringRef Id) {
489  Finder->addMatcher(
490  compoundStmt(
491  hasAnySubstatement(
492  ifStmt(hasThen(returnsBool(Value)), unless(hasElse(stmt())))),
493  hasAnySubstatement(returnStmt(has(ignoringParenImpCasts(
494  cxxBoolLiteral(equals(!Value)))))
495  .bind(CompoundReturnId)))
496  .bind(Id),
497  this);
498 }
499 
501  Options.store(Opts, "ChainedConditionalReturn", ChainedConditionalReturn);
502  Options.store(Opts, "ChainedConditionalAssignment",
503  ChainedConditionalAssignment);
504 }
505 
507  Finder->addMatcher(translationUnitDecl().bind("top"), this);
508 
509  matchBoolCondition(Finder, true, ConditionThenStmtId);
510  matchBoolCondition(Finder, false, ConditionElseStmtId);
511 
512  matchTernaryResult(Finder, true, TernaryId);
513  matchTernaryResult(Finder, false, TernaryNegatedId);
514 
515  matchIfReturnsBool(Finder, true, IfReturnsBoolId);
516  matchIfReturnsBool(Finder, false, IfReturnsNotBoolId);
517 
518  matchIfAssignsBool(Finder, true, IfAssignBoolId);
519  matchIfAssignsBool(Finder, false, IfAssignNotBoolId);
520 
521  matchCompoundIfReturnsBool(Finder, true, CompoundBoolId);
522  matchCompoundIfReturnsBool(Finder, false, CompoundNotBoolId);
523 }
524 
525 void SimplifyBooleanExprCheck::check(const MatchFinder::MatchResult &Result) {
526  if (Result.Nodes.getNodeAs<TranslationUnitDecl>("top"))
527  Visitor(this, Result).TraverseAST(*Result.Context);
528  else if (const CXXBoolLiteralExpr *TrueConditionRemoved =
529  getBoolLiteral(Result, ConditionThenStmtId))
530  replaceWithThenStatement(Result, TrueConditionRemoved);
531  else if (const CXXBoolLiteralExpr *FalseConditionRemoved =
532  getBoolLiteral(Result, ConditionElseStmtId))
533  replaceWithElseStatement(Result, FalseConditionRemoved);
534  else if (const auto *Ternary =
535  Result.Nodes.getNodeAs<ConditionalOperator>(TernaryId))
536  replaceWithCondition(Result, Ternary);
537  else if (const auto *TernaryNegated =
538  Result.Nodes.getNodeAs<ConditionalOperator>(TernaryNegatedId))
539  replaceWithCondition(Result, TernaryNegated, true);
540  else if (const auto *If = Result.Nodes.getNodeAs<IfStmt>(IfReturnsBoolId))
541  replaceWithReturnCondition(Result, If);
542  else if (const auto *IfNot =
543  Result.Nodes.getNodeAs<IfStmt>(IfReturnsNotBoolId))
544  replaceWithReturnCondition(Result, IfNot, true);
545  else if (const auto *IfAssign =
546  Result.Nodes.getNodeAs<IfStmt>(IfAssignBoolId))
547  replaceWithAssignment(Result, IfAssign);
548  else if (const auto *IfAssignNot =
549  Result.Nodes.getNodeAs<IfStmt>(IfAssignNotBoolId))
550  replaceWithAssignment(Result, IfAssignNot, true);
551  else if (const auto *Compound =
552  Result.Nodes.getNodeAs<CompoundStmt>(CompoundBoolId))
553  replaceCompoundReturnWithCondition(Result, Compound);
554  else if (const auto *Compound =
555  Result.Nodes.getNodeAs<CompoundStmt>(CompoundNotBoolId))
556  replaceCompoundReturnWithCondition(Result, Compound, true);
557 }
558 
559 void SimplifyBooleanExprCheck::issueDiag(
560  const ast_matchers::MatchFinder::MatchResult &Result, SourceLocation Loc,
561  StringRef Description, SourceRange ReplacementRange,
562  StringRef Replacement) {
563  CharSourceRange CharRange =
564  Lexer::makeFileCharRange(CharSourceRange::getTokenRange(ReplacementRange),
565  *Result.SourceManager, getLangOpts());
566 
567  DiagnosticBuilder Diag = diag(Loc, Description);
568  if (!containsDiscardedTokens(Result, CharRange))
569  Diag << FixItHint::CreateReplacement(CharRange, Replacement);
570 }
571 
572 void SimplifyBooleanExprCheck::replaceWithThenStatement(
573  const MatchFinder::MatchResult &Result,
574  const CXXBoolLiteralExpr *TrueConditionRemoved) {
575  const auto *IfStatement = Result.Nodes.getNodeAs<IfStmt>(IfStmtId);
576  issueDiag(Result, TrueConditionRemoved->getBeginLoc(),
577  SimplifyConditionDiagnostic, IfStatement->getSourceRange(),
578  getText(Result, *IfStatement->getThen()));
579 }
580 
581 void SimplifyBooleanExprCheck::replaceWithElseStatement(
582  const MatchFinder::MatchResult &Result,
583  const CXXBoolLiteralExpr *FalseConditionRemoved) {
584  const auto *IfStatement = Result.Nodes.getNodeAs<IfStmt>(IfStmtId);
585  const Stmt *ElseStatement = IfStatement->getElse();
586  issueDiag(Result, FalseConditionRemoved->getBeginLoc(),
587  SimplifyConditionDiagnostic, IfStatement->getSourceRange(),
588  ElseStatement ? getText(Result, *ElseStatement) : "");
589 }
590 
591 void SimplifyBooleanExprCheck::replaceWithCondition(
592  const MatchFinder::MatchResult &Result, const ConditionalOperator *Ternary,
593  bool Negated) {
594  std::string Replacement =
595  replacementExpression(Result, Negated, Ternary->getCond());
596  issueDiag(Result, Ternary->getTrueExpr()->getBeginLoc(),
597  "redundant boolean literal in ternary expression result",
598  Ternary->getSourceRange(), Replacement);
599 }
600 
601 void SimplifyBooleanExprCheck::replaceWithReturnCondition(
602  const MatchFinder::MatchResult &Result, const IfStmt *If, bool Negated) {
603  StringRef Terminator = isa<CompoundStmt>(If->getElse()) ? ";" : "";
604  std::string Condition = replacementExpression(Result, Negated, If->getCond());
605  std::string Replacement = ("return " + Condition + Terminator).str();
606  SourceLocation Start =
607  Result.Nodes.getNodeAs<CXXBoolLiteralExpr>(ThenLiteralId)->getBeginLoc();
608  issueDiag(Result, Start, SimplifyConditionalReturnDiagnostic,
609  If->getSourceRange(), Replacement);
610 }
611 
612 void SimplifyBooleanExprCheck::replaceCompoundReturnWithCondition(
613  const MatchFinder::MatchResult &Result, const CompoundStmt *Compound,
614  bool Negated) {
615  const auto *Ret = Result.Nodes.getNodeAs<ReturnStmt>(CompoundReturnId);
616 
617  // The body shouldn't be empty because the matcher ensures that it must
618  // contain at least two statements:
619  // 1) A `return` statement returning a boolean literal `false` or `true`
620  // 2) An `if` statement with no `else` clause that consists of a single
621  // `return` statement returning the opposite boolean literal `true` or
622  // `false`.
623  assert(Compound->size() >= 2);
624  const IfStmt *BeforeIf = nullptr;
625  CompoundStmt::const_body_iterator Current = Compound->body_begin();
626  CompoundStmt::const_body_iterator After = Compound->body_begin();
627  for (++After; After != Compound->body_end() && *Current != Ret;
628  ++Current, ++After) {
629  if (const auto *If = dyn_cast<IfStmt>(*Current)) {
630  if (const CXXBoolLiteralExpr *Lit = stmtReturnsBool(If, Negated)) {
631  if (*After == Ret) {
632  if (!ChainedConditionalReturn && BeforeIf)
633  continue;
634 
635  const Expr *Condition = If->getCond();
636  std::string Replacement =
637  "return " + replacementExpression(Result, Negated, Condition);
638  issueDiag(
639  Result, Lit->getBeginLoc(), SimplifyConditionalReturnDiagnostic,
640  SourceRange(If->getBeginLoc(), Ret->getEndLoc()), Replacement);
641  return;
642  }
643 
644  BeforeIf = If;
645  }
646  } else {
647  BeforeIf = nullptr;
648  }
649  }
650 }
651 
652 void SimplifyBooleanExprCheck::replaceWithAssignment(
653  const MatchFinder::MatchResult &Result, const IfStmt *IfAssign,
654  bool Negated) {
655  SourceRange Range = IfAssign->getSourceRange();
656  StringRef VariableName =
657  getText(Result, *Result.Nodes.getNodeAs<Expr>(IfAssignVariableId));
658  StringRef Terminator = isa<CompoundStmt>(IfAssign->getElse()) ? ";" : "";
659  std::string Condition =
660  replacementExpression(Result, Negated, IfAssign->getCond());
661  std::string Replacement =
662  (VariableName + " = " + Condition + Terminator).str();
663  SourceLocation Location =
664  Result.Nodes.getNodeAs<CXXBoolLiteralExpr>(IfAssignLocId)->getBeginLoc();
665  issueDiag(Result, Location,
666  "redundant boolean literal in conditional assignment", Range,
667  Replacement);
668 }
669 
670 } // namespace readability
671 } // namespace tidy
672 } // namespace clang
SourceLocation Loc
&#39;#&#39; location in the include directive
std::string Text
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.
const LangOptions & getLangOpts() const
Returns the language options from the context.
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.
static constexpr llvm::StringLiteral Name
std::map< std::string, std::string > OptionMap
llvm::Optional< Range > getTokenRange(const SourceManager &SM, const LangOptions &LangOpts, SourceLocation TokLoc)
Returns the taken range at TokLoc.
Definition: SourceCode.cpp:203
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.
llvm::Optional< llvm::Expected< tooling::AtomicChanges > > Result
Definition: Rename.cpp:36
const char * Description
Definition: Dexp.cpp:257
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check&#39;s name.