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