clang-tools  4.0.0
ImplicitBoolCastCheck.cpp
Go to the documentation of this file.
1 //===--- ImplicitBoolCastCheck.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 
10 #include "ImplicitBoolCastCheck.h"
11 #include "clang/AST/ASTContext.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 #include "clang/Lex/Lexer.h"
14 
15 using namespace clang::ast_matchers;
16 
17 namespace clang {
18 namespace tidy {
19 namespace readability {
20 
21 namespace {
22 
23 AST_MATCHER(Stmt, isMacroExpansion) {
24  SourceManager &SM = Finder->getASTContext().getSourceManager();
25  SourceLocation Loc = Node.getLocStart();
26  return SM.isMacroBodyExpansion(Loc) || SM.isMacroArgExpansion(Loc);
27 }
28 
29 bool isNULLMacroExpansion(const Stmt *Statement, ASTContext &Context) {
30  SourceManager &SM = Context.getSourceManager();
31  const LangOptions &LO = Context.getLangOpts();
32  SourceLocation Loc = Statement->getLocStart();
33  return SM.isMacroBodyExpansion(Loc) &&
34  clang::Lexer::getImmediateMacroName(Loc, SM, LO) == "NULL";
35 }
36 
37 AST_MATCHER(Stmt, isNULLMacroExpansion) {
38  return isNULLMacroExpansion(&Node, Finder->getASTContext());
39 }
40 
41 ast_matchers::internal::Matcher<Expr> createExceptionCasesMatcher() {
42  return expr(anyOf(hasParent(explicitCastExpr()),
43  allOf(isMacroExpansion(), unless(isNULLMacroExpansion())),
44  isInTemplateInstantiation(),
45  hasAncestor(functionTemplateDecl())));
46 }
47 
48 StatementMatcher createImplicitCastFromBoolMatcher() {
49  return implicitCastExpr(
50  unless(createExceptionCasesMatcher()),
51  anyOf(hasCastKind(CK_IntegralCast), hasCastKind(CK_IntegralToFloating),
52  // Prior to C++11 cast from bool literal to pointer was allowed.
53  allOf(anyOf(hasCastKind(CK_NullToPointer),
54  hasCastKind(CK_NullToMemberPointer)),
55  hasSourceExpression(cxxBoolLiteral()))),
56  hasSourceExpression(expr(hasType(qualType(booleanType())))));
57 }
58 
59 StringRef
60 getZeroLiteralToCompareWithForGivenType(CastKind CastExpressionKind,
61  QualType CastSubExpressionType,
62  ASTContext &Context) {
63  switch (CastExpressionKind) {
64  case CK_IntegralToBoolean:
65  return CastSubExpressionType->isUnsignedIntegerType() ? "0u" : "0";
66 
67  case CK_FloatingToBoolean:
68  return Context.hasSameType(CastSubExpressionType, Context.FloatTy) ? "0.0f"
69  : "0.0";
70 
71  case CK_PointerToBoolean:
72  case CK_MemberPointerToBoolean: // Fall-through on purpose.
73  return Context.getLangOpts().CPlusPlus11 ? "nullptr" : "0";
74 
75  default:
76  llvm_unreachable("Unexpected cast kind");
77  }
78 }
79 
80 bool isUnaryLogicalNotOperator(const Stmt *Statement) {
81  const auto *UnaryOperatorExpression =
82  llvm::dyn_cast<UnaryOperator>(Statement);
83  return UnaryOperatorExpression != nullptr &&
84  UnaryOperatorExpression->getOpcode() == UO_LNot;
85 }
86 
87 bool areParensNeededForOverloadedOperator(OverloadedOperatorKind OperatorKind) {
88  switch (OperatorKind) {
89  case OO_New:
90  case OO_Delete: // Fall-through on purpose.
91  case OO_Array_New:
92  case OO_Array_Delete:
93  case OO_ArrowStar:
94  case OO_Arrow:
95  case OO_Call:
96  case OO_Subscript:
97  return false;
98 
99  default:
100  return true;
101  }
102 }
103 
104 bool areParensNeededForStatement(const Stmt *Statement) {
105  if (const auto *OverloadedOperatorCall =
106  llvm::dyn_cast<CXXOperatorCallExpr>(Statement)) {
107  return areParensNeededForOverloadedOperator(
108  OverloadedOperatorCall->getOperator());
109  }
110 
111  return llvm::isa<BinaryOperator>(Statement) ||
112  llvm::isa<UnaryOperator>(Statement);
113 }
114 
115 void addFixItHintsForGenericExpressionCastToBool(
116  DiagnosticBuilder &Diagnostic, const ImplicitCastExpr *CastExpression,
117  const Stmt *ParentStatement, ASTContext &Context) {
118  // In case of expressions like (! integer), we should remove the redundant not
119  // operator and use inverted comparison (integer == 0).
120  bool InvertComparison =
121  ParentStatement != nullptr && isUnaryLogicalNotOperator(ParentStatement);
122  if (InvertComparison) {
123  SourceLocation ParentStartLoc = ParentStatement->getLocStart();
124  SourceLocation ParentEndLoc =
125  llvm::cast<UnaryOperator>(ParentStatement)->getSubExpr()->getLocStart();
126  Diagnostic.AddFixItHint(FixItHint::CreateRemoval(
127  CharSourceRange::getCharRange(ParentStartLoc, ParentEndLoc)));
128 
129  auto FurtherParents = Context.getParents(*ParentStatement);
130  ParentStatement = FurtherParents[0].get<Stmt>();
131  }
132 
133  const Expr *SubExpression = CastExpression->getSubExpr();
134 
135  bool NeedInnerParens = areParensNeededForStatement(SubExpression);
136  bool NeedOuterParens = ParentStatement != nullptr &&
137  areParensNeededForStatement(ParentStatement);
138 
139  std::string StartLocInsertion;
140 
141  if (NeedOuterParens) {
142  StartLocInsertion += "(";
143  }
144  if (NeedInnerParens) {
145  StartLocInsertion += "(";
146  }
147 
148  if (!StartLocInsertion.empty()) {
149  SourceLocation StartLoc = CastExpression->getLocStart();
150  Diagnostic.AddFixItHint(
151  FixItHint::CreateInsertion(StartLoc, StartLocInsertion));
152  }
153 
154  std::string EndLocInsertion;
155 
156  if (NeedInnerParens) {
157  EndLocInsertion += ")";
158  }
159 
160  if (InvertComparison) {
161  EndLocInsertion += " == ";
162  } else {
163  EndLocInsertion += " != ";
164  }
165 
166  EndLocInsertion += getZeroLiteralToCompareWithForGivenType(
167  CastExpression->getCastKind(), SubExpression->getType(), Context);
168 
169  if (NeedOuterParens) {
170  EndLocInsertion += ")";
171  }
172 
173  SourceLocation EndLoc = Lexer::getLocForEndOfToken(
174  CastExpression->getLocEnd(), 0, Context.getSourceManager(),
175  Context.getLangOpts());
176  Diagnostic.AddFixItHint(FixItHint::CreateInsertion(EndLoc, EndLocInsertion));
177 }
178 
179 StringRef getEquivalentBoolLiteralForExpression(const Expr *Expression,
180  ASTContext &Context) {
181  if (isNULLMacroExpansion(Expression, Context)) {
182  return "false";
183  }
184 
185  if (const auto *IntLit = llvm::dyn_cast<IntegerLiteral>(Expression)) {
186  return (IntLit->getValue() == 0) ? "false" : "true";
187  }
188 
189  if (const auto *FloatLit = llvm::dyn_cast<FloatingLiteral>(Expression)) {
190  llvm::APFloat FloatLitAbsValue = FloatLit->getValue();
191  FloatLitAbsValue.clearSign();
192  return (FloatLitAbsValue.bitcastToAPInt() == 0) ? "false" : "true";
193  }
194 
195  if (const auto *CharLit = llvm::dyn_cast<CharacterLiteral>(Expression)) {
196  return (CharLit->getValue() == 0) ? "false" : "true";
197  }
198 
199  if (llvm::isa<StringLiteral>(Expression->IgnoreCasts())) {
200  return "true";
201  }
202 
203  return StringRef();
204 }
205 
206 void addFixItHintsForLiteralCastToBool(DiagnosticBuilder &Diagnostic,
207  const ImplicitCastExpr *CastExpression,
208  StringRef EquivalentLiteralExpression) {
209  SourceLocation StartLoc = CastExpression->getLocStart();
210  SourceLocation EndLoc = CastExpression->getLocEnd();
211 
212  Diagnostic.AddFixItHint(FixItHint::CreateReplacement(
213  CharSourceRange::getTokenRange(StartLoc, EndLoc),
214  EquivalentLiteralExpression));
215 }
216 
217 void addFixItHintsForGenericExpressionCastFromBool(
218  DiagnosticBuilder &Diagnostic, const ImplicitCastExpr *CastExpression,
219  ASTContext &Context, StringRef OtherType) {
220  const Expr *SubExpression = CastExpression->getSubExpr();
221  bool NeedParens = !llvm::isa<ParenExpr>(SubExpression);
222 
223  std::string StartLocInsertion = "static_cast<";
224  StartLocInsertion += OtherType.str();
225  StartLocInsertion += ">";
226  if (NeedParens) {
227  StartLocInsertion += "(";
228  }
229 
230  SourceLocation StartLoc = CastExpression->getLocStart();
231  Diagnostic.AddFixItHint(
232  FixItHint::CreateInsertion(StartLoc, StartLocInsertion));
233 
234  if (NeedParens) {
235  SourceLocation EndLoc = Lexer::getLocForEndOfToken(
236  CastExpression->getLocEnd(), 0, Context.getSourceManager(),
237  Context.getLangOpts());
238 
239  Diagnostic.AddFixItHint(FixItHint::CreateInsertion(EndLoc, ")"));
240  }
241 }
242 
243 StringRef getEquivalentLiteralForBoolLiteral(
244  const CXXBoolLiteralExpr *BoolLiteralExpression, QualType DestinationType,
245  ASTContext &Context) {
246  // Prior to C++11, false literal could be implicitly converted to pointer.
247  if (!Context.getLangOpts().CPlusPlus11 &&
248  (DestinationType->isPointerType() ||
249  DestinationType->isMemberPointerType()) &&
250  BoolLiteralExpression->getValue() == false) {
251  return "0";
252  }
253 
254  if (DestinationType->isFloatingType()) {
255  if (BoolLiteralExpression->getValue() == true) {
256  return Context.hasSameType(DestinationType, Context.FloatTy) ? "1.0f"
257  : "1.0";
258  }
259  return Context.hasSameType(DestinationType, Context.FloatTy) ? "0.0f"
260  : "0.0";
261  }
262 
263  if (BoolLiteralExpression->getValue() == true) {
264  return DestinationType->isUnsignedIntegerType() ? "1u" : "1";
265  }
266  return DestinationType->isUnsignedIntegerType() ? "0u" : "0";
267 }
268 
269 void addFixItHintsForLiteralCastFromBool(DiagnosticBuilder &Diagnostic,
270  const ImplicitCastExpr *CastExpression,
271  ASTContext &Context,
272  QualType DestinationType) {
273  SourceLocation StartLoc = CastExpression->getLocStart();
274  SourceLocation EndLoc = CastExpression->getLocEnd();
275  const auto *BoolLiteralExpression =
276  llvm::dyn_cast<CXXBoolLiteralExpr>(CastExpression->getSubExpr());
277 
278  Diagnostic.AddFixItHint(FixItHint::CreateReplacement(
279  CharSourceRange::getTokenRange(StartLoc, EndLoc),
280  getEquivalentLiteralForBoolLiteral(BoolLiteralExpression, DestinationType,
281  Context)));
282 }
283 
284 StatementMatcher createConditionalExpressionMatcher() {
285  return stmt(anyOf(ifStmt(), conditionalOperator(),
286  parenExpr(hasParent(conditionalOperator()))));
287 }
288 
289 bool isAllowedConditionalCast(const ImplicitCastExpr *CastExpression,
290  ASTContext &Context) {
291  auto AllowedConditionalMatcher = stmt(hasParent(stmt(
292  anyOf(createConditionalExpressionMatcher(),
293  unaryOperator(hasOperatorName("!"),
294  hasParent(createConditionalExpressionMatcher()))))));
295 
296  auto MatchResult = match(AllowedConditionalMatcher, *CastExpression, Context);
297  return !MatchResult.empty();
298 }
299 
300 } // anonymous namespace
301 
302 ImplicitBoolCastCheck::ImplicitBoolCastCheck(StringRef Name,
303  ClangTidyContext *Context)
304  : ClangTidyCheck(Name, Context),
305  AllowConditionalIntegerCasts(
306  Options.get("AllowConditionalIntegerCasts", false)),
307  AllowConditionalPointerCasts(
308  Options.get("AllowConditionalPointerCasts", false)) {}
309 
311  Options.store(Opts, "AllowConditionalIntegerCasts",
312  AllowConditionalIntegerCasts);
313  Options.store(Opts, "AllowConditionalPointerCasts",
314  AllowConditionalPointerCasts);
315 }
316 
318  // This check doesn't make much sense if we run it on language without
319  // built-in bool support.
320  if (!getLangOpts().Bool) {
321  return;
322  }
323 
324  Finder->addMatcher(
325  implicitCastExpr(
326  // Exclude cases common to implicit cast to and from bool.
327  unless(createExceptionCasesMatcher()),
328  // Exclude case of using if or while statements with variable
329  // declaration, e.g.:
330  // if (int var = functionCall()) {}
331  unless(
332  hasParent(stmt(anyOf(ifStmt(), whileStmt()), has(declStmt())))),
333  anyOf(hasCastKind(CK_IntegralToBoolean),
334  hasCastKind(CK_FloatingToBoolean),
335  hasCastKind(CK_PointerToBoolean),
336  hasCastKind(CK_MemberPointerToBoolean)),
337  // Retrive also parent statement, to check if we need additional
338  // parens in replacement.
339  anyOf(hasParent(stmt().bind("parentStmt")), anything()))
340  .bind("implicitCastToBool"),
341  this);
342 
343  Finder->addMatcher(
344  implicitCastExpr(
345  createImplicitCastFromBoolMatcher(),
346  // Exclude comparisons of bools, as they are always cast to integers
347  // in such context:
348  // bool_expr_a == bool_expr_b
349  // bool_expr_a != bool_expr_b
350  unless(hasParent(binaryOperator(
351  anyOf(hasOperatorName("=="), hasOperatorName("!=")),
352  hasLHS(createImplicitCastFromBoolMatcher()),
353  hasRHS(createImplicitCastFromBoolMatcher())))),
354  // Check also for nested casts, for example: bool -> int -> float.
355  anyOf(hasParent(implicitCastExpr().bind("furtherImplicitCast")),
356  anything()))
357  .bind("implicitCastFromBool"),
358  this);
359 }
360 
361 void ImplicitBoolCastCheck::check(const MatchFinder::MatchResult &Result) {
362  if (const auto *CastToBool =
363  Result.Nodes.getNodeAs<ImplicitCastExpr>("implicitCastToBool")) {
364  const auto *ParentStatement = Result.Nodes.getNodeAs<Stmt>("parentStmt");
365  return handleCastToBool(CastToBool, ParentStatement, *Result.Context);
366  }
367 
368  if (const auto *CastFromBool =
369  Result.Nodes.getNodeAs<ImplicitCastExpr>("implicitCastFromBool")) {
370  const auto *FurtherImplicitCastExpression =
371  Result.Nodes.getNodeAs<ImplicitCastExpr>("furtherImplicitCast");
372  return handleCastFromBool(CastFromBool, FurtherImplicitCastExpression,
373  *Result.Context);
374  }
375 }
376 
377 void ImplicitBoolCastCheck::handleCastToBool(
378  const ImplicitCastExpr *CastExpression, const Stmt *ParentStatement,
379  ASTContext &Context) {
380  if (AllowConditionalPointerCasts &&
381  (CastExpression->getCastKind() == CK_PointerToBoolean ||
382  CastExpression->getCastKind() == CK_MemberPointerToBoolean) &&
383  isAllowedConditionalCast(CastExpression, Context)) {
384  return;
385  }
386 
387  if (AllowConditionalIntegerCasts &&
388  CastExpression->getCastKind() == CK_IntegralToBoolean &&
389  isAllowedConditionalCast(CastExpression, Context)) {
390  return;
391  }
392 
393  std::string OtherType = CastExpression->getSubExpr()->getType().getAsString();
394  DiagnosticBuilder Diagnostic =
395  diag(CastExpression->getLocStart(), "implicit cast '%0' -> bool")
396  << OtherType;
397 
398  StringRef EquivalentLiteralExpression = getEquivalentBoolLiteralForExpression(
399  CastExpression->getSubExpr(), Context);
400  if (!EquivalentLiteralExpression.empty()) {
401  addFixItHintsForLiteralCastToBool(Diagnostic, CastExpression,
402  EquivalentLiteralExpression);
403  } else {
404  addFixItHintsForGenericExpressionCastToBool(Diagnostic, CastExpression,
405  ParentStatement, Context);
406  }
407 }
408 
409 void ImplicitBoolCastCheck::handleCastFromBool(
410  const ImplicitCastExpr *CastExpression,
411  const ImplicitCastExpr *FurtherImplicitCastExpression,
412  ASTContext &Context) {
413  QualType DestinationType = (FurtherImplicitCastExpression != nullptr)
414  ? FurtherImplicitCastExpression->getType()
415  : CastExpression->getType();
416  std::string DestinationTypeString = DestinationType.getAsString();
417  DiagnosticBuilder Diagnostic =
418  diag(CastExpression->getLocStart(), "implicit cast bool -> '%0'")
419  << DestinationTypeString;
420 
421  if (llvm::isa<CXXBoolLiteralExpr>(CastExpression->getSubExpr())) {
422  addFixItHintsForLiteralCastFromBool(Diagnostic, CastExpression, Context,
423  DestinationType);
424  } else {
425  addFixItHintsForGenericExpressionCastFromBool(
426  Diagnostic, CastExpression, Context, DestinationTypeString);
427  }
428 }
429 
430 } // namespace readability
431 } // namespace tidy
432 } // namespace clang
SourceLocation Loc
'#' location in the include directive
AST_MATCHER(Type, isStrictlyInteger)
const std::string Name
Definition: USRFinder.cpp:164
LangOptions getLangOpts() const
Returns the language options from the context.
Definition: ClangTidy.h:187
std::unique_ptr< ast_matchers::MatchFinder > Finder
Definition: ClangTidy.cpp:262
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
SourceManager & SM
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:436
std::map< std::string, std::string > OptionMap
ClangTidyContext & Context
Definition: ClangTidy.cpp:87
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's name.
Definition: ClangTidy.cpp:403
const NamedDecl * Result
Definition: USRFinder.cpp:162
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.