clang-tools  3.9.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 CXXOperatorCallExpr *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 void ImplicitBoolCastCheck::registerMatchers(MatchFinder *Finder) {
303  // This check doesn't make much sense if we run it on language without
304  // built-in bool support.
305  if (!getLangOpts().Bool) {
306  return;
307  }
308 
309  Finder->addMatcher(
310  implicitCastExpr(
311  // Exclude cases common to implicit cast to and from bool.
312  unless(createExceptionCasesMatcher()),
313  // Exclude case of using if or while statements with variable
314  // declaration, e.g.:
315  // if (int var = functionCall()) {}
316  unless(
317  hasParent(stmt(anyOf(ifStmt(), whileStmt()), has(declStmt())))),
318  anyOf(hasCastKind(CK_IntegralToBoolean),
319  hasCastKind(CK_FloatingToBoolean),
320  hasCastKind(CK_PointerToBoolean),
321  hasCastKind(CK_MemberPointerToBoolean)),
322  // Retrive also parent statement, to check if we need additional
323  // parens in replacement.
324  anyOf(hasParent(stmt().bind("parentStmt")), anything()))
325  .bind("implicitCastToBool"),
326  this);
327 
328  Finder->addMatcher(
329  implicitCastExpr(
330  createImplicitCastFromBoolMatcher(),
331  // Exclude comparisons of bools, as they are always cast to integers
332  // in such context:
333  // bool_expr_a == bool_expr_b
334  // bool_expr_a != bool_expr_b
335  unless(hasParent(binaryOperator(
336  anyOf(hasOperatorName("=="), hasOperatorName("!=")),
337  hasLHS(createImplicitCastFromBoolMatcher()),
338  hasRHS(createImplicitCastFromBoolMatcher())))),
339  // Check also for nested casts, for example: bool -> int -> float.
340  anyOf(hasParent(implicitCastExpr().bind("furtherImplicitCast")),
341  anything()))
342  .bind("implicitCastFromBool"),
343  this);
344 }
345 
346 void ImplicitBoolCastCheck::check(const MatchFinder::MatchResult &Result) {
347  if (const auto *CastToBool =
348  Result.Nodes.getNodeAs<ImplicitCastExpr>("implicitCastToBool")) {
349  const auto *ParentStatement = Result.Nodes.getNodeAs<Stmt>("parentStmt");
350  return handleCastToBool(CastToBool, ParentStatement, *Result.Context);
351  }
352 
353  if (const auto *CastFromBool =
354  Result.Nodes.getNodeAs<ImplicitCastExpr>("implicitCastFromBool")) {
355  const auto *FurtherImplicitCastExpression =
356  Result.Nodes.getNodeAs<ImplicitCastExpr>("furtherImplicitCast");
357  return handleCastFromBool(CastFromBool, FurtherImplicitCastExpression,
358  *Result.Context);
359  }
360 }
361 
362 void ImplicitBoolCastCheck::handleCastToBool(
363  const ImplicitCastExpr *CastExpression, const Stmt *ParentStatement,
364  ASTContext &Context) {
365  if (AllowConditionalPointerCasts &&
366  (CastExpression->getCastKind() == CK_PointerToBoolean ||
367  CastExpression->getCastKind() == CK_MemberPointerToBoolean) &&
368  isAllowedConditionalCast(CastExpression, Context)) {
369  return;
370  }
371 
372  if (AllowConditionalIntegerCasts &&
373  CastExpression->getCastKind() == CK_IntegralToBoolean &&
374  isAllowedConditionalCast(CastExpression, Context)) {
375  return;
376  }
377 
378  std::string OtherType = CastExpression->getSubExpr()->getType().getAsString();
379  DiagnosticBuilder Diagnostic =
380  diag(CastExpression->getLocStart(), "implicit cast '%0' -> bool")
381  << OtherType;
382 
383  StringRef EquivalentLiteralExpression = getEquivalentBoolLiteralForExpression(
384  CastExpression->getSubExpr(), Context);
385  if (!EquivalentLiteralExpression.empty()) {
386  addFixItHintsForLiteralCastToBool(Diagnostic, CastExpression,
387  EquivalentLiteralExpression);
388  } else {
389  addFixItHintsForGenericExpressionCastToBool(Diagnostic, CastExpression,
390  ParentStatement, Context);
391  }
392 }
393 
394 void ImplicitBoolCastCheck::handleCastFromBool(
395  const ImplicitCastExpr *CastExpression,
396  const ImplicitCastExpr *FurtherImplicitCastExpression,
397  ASTContext &Context) {
398  QualType DestinationType = (FurtherImplicitCastExpression != nullptr)
399  ? FurtherImplicitCastExpression->getType()
400  : CastExpression->getType();
401  std::string DestinationTypeString = DestinationType.getAsString();
402  DiagnosticBuilder Diagnostic =
403  diag(CastExpression->getLocStart(), "implicit cast bool -> '%0'")
404  << DestinationTypeString;
405 
406  if (llvm::isa<CXXBoolLiteralExpr>(CastExpression->getSubExpr())) {
407  addFixItHintsForLiteralCastFromBool(Diagnostic, CastExpression, Context,
408  DestinationType);
409  } else {
410  addFixItHintsForGenericExpressionCastFromBool(
411  Diagnostic, CastExpression, Context, DestinationTypeString);
412  }
413 }
414 
415 } // namespace readability
416 } // namespace tidy
417 } // namespace clang
SourceLocation Loc
'#' location in the include directive
AST_MATCHER(Type, isStrictlyInteger)
std::unique_ptr< ast_matchers::MatchFinder > Finder
Definition: ClangTidy.cpp:210
SourceManager & SM
ClangTidyContext & Context
Definition: ClangTidy.cpp:93
const NamedDecl * Result
Definition: USRFinder.cpp:137