clang-tools  11.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 std::string(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 SimplifyBooleanExprCheck::SimplifyBooleanExprCheck(StringRef Name,
337  ClangTidyContext *Context)
338  : ClangTidyCheck(Name, Context),
339  ChainedConditionalReturn(Options.get("ChainedConditionalReturn", false)),
340  ChainedConditionalAssignment(
341  Options.get("ChainedConditionalAssignment", false)) {}
342 
343 bool containsBoolLiteral(const Expr *E) {
344  if (!E)
345  return false;
346  E = E->IgnoreParenImpCasts();
347  if (isa<CXXBoolLiteralExpr>(E))
348  return true;
349  if (const auto *BinOp = dyn_cast<BinaryOperator>(E))
350  return containsBoolLiteral(BinOp->getLHS()) ||
351  containsBoolLiteral(BinOp->getRHS());
352  if (const auto *UnaryOp = dyn_cast<UnaryOperator>(E))
353  return containsBoolLiteral(UnaryOp->getSubExpr());
354  return false;
355 }
356 
357 void SimplifyBooleanExprCheck::reportBinOp(
358  const MatchFinder::MatchResult &Result, const BinaryOperator *Op) {
359  const auto *LHS = Op->getLHS()->IgnoreParenImpCasts();
360  const auto *RHS = Op->getRHS()->IgnoreParenImpCasts();
361 
362  const CXXBoolLiteralExpr *Bool;
363  const Expr *Other = nullptr;
364  if ((Bool = dyn_cast<CXXBoolLiteralExpr>(LHS)))
365  Other = RHS;
366  else if ((Bool = dyn_cast<CXXBoolLiteralExpr>(RHS)))
367  Other = LHS;
368  else
369  return;
370 
371  if (Bool->getBeginLoc().isMacroID())
372  return;
373 
374  // FIXME: why do we need this?
375  if (!isa<CXXBoolLiteralExpr>(Other) && containsBoolLiteral(Other))
376  return;
377 
378  bool BoolValue = Bool->getValue();
379 
380  auto replaceWithExpression = [this, &Result, LHS, RHS, Bool](
381  const Expr *ReplaceWith, bool Negated) {
382  std::string Replacement =
383  replacementExpression(Result, Negated, ReplaceWith);
384  SourceRange Range(LHS->getBeginLoc(), RHS->getEndLoc());
385  issueDiag(Result, Bool->getBeginLoc(), SimplifyOperatorDiagnostic, Range,
386  Replacement);
387  };
388 
389  switch (Op->getOpcode()) {
390  case BO_LAnd:
391  if (BoolValue) {
392  // expr && true -> expr
393  replaceWithExpression(Other, /*Negated=*/false);
394  } else {
395  // expr && false -> false
396  replaceWithExpression(Bool, /*Negated=*/false);
397  }
398  break;
399  case BO_LOr:
400  if (BoolValue) {
401  // expr || true -> true
402  replaceWithExpression(Bool, /*Negated=*/false);
403  } else {
404  // expr || false -> expr
405  replaceWithExpression(Other, /*Negated=*/false);
406  }
407  break;
408  case BO_EQ:
409  // expr == true -> expr, expr == false -> !expr
410  replaceWithExpression(Other, /*Negated=*/!BoolValue);
411  break;
412  case BO_NE:
413  // expr != true -> !expr, expr != false -> expr
414  replaceWithExpression(Other, /*Negated=*/BoolValue);
415  break;
416  default:
417  break;
418  }
419 }
420 
421 void SimplifyBooleanExprCheck::matchBoolCondition(MatchFinder *Finder,
422  bool Value,
423  StringRef BooleanId) {
424  Finder->addMatcher(
425  ifStmt(unless(isInTemplateInstantiation()),
426  hasCondition(cxxBoolLiteral(equals(Value)).bind(BooleanId)))
427  .bind(IfStmtId),
428  this);
429 }
430 
431 void SimplifyBooleanExprCheck::matchTernaryResult(MatchFinder *Finder,
432  bool Value,
433  StringRef TernaryId) {
434  Finder->addMatcher(
435  conditionalOperator(unless(isInTemplateInstantiation()),
436  hasTrueExpression(cxxBoolLiteral(equals(Value))),
437  hasFalseExpression(cxxBoolLiteral(equals(!Value))))
438  .bind(TernaryId),
439  this);
440 }
441 
442 void SimplifyBooleanExprCheck::matchIfReturnsBool(MatchFinder *Finder,
443  bool Value, StringRef Id) {
444  if (ChainedConditionalReturn)
445  Finder->addMatcher(ifStmt(unless(isInTemplateInstantiation()),
446  hasThen(returnsBool(Value, ThenLiteralId)),
447  hasElse(returnsBool(!Value)))
448  .bind(Id),
449  this);
450  else
451  Finder->addMatcher(ifStmt(unless(isInTemplateInstantiation()),
452  unless(hasParent(ifStmt())),
453  hasThen(returnsBool(Value, ThenLiteralId)),
454  hasElse(returnsBool(!Value)))
455  .bind(Id),
456  this);
457 }
458 
459 void SimplifyBooleanExprCheck::matchIfAssignsBool(MatchFinder *Finder,
460  bool Value, StringRef Id) {
461  auto VarAssign = declRefExpr(hasDeclaration(decl().bind(IfAssignVarId)));
462  auto VarRef = declRefExpr(hasDeclaration(equalsBoundNode(IfAssignVarId)));
463  auto MemAssign = memberExpr(hasDeclaration(decl().bind(IfAssignVarId)));
464  auto MemRef = memberExpr(hasDeclaration(equalsBoundNode(IfAssignVarId)));
465  auto SimpleThen =
466  binaryOperator(hasOperatorName("="), hasLHS(anyOf(VarAssign, MemAssign)),
467  hasLHS(expr().bind(IfAssignVariableId)),
468  hasRHS(cxxBoolLiteral(equals(Value)).bind(IfAssignLocId)));
469  auto Then = anyOf(SimpleThen, compoundStmt(statementCountIs(1),
470  hasAnySubstatement(SimpleThen)));
471  auto SimpleElse =
472  binaryOperator(hasOperatorName("="), hasLHS(anyOf(VarRef, MemRef)),
473  hasRHS(cxxBoolLiteral(equals(!Value))));
474  auto Else = anyOf(SimpleElse, compoundStmt(statementCountIs(1),
475  hasAnySubstatement(SimpleElse)));
476  if (ChainedConditionalAssignment)
477  Finder->addMatcher(ifStmt(unless(isInTemplateInstantiation()),
478  hasThen(Then), hasElse(Else))
479  .bind(Id),
480  this);
481  else
482  Finder->addMatcher(ifStmt(unless(isInTemplateInstantiation()),
483  unless(hasParent(ifStmt())), hasThen(Then),
484  hasElse(Else))
485  .bind(Id),
486  this);
487 }
488 
489 void SimplifyBooleanExprCheck::matchCompoundIfReturnsBool(MatchFinder *Finder,
490  bool Value,
491  StringRef Id) {
492  Finder->addMatcher(
493  compoundStmt(
494  unless(isInTemplateInstantiation()),
495  hasAnySubstatement(
496  ifStmt(hasThen(returnsBool(Value)), unless(hasElse(stmt())))),
497  hasAnySubstatement(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 (Result.Nodes.getNodeAs<TranslationUnitDecl>("top"))
531  Visitor(this, Result).TraverseAST(*Result.Context);
532  else if (const CXXBoolLiteralExpr *TrueConditionRemoved =
533  getBoolLiteral(Result, ConditionThenStmtId))
534  replaceWithThenStatement(Result, TrueConditionRemoved);
535  else if (const CXXBoolLiteralExpr *FalseConditionRemoved =
536  getBoolLiteral(Result, ConditionElseStmtId))
537  replaceWithElseStatement(Result, FalseConditionRemoved);
538  else if (const auto *Ternary =
539  Result.Nodes.getNodeAs<ConditionalOperator>(TernaryId))
540  replaceWithCondition(Result, Ternary);
541  else if (const auto *TernaryNegated =
542  Result.Nodes.getNodeAs<ConditionalOperator>(TernaryNegatedId))
543  replaceWithCondition(Result, TernaryNegated, true);
544  else if (const auto *If = Result.Nodes.getNodeAs<IfStmt>(IfReturnsBoolId))
545  replaceWithReturnCondition(Result, If);
546  else if (const auto *IfNot =
547  Result.Nodes.getNodeAs<IfStmt>(IfReturnsNotBoolId))
548  replaceWithReturnCondition(Result, IfNot, true);
549  else if (const auto *IfAssign =
550  Result.Nodes.getNodeAs<IfStmt>(IfAssignBoolId))
551  replaceWithAssignment(Result, IfAssign);
552  else if (const auto *IfAssignNot =
553  Result.Nodes.getNodeAs<IfStmt>(IfAssignNotBoolId))
554  replaceWithAssignment(Result, IfAssignNot, true);
555  else if (const auto *Compound =
556  Result.Nodes.getNodeAs<CompoundStmt>(CompoundBoolId))
557  replaceCompoundReturnWithCondition(Result, Compound);
558  else if (const auto *Compound =
559  Result.Nodes.getNodeAs<CompoundStmt>(CompoundNotBoolId))
560  replaceCompoundReturnWithCondition(Result, Compound, true);
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->getBeginLoc(),
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->getBeginLoc(),
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()->getBeginLoc(),
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)->getBeginLoc();
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->getBeginLoc(), SimplifyConditionalReturnDiagnostic,
644  SourceRange(If->getBeginLoc(), Ret->getEndLoc()), 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)->getBeginLoc();
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
Range
CharSourceRange Range
SourceRange for the file name.
Definition: IncludeOrderCheck.cpp:38
E
const Expr * E
Definition: AvoidBindCheck.cpp:88
Location
Definition: Modularize.cpp:383
clang::tidy::readability::SimplifyBooleanExprCheck::check
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
Definition: SimplifyBooleanExprCheck.cpp:529
Text
std::string Text
Definition: HTMLGenerator.cpp:80
clang::tidy::ClangTidyCheck
Base class for all clang-tidy checks.
Definition: ClangTidyCheck.h:114
clang::tidy::ClangTidyCheck::getLangOpts
const LangOptions & getLangOpts() const
Returns the language options from the context.
Definition: ClangTidyCheck.h:475
clang::ast_matchers
Definition: AbseilMatcher.h:14
Condition
std::string Condition
Condition used after the preprocessor directive.
Definition: RedundantPreprocessorCheck.cpp:24
clang::tidy::readability::SimplifyBooleanExprCheck::storeOptions
void storeOptions(ClangTidyOptions::OptionMap &Options) override
Should store all options supported by this check with their current values or default values for opti...
Definition: SimplifyBooleanExprCheck.cpp:504
clang::tidy::readability::SimplifyBooleanExprCheck::registerMatchers
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
Definition: SimplifyBooleanExprCheck.cpp:510
clang::tidy::ClangTidyCheck::Options
OptionsView Options
Definition: ClangTidyCheck.h:471
clang::tidy::readability::SimplifyBooleanExprCheck::Visitor
Definition: SimplifyBooleanExprCheck.cpp:320
clang::tidy::ClangTidyContext
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
Definition: ClangTidyDiagnosticConsumer.h:76
Description
const char * Description
Definition: Dexp.cpp:320
Name
static constexpr llvm::StringLiteral Name
Definition: UppercaseLiteralSuffixCheck.cpp:27
clang::tidy::readability::SimplifyBooleanExprCheck::Visitor::VisitBinaryOperator
bool VisitBinaryOperator(BinaryOperator *Op)
Definition: SimplifyBooleanExprCheck.cpp:326
clang::tidy::readability::SimplifyBooleanExprCheck
Looks for boolean expressions involving boolean constants and simplifies them to use the appropriate ...
Definition: SimplifyBooleanExprCheck.h:23
clang::tidy::readability::containsBoolLiteral
bool containsBoolLiteral(const Expr *E)
Definition: SimplifyBooleanExprCheck.cpp:343
clang::tidy::ClangTidyCheck::diag
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
Definition: ClangTidyCheck.cpp:55
SimplifyBooleanExprCheck.h
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
clang::tidy::ClangTidyCheck::OptionsView::store
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: ClangTidyCheck.cpp:152
Loc
SourceLocation Loc
'#' location in the include directive
Definition: IncludeOrderCheck.cpp:37
clang::tidy::readability::SimplifyBooleanExprCheck::Visitor::Visitor
Visitor(SimplifyBooleanExprCheck *Check, const MatchFinder::MatchResult &Result)
Definition: SimplifyBooleanExprCheck.cpp:322
clang::tidy::ClangTidyOptions::OptionMap
std::map< std::string, ClangTidyValue > OptionMap
Definition: ClangTidyOptions.h:111