clang-tools  8.0.0
ImplicitBoolConversionCheck.cpp
Go to the documentation of this file.
1 //===--- ImplicitBoolConversionCheck.cpp - clang-tidy----------------------===//
2 //
3 // The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
11 #include "clang/AST/ASTContext.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 #include "clang/Lex/Lexer.h"
14 #include "clang/Tooling/FixIt.h"
15 #include <queue>
16 
17 using namespace clang::ast_matchers;
18 
19 namespace clang {
20 namespace tidy {
21 namespace readability {
22 
23 namespace {
24 
25 AST_MATCHER(Stmt, isMacroExpansion) {
26  SourceManager &SM = Finder->getASTContext().getSourceManager();
27  SourceLocation Loc = Node.getBeginLoc();
28  return SM.isMacroBodyExpansion(Loc) || SM.isMacroArgExpansion(Loc);
29 }
30 
31 bool isNULLMacroExpansion(const Stmt *Statement, ASTContext &Context) {
32  SourceManager &SM = Context.getSourceManager();
33  const LangOptions &LO = Context.getLangOpts();
34  SourceLocation Loc = Statement->getBeginLoc();
35  return SM.isMacroBodyExpansion(Loc) &&
36  Lexer::getImmediateMacroName(Loc, SM, LO) == "NULL";
37 }
38 
39 AST_MATCHER(Stmt, isNULLMacroExpansion) {
40  return isNULLMacroExpansion(&Node, Finder->getASTContext());
41 }
42 
43 StringRef getZeroLiteralToCompareWithForType(CastKind CastExprKind,
44  QualType Type,
45  ASTContext &Context) {
46  switch (CastExprKind) {
47  case CK_IntegralToBoolean:
48  return Type->isUnsignedIntegerType() ? "0u" : "0";
49 
50  case CK_FloatingToBoolean:
51  return Context.hasSameType(Type, Context.FloatTy) ? "0.0f" : "0.0";
52 
53  case CK_PointerToBoolean:
54  case CK_MemberPointerToBoolean: // Fall-through on purpose.
55  return Context.getLangOpts().CPlusPlus11 ? "nullptr" : "0";
56 
57  default:
58  llvm_unreachable("Unexpected cast kind");
59  }
60 }
61 
62 bool isUnaryLogicalNotOperator(const Stmt *Statement) {
63  const auto *UnaryOperatorExpr = dyn_cast<UnaryOperator>(Statement);
64  return UnaryOperatorExpr && UnaryOperatorExpr->getOpcode() == UO_LNot;
65 }
66 
67 bool areParensNeededForOverloadedOperator(OverloadedOperatorKind OperatorKind) {
68  switch (OperatorKind) {
69  case OO_New:
70  case OO_Delete: // Fall-through on purpose.
71  case OO_Array_New:
72  case OO_Array_Delete:
73  case OO_ArrowStar:
74  case OO_Arrow:
75  case OO_Call:
76  case OO_Subscript:
77  return false;
78 
79  default:
80  return true;
81  }
82 }
83 
84 bool areParensNeededForStatement(const Stmt *Statement) {
85  if (const auto *OperatorCall = dyn_cast<CXXOperatorCallExpr>(Statement)) {
86  return areParensNeededForOverloadedOperator(OperatorCall->getOperator());
87  }
88 
89  return isa<BinaryOperator>(Statement) || isa<UnaryOperator>(Statement);
90 }
91 
92 void fixGenericExprCastToBool(DiagnosticBuilder &Diag,
93  const ImplicitCastExpr *Cast, const Stmt *Parent,
94  ASTContext &Context) {
95  // In case of expressions like (! integer), we should remove the redundant not
96  // operator and use inverted comparison (integer == 0).
97  bool InvertComparison =
98  Parent != nullptr && isUnaryLogicalNotOperator(Parent);
99  if (InvertComparison) {
100  SourceLocation ParentStartLoc = Parent->getBeginLoc();
101  SourceLocation ParentEndLoc =
102  cast<UnaryOperator>(Parent)->getSubExpr()->getBeginLoc();
103  Diag << FixItHint::CreateRemoval(
104  CharSourceRange::getCharRange(ParentStartLoc, ParentEndLoc));
105 
106  Parent = Context.getParents(*Parent)[0].get<Stmt>();
107  }
108 
109  const Expr *SubExpr = Cast->getSubExpr();
110 
111  bool NeedInnerParens = areParensNeededForStatement(SubExpr);
112  bool NeedOuterParens =
113  Parent != nullptr && areParensNeededForStatement(Parent);
114 
115  std::string StartLocInsertion;
116 
117  if (NeedOuterParens) {
118  StartLocInsertion += "(";
119  }
120  if (NeedInnerParens) {
121  StartLocInsertion += "(";
122  }
123 
124  if (!StartLocInsertion.empty()) {
125  Diag << FixItHint::CreateInsertion(Cast->getBeginLoc(), StartLocInsertion);
126  }
127 
128  std::string EndLocInsertion;
129 
130  if (NeedInnerParens) {
131  EndLocInsertion += ")";
132  }
133 
134  if (InvertComparison) {
135  EndLocInsertion += " == ";
136  } else {
137  EndLocInsertion += " != ";
138  }
139 
140  EndLocInsertion += getZeroLiteralToCompareWithForType(
141  Cast->getCastKind(), SubExpr->getType(), Context);
142 
143  if (NeedOuterParens) {
144  EndLocInsertion += ")";
145  }
146 
147  SourceLocation EndLoc = Lexer::getLocForEndOfToken(
148  Cast->getEndLoc(), 0, Context.getSourceManager(), Context.getLangOpts());
149  Diag << FixItHint::CreateInsertion(EndLoc, EndLocInsertion);
150 }
151 
152 StringRef getEquivalentBoolLiteralForExpr(const Expr *Expression,
153  ASTContext &Context) {
154  if (isNULLMacroExpansion(Expression, Context)) {
155  return "false";
156  }
157 
158  if (const auto *IntLit = dyn_cast<IntegerLiteral>(Expression)) {
159  return (IntLit->getValue() == 0) ? "false" : "true";
160  }
161 
162  if (const auto *FloatLit = dyn_cast<FloatingLiteral>(Expression)) {
163  llvm::APFloat FloatLitAbsValue = FloatLit->getValue();
164  FloatLitAbsValue.clearSign();
165  return (FloatLitAbsValue.bitcastToAPInt() == 0) ? "false" : "true";
166  }
167 
168  if (const auto *CharLit = dyn_cast<CharacterLiteral>(Expression)) {
169  return (CharLit->getValue() == 0) ? "false" : "true";
170  }
171 
172  if (isa<StringLiteral>(Expression->IgnoreCasts())) {
173  return "true";
174  }
175 
176  return StringRef();
177 }
178 
179 void fixGenericExprCastFromBool(DiagnosticBuilder &Diag,
180  const ImplicitCastExpr *Cast,
181  ASTContext &Context, StringRef OtherType) {
182  const Expr *SubExpr = Cast->getSubExpr();
183  bool NeedParens = !isa<ParenExpr>(SubExpr);
184 
185  Diag << FixItHint::CreateInsertion(
186  Cast->getBeginLoc(),
187  (Twine("static_cast<") + OtherType + ">" + (NeedParens ? "(" : ""))
188  .str());
189 
190  if (NeedParens) {
191  SourceLocation EndLoc = Lexer::getLocForEndOfToken(
192  Cast->getEndLoc(), 0, Context.getSourceManager(),
193  Context.getLangOpts());
194 
195  Diag << FixItHint::CreateInsertion(EndLoc, ")");
196  }
197 }
198 
199 StringRef getEquivalentForBoolLiteral(const CXXBoolLiteralExpr *BoolLiteral,
200  QualType DestType, ASTContext &Context) {
201  // Prior to C++11, false literal could be implicitly converted to pointer.
202  if (!Context.getLangOpts().CPlusPlus11 &&
203  (DestType->isPointerType() || DestType->isMemberPointerType()) &&
204  BoolLiteral->getValue() == false) {
205  return "0";
206  }
207 
208  if (DestType->isFloatingType()) {
209  if (Context.hasSameType(DestType, Context.FloatTy)) {
210  return BoolLiteral->getValue() ? "1.0f" : "0.0f";
211  }
212  return BoolLiteral->getValue() ? "1.0" : "0.0";
213  }
214 
215  if (DestType->isUnsignedIntegerType()) {
216  return BoolLiteral->getValue() ? "1u" : "0u";
217  }
218  return BoolLiteral->getValue() ? "1" : "0";
219 }
220 
221 bool isCastAllowedInCondition(const ImplicitCastExpr *Cast,
222  ASTContext &Context) {
223  std::queue<const Stmt *> Q;
224  Q.push(Cast);
225  while (!Q.empty()) {
226  for (const auto &N : Context.getParents(*Q.front())) {
227  const Stmt *S = N.get<Stmt>();
228  if (!S)
229  return false;
230  if (isa<IfStmt>(S) || isa<ConditionalOperator>(S) || isa<ForStmt>(S) ||
231  isa<WhileStmt>(S) || isa<BinaryConditionalOperator>(S))
232  return true;
233  if (isa<ParenExpr>(S) || isa<ImplicitCastExpr>(S) ||
234  isUnaryLogicalNotOperator(S) ||
235  (isa<BinaryOperator>(S) && cast<BinaryOperator>(S)->isLogicalOp())) {
236  Q.push(S);
237  } else {
238  return false;
239  }
240  }
241  Q.pop();
242  }
243  return false;
244 }
245 
246 } // anonymous namespace
247 
248 ImplicitBoolConversionCheck::ImplicitBoolConversionCheck(
249  StringRef Name, ClangTidyContext *Context)
250  : ClangTidyCheck(Name, Context),
251  AllowIntegerConditions(Options.get("AllowIntegerConditions", false)),
252  AllowPointerConditions(Options.get("AllowPointerConditions", false)) {}
253 
256  Options.store(Opts, "AllowIntegerConditions", AllowIntegerConditions);
257  Options.store(Opts, "AllowPointerConditions", AllowPointerConditions);
258 }
259 
261  // This check doesn't make much sense if we run it on language without
262  // built-in bool support.
263  if (!getLangOpts().Bool) {
264  return;
265  }
266 
267  auto exceptionCases =
268  expr(anyOf(allOf(isMacroExpansion(), unless(isNULLMacroExpansion())),
269  has(ignoringImplicit(memberExpr(hasDeclaration(fieldDecl(hasBitWidth(1)))))),
270  hasParent(explicitCastExpr())));
271  auto implicitCastFromBool = implicitCastExpr(
272  anyOf(hasCastKind(CK_IntegralCast), hasCastKind(CK_IntegralToFloating),
273  // Prior to C++11 cast from bool literal to pointer was allowed.
274  allOf(anyOf(hasCastKind(CK_NullToPointer),
275  hasCastKind(CK_NullToMemberPointer)),
276  hasSourceExpression(cxxBoolLiteral()))),
277  hasSourceExpression(expr(hasType(booleanType()))),
278  unless(exceptionCases));
279  auto boolXor =
280  binaryOperator(hasOperatorName("^"), hasLHS(implicitCastFromBool),
281  hasRHS(implicitCastFromBool));
282  Finder->addMatcher(
283  implicitCastExpr(
284  anyOf(hasCastKind(CK_IntegralToBoolean),
285  hasCastKind(CK_FloatingToBoolean),
286  hasCastKind(CK_PointerToBoolean),
287  hasCastKind(CK_MemberPointerToBoolean)),
288  // Exclude case of using if or while statements with variable
289  // declaration, e.g.:
290  // if (int var = functionCall()) {}
291  unless(
292  hasParent(stmt(anyOf(ifStmt(), whileStmt()), has(declStmt())))),
293  // Exclude cases common to implicit cast to and from bool.
294  unless(exceptionCases), unless(has(boolXor)),
295  // Retrive also parent statement, to check if we need additional
296  // parens in replacement.
297  anyOf(hasParent(stmt().bind("parentStmt")), anything()),
298  unless(isInTemplateInstantiation()),
299  unless(hasAncestor(functionTemplateDecl())))
300  .bind("implicitCastToBool"),
301  this);
302 
303  auto boolComparison = binaryOperator(
304  anyOf(hasOperatorName("=="), hasOperatorName("!=")),
305  hasLHS(implicitCastFromBool), hasRHS(implicitCastFromBool));
306  auto boolOpAssignment =
307  binaryOperator(anyOf(hasOperatorName("|="), hasOperatorName("&=")),
308  hasLHS(expr(hasType(booleanType()))));
309  auto bitfieldAssignment = binaryOperator(
310  hasLHS(memberExpr(hasDeclaration(fieldDecl(hasBitWidth(1))))));
311  auto bitfieldConstruct = cxxConstructorDecl(hasDescendant(cxxCtorInitializer(
312  withInitializer(equalsBoundNode("implicitCastFromBool")),
313  forField(hasBitWidth(1)))));
314  Finder->addMatcher(
315  implicitCastExpr(
316  implicitCastFromBool,
317  // Exclude comparisons of bools, as they are always cast to integers
318  // in such context:
319  // bool_expr_a == bool_expr_b
320  // bool_expr_a != bool_expr_b
321  unless(hasParent(binaryOperator(anyOf(
322  boolComparison, boolXor, boolOpAssignment, bitfieldAssignment)))),
323  implicitCastExpr().bind("implicitCastFromBool"),
324  unless(hasParent(bitfieldConstruct)),
325  // Check also for nested casts, for example: bool -> int -> float.
326  anyOf(hasParent(implicitCastExpr().bind("furtherImplicitCast")),
327  anything()),
328  unless(isInTemplateInstantiation()),
329  unless(hasAncestor(functionTemplateDecl()))),
330  this);
331 }
332 
334  const MatchFinder::MatchResult &Result) {
335  if (const auto *CastToBool =
336  Result.Nodes.getNodeAs<ImplicitCastExpr>("implicitCastToBool")) {
337  const auto *Parent = Result.Nodes.getNodeAs<Stmt>("parentStmt");
338  return handleCastToBool(CastToBool, Parent, *Result.Context);
339  }
340 
341  if (const auto *CastFromBool =
342  Result.Nodes.getNodeAs<ImplicitCastExpr>("implicitCastFromBool")) {
343  const auto *NextImplicitCast =
344  Result.Nodes.getNodeAs<ImplicitCastExpr>("furtherImplicitCast");
345  return handleCastFromBool(CastFromBool, NextImplicitCast, *Result.Context);
346  }
347 }
348 
349 void ImplicitBoolConversionCheck::handleCastToBool(const ImplicitCastExpr *Cast,
350  const Stmt *Parent,
351  ASTContext &Context) {
352  if (AllowPointerConditions &&
353  (Cast->getCastKind() == CK_PointerToBoolean ||
354  Cast->getCastKind() == CK_MemberPointerToBoolean) &&
355  isCastAllowedInCondition(Cast, Context)) {
356  return;
357  }
358 
359  if (AllowIntegerConditions && Cast->getCastKind() == CK_IntegralToBoolean &&
360  isCastAllowedInCondition(Cast, Context)) {
361  return;
362  }
363 
364  auto Diag = diag(Cast->getBeginLoc(), "implicit conversion %0 -> bool")
365  << Cast->getSubExpr()->getType();
366 
367  StringRef EquivalentLiteral =
368  getEquivalentBoolLiteralForExpr(Cast->getSubExpr(), Context);
369  if (!EquivalentLiteral.empty()) {
370  Diag << tooling::fixit::createReplacement(*Cast, EquivalentLiteral);
371  } else {
372  fixGenericExprCastToBool(Diag, Cast, Parent, Context);
373  }
374 }
375 
376 void ImplicitBoolConversionCheck::handleCastFromBool(
377  const ImplicitCastExpr *Cast, const ImplicitCastExpr *NextImplicitCast,
378  ASTContext &Context) {
379  QualType DestType =
380  NextImplicitCast ? NextImplicitCast->getType() : Cast->getType();
381  auto Diag = diag(Cast->getBeginLoc(), "implicit conversion bool -> %0")
382  << DestType;
383 
384  if (const auto *BoolLiteral =
385  dyn_cast<CXXBoolLiteralExpr>(Cast->getSubExpr())) {
386  Diag << tooling::fixit::createReplacement(
387  *Cast, getEquivalentForBoolLiteral(BoolLiteral, DestType, Context));
388  } else {
389  fixGenericExprCastFromBool(Diag, Cast, Context, DestType.getAsString());
390  }
391 }
392 
393 } // namespace readability
394 } // namespace tidy
395 } // namespace clang
SourceLocation Loc
&#39;#&#39; location in the include directive
AST_MATCHER(BinaryOperator, isAssignmentOperator)
Definition: Matchers.h:20
void store(ClangTidyOptions::OptionMap &Options, StringRef LocalName, StringRef Value) const
Stores an option with the check-local name LocalName with string value Value to Options.
Definition: ClangTidy.cpp:473
LangOptions getLangOpts() const
Returns the language options from the context.
Definition: ClangTidy.h:187
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
Base class for all clang-tidy checks.
Definition: ClangTidy.h:127
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
static constexpr llvm::StringLiteral Name
std::map< std::string, std::string > OptionMap
llvm::Optional< llvm::Expected< tooling::AtomicChanges > > Result
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Should store all options supported by this check with their current values or default values for opti...
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check&#39;s name.
Definition: ClangTidy.cpp:438