clang-tools  8.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  while ((SourceType->isPointerType() && DestType->isPointerType()) ||
39  (SourceType->isReferenceType() && DestType->isReferenceType())) {
40  SourceType = SourceType->getPointeeType();
41  DestType = DestType->getPointeeType();
42  if (SourceType.isConstQualified() && !DestType.isConstQualified()) {
43  return (SourceType->isPointerType() == DestType->isPointerType()) &&
44  (SourceType->isReferenceType() == DestType->isReferenceType());
45  }
46  }
47  return false;
48 }
49 
50 static bool pointedUnqualifiedTypesAreEqual(QualType T1, QualType T2) {
51  while ((T1->isPointerType() && T2->isPointerType()) ||
52  (T1->isReferenceType() && T2->isReferenceType())) {
53  T1 = T1->getPointeeType();
54  T2 = T2->getPointeeType();
55  }
56  return T1.getUnqualifiedType() == T2.getUnqualifiedType();
57 }
58 
59 void AvoidCStyleCastsCheck::check(const MatchFinder::MatchResult &Result) {
60  const auto *CastExpr = Result.Nodes.getNodeAs<CStyleCastExpr>("cast");
61 
62  // Ignore casts in macros.
63  if (CastExpr->getExprLoc().isMacroID())
64  return;
65 
66  // Casting to void is an idiomatic way to mute "unused variable" and similar
67  // warnings.
68  if (CastExpr->getCastKind() == CK_ToVoid)
69  return;
70 
71  auto isFunction = [](QualType T) {
72  T = T.getCanonicalType().getNonReferenceType();
73  return T->isFunctionType() || T->isFunctionPointerType() ||
74  T->isMemberFunctionPointerType();
75  };
76 
77  const QualType DestTypeAsWritten =
78  CastExpr->getTypeAsWritten().getUnqualifiedType();
79  const QualType SourceTypeAsWritten =
80  CastExpr->getSubExprAsWritten()->getType().getUnqualifiedType();
81  const QualType SourceType = SourceTypeAsWritten.getCanonicalType();
82  const QualType DestType = DestTypeAsWritten.getCanonicalType();
83 
84  auto ReplaceRange = CharSourceRange::getCharRange(
85  CastExpr->getLParenLoc(), CastExpr->getSubExprAsWritten()->getBeginLoc());
86 
87  bool FnToFnCast =
88  isFunction(SourceTypeAsWritten) && isFunction(DestTypeAsWritten);
89 
90  if (CastExpr->getCastKind() == CK_NoOp && !FnToFnCast) {
91  // Function pointer/reference casts may be needed to resolve ambiguities in
92  // case of overloaded functions, so detection of redundant casts is trickier
93  // in this case. Don't emit "redundant cast" warnings for function
94  // pointer/reference types.
95  if (SourceTypeAsWritten == DestTypeAsWritten) {
96  diag(CastExpr->getBeginLoc(), "redundant cast to the same type")
97  << FixItHint::CreateRemoval(ReplaceRange);
98  return;
99  }
100  }
101 
102  // The rest of this check is only relevant to C++.
103  // We also disable it for Objective-C++.
104  if (!getLangOpts().CPlusPlus || getLangOpts().ObjC)
105  return;
106  // Ignore code inside extern "C" {} blocks.
107  if (!match(expr(hasAncestor(linkageSpecDecl())), *CastExpr, *Result.Context)
108  .empty())
109  return;
110  // Ignore code in .c files and headers included from them, even if they are
111  // compiled as C++.
112  if (getCurrentMainFile().endswith(".c"))
113  return;
114 
115  SourceManager &SM = *Result.SourceManager;
116 
117  // Ignore code in .c files #included in other files (which shouldn't be done,
118  // but people still do this for test and other purposes).
119  if (SM.getFilename(SM.getSpellingLoc(CastExpr->getBeginLoc())).endswith(".c"))
120  return;
121 
122  // Leave type spelling exactly as it was (unlike
123  // getTypeAsWritten().getAsString() which would spell enum types 'enum X').
124  StringRef DestTypeString =
125  Lexer::getSourceText(CharSourceRange::getTokenRange(
126  CastExpr->getLParenLoc().getLocWithOffset(1),
127  CastExpr->getRParenLoc().getLocWithOffset(-1)),
128  SM, getLangOpts());
129 
130  auto Diag =
131  diag(CastExpr->getBeginLoc(), "C-style casts are discouraged; use %0");
132 
133  auto ReplaceWithCast = [&](std::string CastText) {
134  const Expr *SubExpr = CastExpr->getSubExprAsWritten()->IgnoreImpCasts();
135  if (!isa<ParenExpr>(SubExpr)) {
136  CastText.push_back('(');
137  Diag << FixItHint::CreateInsertion(
138  Lexer::getLocForEndOfToken(SubExpr->getEndLoc(), 0, SM,
139  getLangOpts()),
140  ")");
141  }
142  Diag << FixItHint::CreateReplacement(ReplaceRange, CastText);
143  };
144  auto ReplaceWithNamedCast = [&](StringRef CastType) {
145  Diag << CastType;
146  ReplaceWithCast((CastType + "<" + DestTypeString + ">").str());
147  };
148 
149  // Suggest appropriate C++ cast. See [expr.cast] for cast notation semantics.
150  switch (CastExpr->getCastKind()) {
151  case CK_FunctionToPointerDecay:
152  ReplaceWithNamedCast("static_cast");
153  return;
154  case CK_ConstructorConversion:
155  if (!CastExpr->getTypeAsWritten().hasQualifiers() &&
156  DestTypeAsWritten->isRecordType() &&
157  !DestTypeAsWritten->isElaboratedTypeSpecifier()) {
158  Diag << "constructor call syntax";
159  // FIXME: Validate DestTypeString, maybe.
160  ReplaceWithCast(DestTypeString.str());
161  } else {
162  ReplaceWithNamedCast("static_cast");
163  }
164  return;
165  case CK_NoOp:
166  if (FnToFnCast) {
167  ReplaceWithNamedCast("static_cast");
168  return;
169  }
170  if (SourceType == DestType) {
171  Diag << "static_cast (if needed, the cast may be redundant)";
172  ReplaceWithCast(("static_cast<" + DestTypeString + ">").str());
173  return;
174  }
175  if (needsConstCast(SourceType, DestType) &&
176  pointedUnqualifiedTypesAreEqual(SourceType, DestType)) {
177  ReplaceWithNamedCast("const_cast");
178  return;
179  }
180  if (DestType->isReferenceType()) {
181  QualType Dest = DestType.getNonReferenceType();
182  QualType Source = SourceType.getNonReferenceType();
183  if (Source == Dest.withConst() ||
184  SourceType.getNonReferenceType() == DestType.getNonReferenceType()) {
185  ReplaceWithNamedCast("const_cast");
186  return;
187  }
188  break;
189  }
190  LLVM_FALLTHROUGH;
191  case clang::CK_IntegralCast:
192  // Convert integral and no-op casts between builtin types and enums to
193  // static_cast. A cast from enum to integer may be unnecessary, but it's
194  // still retained.
195  if ((SourceType->isBuiltinType() || SourceType->isEnumeralType()) &&
196  (DestType->isBuiltinType() || DestType->isEnumeralType())) {
197  ReplaceWithNamedCast("static_cast");
198  return;
199  }
200  break;
201  case CK_BitCast:
202  // FIXME: Suggest const_cast<...>(reinterpret_cast<...>(...)) replacement.
203  if (!needsConstCast(SourceType, DestType)) {
204  if (SourceType->isVoidPointerType())
205  ReplaceWithNamedCast("static_cast");
206  else
207  ReplaceWithNamedCast("reinterpret_cast");
208  return;
209  }
210  break;
211  default:
212  break;
213  }
214 
215  Diag << "static_cast/const_cast/reinterpret_cast";
216 }
217 
218 } // namespace readability
219 } // namespace google
220 } // namespace tidy
221 } // namespace clang
static bool needsConstCast(QualType SourceType, QualType DestType)
llvm::Optional< llvm::Expected< tooling::AtomicChanges > > Result
static bool pointedUnqualifiedTypesAreEqual(QualType T1, QualType T2)
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//