clang-tools  3.9.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
24 AvoidCStyleCastsCheck::registerMatchers(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())).bind("cast"),
33  this);
34 }
35 
36 static bool needsConstCast(QualType SourceType, QualType DestType) {
37  SourceType = SourceType.getNonReferenceType();
38  DestType = DestType.getNonReferenceType();
39  while (SourceType->isPointerType() && DestType->isPointerType()) {
40  SourceType = SourceType->getPointeeType();
41  DestType = DestType->getPointeeType();
42  if (SourceType.isConstQualified() && !DestType.isConstQualified())
43  return true;
44  }
45  return false;
46 }
47 
48 static bool pointedTypesAreEqual(QualType SourceType, QualType DestType) {
49  SourceType = SourceType.getNonReferenceType();
50  DestType = DestType.getNonReferenceType();
51  while (SourceType->isPointerType() && DestType->isPointerType()) {
52  SourceType = SourceType->getPointeeType();
53  DestType = DestType->getPointeeType();
54  }
55  return SourceType.getUnqualifiedType() == DestType.getUnqualifiedType();
56 }
57 
58 void AvoidCStyleCastsCheck::check(const MatchFinder::MatchResult &Result) {
59  const auto *CastExpr = Result.Nodes.getNodeAs<CStyleCastExpr>("cast");
60 
61  auto ParenRange = CharSourceRange::getTokenRange(CastExpr->getLParenLoc(),
62  CastExpr->getRParenLoc());
63  // Ignore casts in macros.
64  if (ParenRange.getBegin().isMacroID() || ParenRange.getEnd().isMacroID())
65  return;
66 
67  // Casting to void is an idiomatic way to mute "unused variable" and similar
68  // warnings.
69  if (CastExpr->getTypeAsWritten()->isVoidType())
70  return;
71 
72  QualType SourceType = CastExpr->getSubExprAsWritten()->getType();
73  QualType DestType = CastExpr->getTypeAsWritten();
74 
75  if (SourceType == DestType) {
76  diag(CastExpr->getLocStart(), "redundant cast to the same type")
77  << FixItHint::CreateRemoval(ParenRange);
78  return;
79  }
80  SourceType = SourceType.getCanonicalType();
81  DestType = DestType.getCanonicalType();
82  if (SourceType == DestType) {
83  diag(CastExpr->getLocStart(),
84  "possibly redundant cast between typedefs of the same type");
85  return;
86  }
87 
88 
89  // The rest of this check is only relevant to C++.
90  if (!Result.Context->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, Result.Context->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  Result.Context->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:210
SourceManager & SM
static bool needsConstCast(QualType SourceType, QualType DestType)
static bool pointedTypesAreEqual(QualType SourceType, QualType DestType)
const NamedDecl * Result
Definition: USRFinder.cpp:137