clang-tools  4.0.0
AvoidCStyleCastsCheck.cpp
Go to the documentation of this file.
1 //===--- AvoidCStyleCastsCheck.cpp - clang-tidy -----------------*- C++ -*-===//
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 "AvoidCStyleCastsCheck.h"
11 #include "clang/AST/ASTContext.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 #include "clang/ASTMatchers/ASTMatchers.h"
14 #include "clang/Lex/Lexer.h"
15 
16 using namespace clang::ast_matchers;
17 
18 namespace clang {
19 namespace tidy {
20 namespace google {
21 namespace readability {
22 
23 void AvoidCStyleCastsCheck::registerMatchers(
24  ast_matchers::MatchFinder *Finder) {
25  Finder->addMatcher(
26  cStyleCastExpr(
27  // Filter out (EnumType)IntegerLiteral construct, which is generated
28  // for non-type template arguments of enum types.
29  // FIXME: Remove this once this is fixed in the AST.
30  unless(hasParent(substNonTypeTemplateParmExpr())),
31  // Avoid matches in template instantiations.
32  unless(isInTemplateInstantiation()))
33  .bind("cast"),
34  this);
35 }
36 
37 static bool needsConstCast(QualType SourceType, QualType DestType) {
38  SourceType = SourceType.getNonReferenceType();
39  DestType = DestType.getNonReferenceType();
40  while (SourceType->isPointerType() && DestType->isPointerType()) {
41  SourceType = SourceType->getPointeeType();
42  DestType = DestType->getPointeeType();
43  if (SourceType.isConstQualified() && !DestType.isConstQualified())
44  return true;
45  }
46  return false;
47 }
48 
49 static bool pointedTypesAreEqual(QualType SourceType, QualType DestType) {
50  SourceType = SourceType.getNonReferenceType();
51  DestType = DestType.getNonReferenceType();
52  while (SourceType->isPointerType() && DestType->isPointerType()) {
53  SourceType = SourceType->getPointeeType();
54  DestType = DestType->getPointeeType();
55  }
56  return SourceType.getUnqualifiedType() == DestType.getUnqualifiedType();
57 }
58 
59 void AvoidCStyleCastsCheck::check(const MatchFinder::MatchResult &Result) {
60  const auto *CastExpr = Result.Nodes.getNodeAs<CStyleCastExpr>("cast");
61 
62  auto ParenRange = CharSourceRange::getTokenRange(CastExpr->getLParenLoc(),
63  CastExpr->getRParenLoc());
64  // Ignore casts in macros.
65  if (ParenRange.getBegin().isMacroID() || ParenRange.getEnd().isMacroID())
66  return;
67 
68  // Casting to void is an idiomatic way to mute "unused variable" and similar
69  // warnings.
70  if (CastExpr->getTypeAsWritten()->isVoidType())
71  return;
72 
73  QualType SourceType = CastExpr->getSubExprAsWritten()->getType();
74  QualType DestType = CastExpr->getTypeAsWritten();
75 
76  if (SourceType == DestType) {
77  diag(CastExpr->getLocStart(), "redundant cast to the same type")
78  << FixItHint::CreateRemoval(ParenRange);
79  return;
80  }
81  SourceType = SourceType.getCanonicalType();
82  DestType = DestType.getCanonicalType();
83  if (SourceType == DestType) {
84  diag(CastExpr->getLocStart(),
85  "possibly redundant cast between typedefs of the same type");
86  return;
87  }
88 
89  // The rest of this check is only relevant to C++.
90  if (!getLangOpts().CPlusPlus)
91  return;
92  // Ignore code inside extern "C" {} blocks.
93  if (!match(expr(hasAncestor(linkageSpecDecl())), *CastExpr, *Result.Context)
94  .empty())
95  return;
96  // Ignore code in .c files and headers included from them, even if they are
97  // compiled as C++.
98  if (getCurrentMainFile().endswith(".c"))
99  return;
100  // Ignore code in .c files #included in other files (which shouldn't be done,
101  // but people still do this for test and other purposes).
102  SourceManager &SM = *Result.SourceManager;
103  if (SM.getFilename(SM.getSpellingLoc(CastExpr->getLocStart())).endswith(".c"))
104  return;
105 
106  // Leave type spelling exactly as it was (unlike
107  // getTypeAsWritten().getAsString() which would spell enum types 'enum X').
108  StringRef DestTypeString =
109  Lexer::getSourceText(CharSourceRange::getTokenRange(
110  CastExpr->getLParenLoc().getLocWithOffset(1),
111  CastExpr->getRParenLoc().getLocWithOffset(-1)),
112  SM, getLangOpts());
113 
114  auto diag_builder =
115  diag(CastExpr->getLocStart(), "C-style casts are discouraged; use %0");
116 
117  auto ReplaceWithCast = [&](StringRef CastType) {
118  diag_builder << CastType;
119 
120  const Expr *SubExpr = CastExpr->getSubExprAsWritten()->IgnoreImpCasts();
121  std::string CastText = (CastType + "<" + DestTypeString + ">").str();
122  if (!isa<ParenExpr>(SubExpr)) {
123  CastText.push_back('(');
124  diag_builder << FixItHint::CreateInsertion(
125  Lexer::getLocForEndOfToken(SubExpr->getLocEnd(), 0, SM,
126  getLangOpts()),
127  ")");
128  }
129  diag_builder << FixItHint::CreateReplacement(ParenRange, CastText);
130  };
131 
132  // Suggest appropriate C++ cast. See [expr.cast] for cast notation semantics.
133  switch (CastExpr->getCastKind()) {
134  case CK_NoOp:
135  if (needsConstCast(SourceType, DestType) &&
136  pointedTypesAreEqual(SourceType, DestType)) {
137  ReplaceWithCast("const_cast");
138  return;
139  }
140  if (DestType->isReferenceType() &&
141  (SourceType.getNonReferenceType() ==
142  DestType.getNonReferenceType().withConst() ||
143  SourceType.getNonReferenceType() == DestType.getNonReferenceType())) {
144  ReplaceWithCast("const_cast");
145  return;
146  }
147  // FALLTHROUGH
148  case clang::CK_IntegralCast:
149  // Convert integral and no-op casts between builtin types and enums to
150  // static_cast. A cast from enum to integer may be unnecessary, but it's
151  // still retained.
152  if ((SourceType->isBuiltinType() || SourceType->isEnumeralType()) &&
153  (DestType->isBuiltinType() || DestType->isEnumeralType())) {
154  ReplaceWithCast("static_cast");
155  return;
156  }
157  break;
158  case CK_BitCast:
159  // FIXME: Suggest const_cast<...>(reinterpret_cast<...>(...)) replacement.
160  if (!needsConstCast(SourceType, DestType)) {
161  ReplaceWithCast("reinterpret_cast");
162  return;
163  }
164  break;
165  default:
166  break;
167  }
168 
169  diag_builder << "static_cast/const_cast/reinterpret_cast";
170 }
171 
172 } // namespace readability
173 } // namespace google
174 } // namespace tidy
175 } // namespace clang
std::unique_ptr< ast_matchers::MatchFinder > Finder
Definition: ClangTidy.cpp:262
SourceManager & SM
static bool needsConstCast(QualType SourceType, QualType DestType)
static bool pointedTypesAreEqual(QualType SourceType, QualType DestType)
const NamedDecl * Result
Definition: USRFinder.cpp:162