clang-tools  6.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.getLocStart();
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->getLocStart();
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->getLocStart();
101  SourceLocation ParentEndLoc =
102  cast<UnaryOperator>(Parent)->getSubExpr()->getLocStart();
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->getLocStart(), 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->getLocEnd(), 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->getLocStart(),
187  (Twine("static_cast<") + OtherType + ">" + (NeedParens ? "(" : ""))
188  .str());
189 
190  if (NeedParens) {
191  SourceLocation EndLoc = Lexer::getLocForEndOfToken(
192  Cast->getLocEnd(), 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  hasParent(explicitCastExpr())));
270  auto implicitCastFromBool = implicitCastExpr(
271  anyOf(hasCastKind(CK_IntegralCast), hasCastKind(CK_IntegralToFloating),
272  // Prior to C++11 cast from bool literal to pointer was allowed.
273  allOf(anyOf(hasCastKind(CK_NullToPointer),
274  hasCastKind(CK_NullToMemberPointer)),
275  hasSourceExpression(cxxBoolLiteral()))),
276  hasSourceExpression(expr(hasType(booleanType()))),
277  unless(exceptionCases));
278  auto boolXor =
279  binaryOperator(hasOperatorName("^"), hasLHS(implicitCastFromBool),
280  hasRHS(implicitCastFromBool));
281  Finder->addMatcher(
282  implicitCastExpr(
283  anyOf(hasCastKind(CK_IntegralToBoolean),
284  hasCastKind(CK_FloatingToBoolean),
285  hasCastKind(CK_PointerToBoolean),
286  hasCastKind(CK_MemberPointerToBoolean)),
287  // Exclude case of using if or while statements with variable
288  // declaration, e.g.:
289  // if (int var = functionCall()) {}
290  unless(
291  hasParent(stmt(anyOf(ifStmt(), whileStmt()), has(declStmt())))),
292  // Exclude cases common to implicit cast to and from bool.
293  unless(exceptionCases), unless(has(boolXor)),
294  // Retrive also parent statement, to check if we need additional
295  // parens in replacement.
296  anyOf(hasParent(stmt().bind("parentStmt")), anything()),
297  unless(isInTemplateInstantiation()),
298  unless(hasAncestor(functionTemplateDecl())))
299  .bind("implicitCastToBool"),
300  this);
301 
302  auto boolComparison = binaryOperator(
303  anyOf(hasOperatorName("=="), hasOperatorName("!=")),
304  hasLHS(implicitCastFromBool), hasRHS(implicitCastFromBool));
305  auto boolOpAssignment =
306  binaryOperator(anyOf(hasOperatorName("|="), hasOperatorName("&=")),
307  hasLHS(expr(hasType(booleanType()))));
308  Finder->addMatcher(
309  implicitCastExpr(
310  implicitCastFromBool,
311  // Exclude comparisons of bools, as they are always cast to integers
312  // in such context:
313  // bool_expr_a == bool_expr_b
314  // bool_expr_a != bool_expr_b
315  unless(hasParent(binaryOperator(
316  anyOf(boolComparison, boolXor, boolOpAssignment)))),
317  // Check also for nested casts, for example: bool -> int -> float.
318  anyOf(hasParent(implicitCastExpr().bind("furtherImplicitCast")),
319  anything()),
320  unless(isInTemplateInstantiation()),
321  unless(hasAncestor(functionTemplateDecl())))
322  .bind("implicitCastFromBool"),
323  this);
324 }
325 
327  const MatchFinder::MatchResult &Result) {
328  if (const auto *CastToBool =
329  Result.Nodes.getNodeAs<ImplicitCastExpr>("implicitCastToBool")) {
330  const auto *Parent = Result.Nodes.getNodeAs<Stmt>("parentStmt");
331  return handleCastToBool(CastToBool, Parent, *Result.Context);
332  }
333 
334  if (const auto *CastFromBool =
335  Result.Nodes.getNodeAs<ImplicitCastExpr>("implicitCastFromBool")) {
336  const auto *NextImplicitCast =
337  Result.Nodes.getNodeAs<ImplicitCastExpr>("furtherImplicitCast");
338  return handleCastFromBool(CastFromBool, NextImplicitCast, *Result.Context);
339  }
340 }
341 
342 void ImplicitBoolConversionCheck::handleCastToBool(const ImplicitCastExpr *Cast,
343  const Stmt *Parent,
344  ASTContext &Context) {
345  if (AllowPointerConditions &&
346  (Cast->getCastKind() == CK_PointerToBoolean ||
347  Cast->getCastKind() == CK_MemberPointerToBoolean) &&
348  isCastAllowedInCondition(Cast, Context)) {
349  return;
350  }
351 
352  if (AllowIntegerConditions && Cast->getCastKind() == CK_IntegralToBoolean &&
353  isCastAllowedInCondition(Cast, Context)) {
354  return;
355  }
356 
357  auto Diag = diag(Cast->getLocStart(), "implicit conversion %0 -> bool")
358  << Cast->getSubExpr()->getType();
359 
360  StringRef EquivalentLiteral =
361  getEquivalentBoolLiteralForExpr(Cast->getSubExpr(), Context);
362  if (!EquivalentLiteral.empty()) {
363  Diag << tooling::fixit::createReplacement(*Cast, EquivalentLiteral);
364  } else {
365  fixGenericExprCastToBool(Diag, Cast, Parent, Context);
366  }
367 }
368 
369 void ImplicitBoolConversionCheck::handleCastFromBool(
370  const ImplicitCastExpr *Cast, const ImplicitCastExpr *NextImplicitCast,
371  ASTContext &Context) {
372  QualType DestType =
373  NextImplicitCast ? NextImplicitCast->getType() : Cast->getType();
374  auto Diag = diag(Cast->getLocStart(), "implicit conversion bool -> %0")
375  << DestType;
376 
377  if (const auto *BoolLiteral =
378  dyn_cast<CXXBoolLiteralExpr>(Cast->getSubExpr())) {
379  Diag << tooling::fixit::createReplacement(
380  *Cast, getEquivalentForBoolLiteral(BoolLiteral, DestType, Context));
381  } else {
382  fixGenericExprCastFromBool(Diag, Cast, Context, DestType.getAsString());
383  }
384 }
385 
386 } // namespace readability
387 } // namespace tidy
388 } // namespace clang
SourceLocation Loc
&#39;#&#39; location in the include directive
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:449
StringHandle Name
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.
std::map< std::string, std::string > OptionMap
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...
AST_MATCHER(VarDecl, isAsm)
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check&#39;s name.
Definition: ClangTidy.cpp:416