clang-tools  14.0.0git
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 Expr *getBoolLiteral(const MatchFinder::MatchResult &Result,
62  StringRef Id) {
63  if (const Expr *Literal = Result.Nodes.getNodeAs<CXXBoolLiteralExpr>(Id))
64  return Literal->getBeginLoc().isMacroID() ? nullptr : Literal;
65  if (const auto *Negated = Result.Nodes.getNodeAs<UnaryOperator>(Id)) {
66  if (Negated->getOpcode() == UO_LNot &&
67  isa<CXXBoolLiteralExpr>(Negated->getSubExpr()))
68  return Negated->getBeginLoc().isMacroID() ? nullptr : Negated;
69  }
70  return nullptr;
71 }
72 
73 internal::BindableMatcher<Stmt> literalOrNegatedBool(bool Value) {
74  return expr(
75  anyOf(cxxBoolLiteral(equals(Value)),
76  unaryOperator(hasUnaryOperand(cxxBoolLiteral(equals(!Value))),
77  hasOperatorName("!"))));
78 }
79 
80 internal::Matcher<Stmt> returnsBool(bool Value, StringRef Id = "ignored") {
81  auto SimpleReturnsBool = returnStmt(has(literalOrNegatedBool(Value).bind(Id)))
82  .bind("returns-bool");
83  return anyOf(SimpleReturnsBool,
84  compoundStmt(statementCountIs(1), has(SimpleReturnsBool)));
85 }
86 
87 bool needsParensAfterUnaryNegation(const Expr *E) {
88  E = E->IgnoreImpCasts();
89  if (isa<BinaryOperator>(E) || isa<ConditionalOperator>(E))
90  return true;
91 
92  if (const auto *Op = dyn_cast<CXXOperatorCallExpr>(E))
93  return Op->getNumArgs() == 2 && Op->getOperator() != OO_Call &&
94  Op->getOperator() != OO_Subscript;
95 
96  return false;
97 }
98 
99 std::pair<BinaryOperatorKind, BinaryOperatorKind> Opposites[] = {
100  {BO_LT, BO_GE}, {BO_GT, BO_LE}, {BO_EQ, BO_NE}};
101 
102 StringRef negatedOperator(const BinaryOperator *BinOp) {
103  const BinaryOperatorKind Opcode = BinOp->getOpcode();
104  for (auto NegatableOp : Opposites) {
105  if (Opcode == NegatableOp.first)
106  return BinOp->getOpcodeStr(NegatableOp.second);
107  if (Opcode == NegatableOp.second)
108  return BinOp->getOpcodeStr(NegatableOp.first);
109  }
110  return StringRef();
111 }
112 
113 std::pair<OverloadedOperatorKind, StringRef> OperatorNames[] = {
114  {OO_EqualEqual, "=="}, {OO_ExclaimEqual, "!="}, {OO_Less, "<"},
115  {OO_GreaterEqual, ">="}, {OO_Greater, ">"}, {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 std::string(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  ImpCast->getCastKind() == CK_MemberPointerToBoolean;
153 
154  return false;
155 }
156 
157 bool needsZeroComparison(const Expr *E) {
158  if (const auto *ImpCast = dyn_cast<ImplicitCastExpr>(E))
159  return ImpCast->getCastKind() == CK_IntegralToBoolean;
160 
161  return false;
162 }
163 
164 bool needsStaticCast(const Expr *E) {
165  if (const auto *ImpCast = dyn_cast<ImplicitCastExpr>(E)) {
166  if (ImpCast->getCastKind() == CK_UserDefinedConversion &&
167  ImpCast->getSubExpr()->getType()->isBooleanType()) {
168  if (const auto *MemCall =
169  dyn_cast<CXXMemberCallExpr>(ImpCast->getSubExpr())) {
170  if (const auto *MemDecl =
171  dyn_cast<CXXConversionDecl>(MemCall->getMethodDecl())) {
172  if (MemDecl->isExplicit())
173  return true;
174  }
175  }
176  }
177  }
178 
179  E = E->IgnoreImpCasts();
180  return !E->getType()->isBooleanType();
181 }
182 
183 std::string compareExpressionToConstant(const MatchFinder::MatchResult &Result,
184  const Expr *E, bool Negated,
185  const char *Constant) {
186  E = E->IgnoreImpCasts();
187  const std::string ExprText =
188  (isa<BinaryOperator>(E) ? ("(" + getText(Result, *E) + ")")
189  : getText(Result, *E))
190  .str();
191  return ExprText + " " + (Negated ? "!=" : "==") + " " + Constant;
192 }
193 
194 std::string compareExpressionToNullPtr(const MatchFinder::MatchResult &Result,
195  const Expr *E, bool Negated) {
196  const char *NullPtr =
197  Result.Context->getLangOpts().CPlusPlus11 ? "nullptr" : "NULL";
198  return compareExpressionToConstant(Result, E, Negated, NullPtr);
199 }
200 
201 std::string compareExpressionToZero(const MatchFinder::MatchResult &Result,
202  const Expr *E, bool Negated) {
203  return compareExpressionToConstant(Result, E, Negated, "0");
204 }
205 
206 std::string replacementExpression(const MatchFinder::MatchResult &Result,
207  bool Negated, const Expr *E) {
208  E = E->IgnoreParenBaseCasts();
209  if (const auto *EC = dyn_cast<ExprWithCleanups>(E))
210  E = EC->getSubExpr();
211 
212  const bool NeedsStaticCast = needsStaticCast(E);
213  if (Negated) {
214  if (const auto *UnOp = dyn_cast<UnaryOperator>(E)) {
215  if (UnOp->getOpcode() == UO_LNot) {
216  if (needsNullPtrComparison(UnOp->getSubExpr()))
217  return compareExpressionToNullPtr(Result, UnOp->getSubExpr(), true);
218 
219  if (needsZeroComparison(UnOp->getSubExpr()))
220  return compareExpressionToZero(Result, UnOp->getSubExpr(), true);
221 
222  return replacementExpression(Result, false, UnOp->getSubExpr());
223  }
224  }
225 
226  if (needsNullPtrComparison(E))
227  return compareExpressionToNullPtr(Result, E, false);
228 
229  if (needsZeroComparison(E))
230  return compareExpressionToZero(Result, E, false);
231 
232  StringRef NegatedOperator;
233  const Expr *LHS = nullptr;
234  const Expr *RHS = nullptr;
235  if (const auto *BinOp = dyn_cast<BinaryOperator>(E)) {
236  NegatedOperator = negatedOperator(BinOp);
237  LHS = BinOp->getLHS();
238  RHS = BinOp->getRHS();
239  } else if (const auto *OpExpr = dyn_cast<CXXOperatorCallExpr>(E)) {
240  if (OpExpr->getNumArgs() == 2) {
241  NegatedOperator = negatedOperator(OpExpr);
242  LHS = OpExpr->getArg(0);
243  RHS = OpExpr->getArg(1);
244  }
245  }
246  if (!NegatedOperator.empty() && LHS && RHS)
247  return (asBool((getText(Result, *LHS) + " " + NegatedOperator + " " +
248  getText(Result, *RHS))
249  .str(),
250  NeedsStaticCast));
251 
252  StringRef Text = getText(Result, *E);
253  if (!NeedsStaticCast && needsParensAfterUnaryNegation(E))
254  return ("!(" + Text + ")").str();
255 
256  if (needsNullPtrComparison(E))
257  return compareExpressionToNullPtr(Result, E, false);
258 
259  if (needsZeroComparison(E))
260  return compareExpressionToZero(Result, E, false);
261 
262  return ("!" + asBool(Text, NeedsStaticCast));
263  }
264 
265  if (const auto *UnOp = dyn_cast<UnaryOperator>(E)) {
266  if (UnOp->getOpcode() == UO_LNot) {
267  if (needsNullPtrComparison(UnOp->getSubExpr()))
268  return compareExpressionToNullPtr(Result, UnOp->getSubExpr(), false);
269 
270  if (needsZeroComparison(UnOp->getSubExpr()))
271  return compareExpressionToZero(Result, UnOp->getSubExpr(), false);
272  }
273  }
274 
275  if (needsNullPtrComparison(E))
276  return compareExpressionToNullPtr(Result, E, true);
277 
278  if (needsZeroComparison(E))
279  return compareExpressionToZero(Result, E, true);
280 
281  return asBool(getText(Result, *E), NeedsStaticCast);
282 }
283 
284 const Expr *stmtReturnsBool(const ReturnStmt *Ret, bool Negated) {
285  if (const auto *Bool = dyn_cast<CXXBoolLiteralExpr>(Ret->getRetValue())) {
286  if (Bool->getValue() == !Negated)
287  return Bool;
288  }
289  if (const auto *Unary = dyn_cast<UnaryOperator>(Ret->getRetValue())) {
290  if (Unary->getOpcode() == UO_LNot) {
291  if (const auto *Bool =
292  dyn_cast<CXXBoolLiteralExpr>(Unary->getSubExpr())) {
293  if (Bool->getValue() == Negated)
294  return Bool;
295  }
296  }
297  }
298 
299  return nullptr;
300 }
301 
302 const Expr *stmtReturnsBool(const IfStmt *IfRet, bool Negated) {
303  if (IfRet->getElse() != nullptr)
304  return nullptr;
305 
306  if (const auto *Ret = dyn_cast<ReturnStmt>(IfRet->getThen()))
307  return stmtReturnsBool(Ret, Negated);
308 
309  if (const auto *Compound = dyn_cast<CompoundStmt>(IfRet->getThen())) {
310  if (Compound->size() == 1) {
311  if (const auto *CompoundRet = dyn_cast<ReturnStmt>(Compound->body_back()))
312  return stmtReturnsBool(CompoundRet, Negated);
313  }
314  }
315 
316  return nullptr;
317 }
318 
319 bool containsDiscardedTokens(const MatchFinder::MatchResult &Result,
320  CharSourceRange CharRange) {
321  std::string ReplacementText =
322  Lexer::getSourceText(CharRange, *Result.SourceManager,
323  Result.Context->getLangOpts())
324  .str();
325  Lexer Lex(CharRange.getBegin(), Result.Context->getLangOpts(),
326  ReplacementText.data(), ReplacementText.data(),
327  ReplacementText.data() + ReplacementText.size());
328  Lex.SetCommentRetentionState(true);
329 
330  Token Tok;
331  while (!Lex.LexFromRawLexer(Tok)) {
332  if (Tok.is(tok::TokenKind::comment) || Tok.is(tok::TokenKind::hash))
333  return true;
334  }
335 
336  return false;
337 }
338 
339 } // namespace
340 
341 class SimplifyBooleanExprCheck::Visitor : public RecursiveASTVisitor<Visitor> {
342  public:
344  const MatchFinder::MatchResult &Result)
345  : Check(Check), Result(Result) {}
346 
347  bool VisitBinaryOperator(BinaryOperator *Op) {
348  Check->reportBinOp(Result, Op);
349  return true;
350  }
351 
352  private:
354  const MatchFinder::MatchResult &Result;
355 };
356 
357 SimplifyBooleanExprCheck::SimplifyBooleanExprCheck(StringRef Name,
358  ClangTidyContext *Context)
359  : ClangTidyCheck(Name, Context),
360  ChainedConditionalReturn(Options.get("ChainedConditionalReturn", false)),
361  ChainedConditionalAssignment(
362  Options.get("ChainedConditionalAssignment", false)) {}
363 
364 bool containsBoolLiteral(const Expr *E) {
365  if (!E)
366  return false;
367  E = E->IgnoreParenImpCasts();
368  if (isa<CXXBoolLiteralExpr>(E))
369  return true;
370  if (const auto *BinOp = dyn_cast<BinaryOperator>(E))
371  return containsBoolLiteral(BinOp->getLHS()) ||
372  containsBoolLiteral(BinOp->getRHS());
373  if (const auto *UnaryOp = dyn_cast<UnaryOperator>(E))
374  return containsBoolLiteral(UnaryOp->getSubExpr());
375  return false;
376 }
377 
378 void SimplifyBooleanExprCheck::reportBinOp(
379  const MatchFinder::MatchResult &Result, const BinaryOperator *Op) {
380  const auto *LHS = Op->getLHS()->IgnoreParenImpCasts();
381  const auto *RHS = Op->getRHS()->IgnoreParenImpCasts();
382 
383  const CXXBoolLiteralExpr *Bool;
384  const Expr *Other = nullptr;
385  if ((Bool = dyn_cast<CXXBoolLiteralExpr>(LHS)))
386  Other = RHS;
387  else if ((Bool = dyn_cast<CXXBoolLiteralExpr>(RHS)))
388  Other = LHS;
389  else
390  return;
391 
392  if (Bool->getBeginLoc().isMacroID())
393  return;
394 
395  // FIXME: why do we need this?
396  if (!isa<CXXBoolLiteralExpr>(Other) && containsBoolLiteral(Other))
397  return;
398 
399  bool BoolValue = Bool->getValue();
400 
401  auto ReplaceWithExpression = [this, &Result, LHS, RHS,
402  Bool](const Expr *ReplaceWith, bool Negated) {
403  std::string Replacement =
404  replacementExpression(Result, Negated, ReplaceWith);
405  SourceRange Range(LHS->getBeginLoc(), RHS->getEndLoc());
406  issueDiag(Result, Bool->getBeginLoc(), SimplifyOperatorDiagnostic, Range,
407  Replacement);
408  };
409 
410  switch (Op->getOpcode()) {
411  case BO_LAnd:
412  if (BoolValue) {
413  // expr && true -> expr
414  ReplaceWithExpression(Other, /*Negated=*/false);
415  } else {
416  // expr && false -> false
417  ReplaceWithExpression(Bool, /*Negated=*/false);
418  }
419  break;
420  case BO_LOr:
421  if (BoolValue) {
422  // expr || true -> true
423  ReplaceWithExpression(Bool, /*Negated=*/false);
424  } else {
425  // expr || false -> expr
426  ReplaceWithExpression(Other, /*Negated=*/false);
427  }
428  break;
429  case BO_EQ:
430  // expr == true -> expr, expr == false -> !expr
431  ReplaceWithExpression(Other, /*Negated=*/!BoolValue);
432  break;
433  case BO_NE:
434  // expr != true -> !expr, expr != false -> expr
435  ReplaceWithExpression(Other, /*Negated=*/BoolValue);
436  break;
437  default:
438  break;
439  }
440 }
441 
442 void SimplifyBooleanExprCheck::matchBoolCondition(MatchFinder *Finder,
443  bool Value,
444  StringRef BooleanId) {
445  Finder->addMatcher(
446  ifStmt(hasCondition(literalOrNegatedBool(Value).bind(BooleanId)))
447  .bind(IfStmtId),
448  this);
449 }
450 
451 void SimplifyBooleanExprCheck::matchTernaryResult(MatchFinder *Finder,
452  bool Value,
453  StringRef TernaryId) {
454  Finder->addMatcher(
455  conditionalOperator(hasTrueExpression(literalOrNegatedBool(Value)),
456  hasFalseExpression(literalOrNegatedBool(!Value)))
457  .bind(TernaryId),
458  this);
459 }
460 
461 void SimplifyBooleanExprCheck::matchIfReturnsBool(MatchFinder *Finder,
462  bool Value, StringRef Id) {
463  if (ChainedConditionalReturn)
464  Finder->addMatcher(ifStmt(hasThen(returnsBool(Value, ThenLiteralId)),
465  hasElse(returnsBool(!Value)))
466  .bind(Id),
467  this);
468  else
469  Finder->addMatcher(ifStmt(unless(hasParent(ifStmt())),
470  hasThen(returnsBool(Value, ThenLiteralId)),
471  hasElse(returnsBool(!Value)))
472  .bind(Id),
473  this);
474 }
475 
476 void SimplifyBooleanExprCheck::matchIfAssignsBool(MatchFinder *Finder,
477  bool Value, StringRef Id) {
478  auto VarAssign = declRefExpr(hasDeclaration(decl().bind(IfAssignVarId)));
479  auto VarRef = declRefExpr(hasDeclaration(equalsBoundNode(IfAssignVarId)));
480  auto MemAssign = memberExpr(hasDeclaration(decl().bind(IfAssignVarId)));
481  auto MemRef = memberExpr(hasDeclaration(equalsBoundNode(IfAssignVarId)));
482  auto SimpleThen =
483  binaryOperator(hasOperatorName("="), hasLHS(anyOf(VarAssign, MemAssign)),
484  hasLHS(expr().bind(IfAssignVariableId)),
485  hasRHS(literalOrNegatedBool(Value).bind(IfAssignLocId)));
486  auto Then = anyOf(SimpleThen, compoundStmt(statementCountIs(1),
487  hasAnySubstatement(SimpleThen)));
488  auto SimpleElse =
489  binaryOperator(hasOperatorName("="), hasLHS(anyOf(VarRef, MemRef)),
490  hasRHS(literalOrNegatedBool(!Value)));
491  auto Else = anyOf(SimpleElse, compoundStmt(statementCountIs(1),
492  hasAnySubstatement(SimpleElse)));
493  if (ChainedConditionalAssignment)
494  Finder->addMatcher(ifStmt(hasThen(Then), hasElse(Else)).bind(Id), this);
495  else
496  Finder->addMatcher(
497  ifStmt(unless(hasParent(ifStmt())), hasThen(Then), hasElse(Else))
498  .bind(Id),
499  this);
500 }
501 
502 void SimplifyBooleanExprCheck::matchCompoundIfReturnsBool(MatchFinder *Finder,
503  bool Value,
504  StringRef Id) {
505  Finder->addMatcher(
506  compoundStmt(
507  hasAnySubstatement(
508  ifStmt(hasThen(returnsBool(Value)), unless(hasElse(stmt())))),
509  hasAnySubstatement(returnStmt(has(literalOrNegatedBool(!Value)))
510  .bind(CompoundReturnId)))
511  .bind(Id),
512  this);
513 }
514 
516  Options.store(Opts, "ChainedConditionalReturn", ChainedConditionalReturn);
517  Options.store(Opts, "ChainedConditionalAssignment",
518  ChainedConditionalAssignment);
519 }
520 
522  Finder->addMatcher(translationUnitDecl().bind("top"), this);
523 
524  matchBoolCondition(Finder, true, ConditionThenStmtId);
525  matchBoolCondition(Finder, false, ConditionElseStmtId);
526 
527  matchTernaryResult(Finder, true, TernaryId);
528  matchTernaryResult(Finder, false, TernaryNegatedId);
529 
530  matchIfReturnsBool(Finder, true, IfReturnsBoolId);
531  matchIfReturnsBool(Finder, false, IfReturnsNotBoolId);
532 
533  matchIfAssignsBool(Finder, true, IfAssignBoolId);
534  matchIfAssignsBool(Finder, false, IfAssignNotBoolId);
535 
536  matchCompoundIfReturnsBool(Finder, true, CompoundBoolId);
537  matchCompoundIfReturnsBool(Finder, false, CompoundNotBoolId);
538 }
539 
540 void SimplifyBooleanExprCheck::check(const MatchFinder::MatchResult &Result) {
541  if (Result.Nodes.getNodeAs<TranslationUnitDecl>("top"))
542  Visitor(this, Result).TraverseAST(*Result.Context);
543  else if (const Expr *TrueConditionRemoved =
544  getBoolLiteral(Result, ConditionThenStmtId))
545  replaceWithThenStatement(Result, TrueConditionRemoved);
546  else if (const Expr *FalseConditionRemoved =
547  getBoolLiteral(Result, ConditionElseStmtId))
548  replaceWithElseStatement(Result, FalseConditionRemoved);
549  else if (const auto *Ternary =
550  Result.Nodes.getNodeAs<ConditionalOperator>(TernaryId))
551  replaceWithCondition(Result, Ternary);
552  else if (const auto *TernaryNegated =
553  Result.Nodes.getNodeAs<ConditionalOperator>(TernaryNegatedId))
554  replaceWithCondition(Result, TernaryNegated, true);
555  else if (const auto *If = Result.Nodes.getNodeAs<IfStmt>(IfReturnsBoolId))
556  replaceWithReturnCondition(Result, If);
557  else if (const auto *IfNot =
558  Result.Nodes.getNodeAs<IfStmt>(IfReturnsNotBoolId))
559  replaceWithReturnCondition(Result, IfNot, true);
560  else if (const auto *IfAssign =
561  Result.Nodes.getNodeAs<IfStmt>(IfAssignBoolId))
562  replaceWithAssignment(Result, IfAssign);
563  else if (const auto *IfAssignNot =
564  Result.Nodes.getNodeAs<IfStmt>(IfAssignNotBoolId))
565  replaceWithAssignment(Result, IfAssignNot, true);
566  else if (const auto *Compound =
567  Result.Nodes.getNodeAs<CompoundStmt>(CompoundBoolId))
568  replaceCompoundReturnWithCondition(Result, Compound);
569  else if (const auto *Compound =
570  Result.Nodes.getNodeAs<CompoundStmt>(CompoundNotBoolId))
571  replaceCompoundReturnWithCondition(Result, Compound, true);
572 }
573 
574 void SimplifyBooleanExprCheck::issueDiag(
575  const ast_matchers::MatchFinder::MatchResult &Result, SourceLocation Loc,
576  StringRef Description, SourceRange ReplacementRange,
577  StringRef Replacement) {
578  CharSourceRange CharRange =
579  Lexer::makeFileCharRange(CharSourceRange::getTokenRange(ReplacementRange),
580  *Result.SourceManager, getLangOpts());
581 
582  DiagnosticBuilder Diag = diag(Loc, Description);
583  if (!containsDiscardedTokens(Result, CharRange))
584  Diag << FixItHint::CreateReplacement(CharRange, Replacement);
585 }
586 
587 void SimplifyBooleanExprCheck::replaceWithThenStatement(
588  const MatchFinder::MatchResult &Result, const Expr *TrueConditionRemoved) {
589  const auto *IfStatement = Result.Nodes.getNodeAs<IfStmt>(IfStmtId);
590  issueDiag(Result, TrueConditionRemoved->getBeginLoc(),
591  SimplifyConditionDiagnostic, IfStatement->getSourceRange(),
592  getText(Result, *IfStatement->getThen()));
593 }
594 
595 void SimplifyBooleanExprCheck::replaceWithElseStatement(
596  const MatchFinder::MatchResult &Result, const Expr *FalseConditionRemoved) {
597  const auto *IfStatement = Result.Nodes.getNodeAs<IfStmt>(IfStmtId);
598  const Stmt *ElseStatement = IfStatement->getElse();
599  issueDiag(Result, FalseConditionRemoved->getBeginLoc(),
600  SimplifyConditionDiagnostic, IfStatement->getSourceRange(),
601  ElseStatement ? getText(Result, *ElseStatement) : "");
602 }
603 
604 void SimplifyBooleanExprCheck::replaceWithCondition(
605  const MatchFinder::MatchResult &Result, const ConditionalOperator *Ternary,
606  bool Negated) {
607  std::string Replacement =
608  replacementExpression(Result, Negated, Ternary->getCond());
609  issueDiag(Result, Ternary->getTrueExpr()->getBeginLoc(),
610  "redundant boolean literal in ternary expression result",
611  Ternary->getSourceRange(), Replacement);
612 }
613 
614 void SimplifyBooleanExprCheck::replaceWithReturnCondition(
615  const MatchFinder::MatchResult &Result, const IfStmt *If, bool Negated) {
616  StringRef Terminator = isa<CompoundStmt>(If->getElse()) ? ";" : "";
617  std::string Condition = replacementExpression(Result, Negated, If->getCond());
618  std::string Replacement = ("return " + Condition + Terminator).str();
619  SourceLocation Start =
620  Result.Nodes.getNodeAs<CXXBoolLiteralExpr>(ThenLiteralId)->getBeginLoc();
621  issueDiag(Result, Start, SimplifyConditionalReturnDiagnostic,
622  If->getSourceRange(), Replacement);
623 }
624 
625 void SimplifyBooleanExprCheck::replaceCompoundReturnWithCondition(
626  const MatchFinder::MatchResult &Result, const CompoundStmt *Compound,
627  bool Negated) {
628  const auto *Ret = Result.Nodes.getNodeAs<ReturnStmt>(CompoundReturnId);
629 
630  // The body shouldn't be empty because the matcher ensures that it must
631  // contain at least two statements:
632  // 1) A `return` statement returning a boolean literal `false` or `true`
633  // 2) An `if` statement with no `else` clause that consists of a single
634  // `return` statement returning the opposite boolean literal `true` or
635  // `false`.
636  assert(Compound->size() >= 2);
637  const IfStmt *BeforeIf = nullptr;
638  CompoundStmt::const_body_iterator Current = Compound->body_begin();
639  CompoundStmt::const_body_iterator After = Compound->body_begin();
640  for (++After; After != Compound->body_end() && *Current != Ret;
641  ++Current, ++After) {
642  if (const auto *If = dyn_cast<IfStmt>(*Current)) {
643  if (const Expr *Lit = stmtReturnsBool(If, Negated)) {
644  if (*After == Ret) {
645  if (!ChainedConditionalReturn && BeforeIf)
646  continue;
647 
648  const Expr *Condition = If->getCond();
649  std::string Replacement =
650  "return " + replacementExpression(Result, Negated, Condition);
651  issueDiag(
652  Result, Lit->getBeginLoc(), SimplifyConditionalReturnDiagnostic,
653  SourceRange(If->getBeginLoc(), Ret->getEndLoc()), Replacement);
654  return;
655  }
656 
657  BeforeIf = If;
658  }
659  } else {
660  BeforeIf = nullptr;
661  }
662  }
663 }
664 
665 void SimplifyBooleanExprCheck::replaceWithAssignment(
666  const MatchFinder::MatchResult &Result, const IfStmt *IfAssign,
667  bool Negated) {
668  SourceRange Range = IfAssign->getSourceRange();
669  StringRef VariableName =
670  getText(Result, *Result.Nodes.getNodeAs<Expr>(IfAssignVariableId));
671  StringRef Terminator = isa<CompoundStmt>(IfAssign->getElse()) ? ";" : "";
672  std::string Condition =
673  replacementExpression(Result, Negated, IfAssign->getCond());
674  std::string Replacement =
675  (VariableName + " = " + Condition + Terminator).str();
676  SourceLocation Location =
677  Result.Nodes.getNodeAs<CXXBoolLiteralExpr>(IfAssignLocId)->getBeginLoc();
678  issueDiag(Result, Location,
679  "redundant boolean literal in conditional assignment", Range,
680  Replacement);
681 }
682 
683 } // namespace readability
684 } // namespace tidy
685 } // namespace clang
Range
CharSourceRange Range
SourceRange for the file name.
Definition: IncludeOrderCheck.cpp:38
Loc
SourceLocation Loc
Definition: KernelNameRestrictionCheck.cpp:45
clang::tidy::ClangTidyOptions::OptionMap
llvm::StringMap< ClangTidyValue > OptionMap
Definition: ClangTidyOptions.h:115
E
const Expr * E
Definition: AvoidBindCheck.cpp:88
Location
Definition: Modularize.cpp:382
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:540
clang::tidy::cppcoreguidelines::getSourceText
static std::string getSourceText(const CXXDestructorDecl &Destructor)
Definition: VirtualClassDestructorCheck.cpp:112
Text
std::string Text
Definition: HTMLGenerator.cpp:80
clang::tidy::ClangTidyCheck
Base class for all clang-tidy checks.
Definition: ClangTidyCheck.h:54
clang::tidy::modernize::getText
static StringRef getText(const Token &Tok, const SourceManager &Sources)
Definition: UseOverrideCheck.cpp:78
clang::tidy::ClangTidyCheck::getLangOpts
const LangOptions & getLangOpts() const
Returns the language options from the context.
Definition: ClangTidyCheck.h:420
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:515
clang::tidy::readability::SimplifyBooleanExprCheck::registerMatchers
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
Definition: SimplifyBooleanExprCheck.cpp:521
clang::tidy::ClangTidyCheck::Options
OptionsView Options
Definition: ClangTidyCheck.h:416
clang::tidy::readability::SimplifyBooleanExprCheck::Visitor
Definition: SimplifyBooleanExprCheck.cpp:341
clang::tidy::ClangTidyContext
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
Definition: ClangTidyDiagnosticConsumer.h:71
Description
const char * Description
Definition: Dexp.cpp:361
Name
static constexpr llvm::StringLiteral Name
Definition: UppercaseLiteralSuffixCheck.cpp:28
clang::tidy::readability::SimplifyBooleanExprCheck::Visitor::VisitBinaryOperator
bool VisitBinaryOperator(BinaryOperator *Op)
Definition: SimplifyBooleanExprCheck.cpp:347
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:364
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:25
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:120
clang::tidy::readability::SimplifyBooleanExprCheck::Visitor::Visitor
Visitor(SimplifyBooleanExprCheck *Check, const MatchFinder::MatchResult &Result)
Definition: SimplifyBooleanExprCheck.cpp:343