clang-tools  9.0.0
AvoidCStyleCastsCheck.cpp
Go to the documentation of this file.
1 //===--- AvoidCStyleCastsCheck.cpp - clang-tidy -----------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
10 #include "clang/AST/ASTContext.h"
11 #include "clang/ASTMatchers/ASTMatchFinder.h"
12 #include "clang/ASTMatchers/ASTMatchers.h"
13 #include "clang/Lex/Lexer.h"
14 
15 using namespace clang::ast_matchers;
16 
17 namespace clang {
18 namespace tidy {
19 namespace google {
20 namespace readability {
21 
22 void AvoidCStyleCastsCheck::registerMatchers(
23  ast_matchers::MatchFinder *Finder) {
24  Finder->addMatcher(
25  cStyleCastExpr(
26  // Filter out (EnumType)IntegerLiteral construct, which is generated
27  // for non-type template arguments of enum types.
28  // FIXME: Remove this once this is fixed in the AST.
29  unless(hasParent(substNonTypeTemplateParmExpr())),
30  // Avoid matches in template instantiations.
31  unless(isInTemplateInstantiation()))
32  .bind("cast"),
33  this);
34 }
35 
36 static bool needsConstCast(QualType SourceType, QualType DestType) {
37  while ((SourceType->isPointerType() && DestType->isPointerType()) ||
38  (SourceType->isReferenceType() && DestType->isReferenceType())) {
39  SourceType = SourceType->getPointeeType();
40  DestType = DestType->getPointeeType();
41  if (SourceType.isConstQualified() && !DestType.isConstQualified()) {
42  return (SourceType->isPointerType() == DestType->isPointerType()) &&
43  (SourceType->isReferenceType() == DestType->isReferenceType());
44  }
45  }
46  return false;
47 }
48 
49 static bool pointedUnqualifiedTypesAreEqual(QualType T1, QualType T2) {
50  while ((T1->isPointerType() && T2->isPointerType()) ||
51  (T1->isReferenceType() && T2->isReferenceType())) {
52  T1 = T1->getPointeeType();
53  T2 = T2->getPointeeType();
54  }
55  return T1.getUnqualifiedType() == T2.getUnqualifiedType();
56 }
57 
58 void AvoidCStyleCastsCheck::check(const MatchFinder::MatchResult &Result) {
59  const auto *CastExpr = Result.Nodes.getNodeAs<CStyleCastExpr>("cast");
60 
61  // Ignore casts in macros.
62  if (CastExpr->getExprLoc().isMacroID())
63  return;
64 
65  // Casting to void is an idiomatic way to mute "unused variable" and similar
66  // warnings.
67  if (CastExpr->getCastKind() == CK_ToVoid)
68  return;
69 
70  auto isFunction = [](QualType T) {
71  T = T.getCanonicalType().getNonReferenceType();
72  return T->isFunctionType() || T->isFunctionPointerType() ||
73  T->isMemberFunctionPointerType();
74  };
75 
76  const QualType DestTypeAsWritten =
77  CastExpr->getTypeAsWritten().getUnqualifiedType();
78  const QualType SourceTypeAsWritten =
79  CastExpr->getSubExprAsWritten()->getType().getUnqualifiedType();
80  const QualType SourceType = SourceTypeAsWritten.getCanonicalType();
81  const QualType DestType = DestTypeAsWritten.getCanonicalType();
82 
83  auto ReplaceRange = CharSourceRange::getCharRange(
84  CastExpr->getLParenLoc(), CastExpr->getSubExprAsWritten()->getBeginLoc());
85 
86  bool FnToFnCast =
87  isFunction(SourceTypeAsWritten) && isFunction(DestTypeAsWritten);
88 
89  const bool ConstructorCast = !CastExpr->getTypeAsWritten().hasQualifiers() &&
90  DestTypeAsWritten->isRecordType() &&
91  !DestTypeAsWritten->isElaboratedTypeSpecifier();
92 
93  if (CastExpr->getCastKind() == CK_NoOp && !FnToFnCast) {
94  // Function pointer/reference casts may be needed to resolve ambiguities in
95  // case of overloaded functions, so detection of redundant casts is trickier
96  // in this case. Don't emit "redundant cast" warnings for function
97  // pointer/reference types.
98  if (SourceTypeAsWritten == DestTypeAsWritten) {
99  diag(CastExpr->getBeginLoc(), "redundant cast to the same type")
100  << FixItHint::CreateRemoval(ReplaceRange);
101  return;
102  }
103  }
104 
105  // The rest of this check is only relevant to C++.
106  // We also disable it for Objective-C++.
107  if (!getLangOpts().CPlusPlus || getLangOpts().ObjC)
108  return;
109  // Ignore code inside extern "C" {} blocks.
110  if (!match(expr(hasAncestor(linkageSpecDecl())), *CastExpr, *Result.Context)
111  .empty())
112  return;
113  // Ignore code in .c files and headers included from them, even if they are
114  // compiled as C++.
115  if (getCurrentMainFile().endswith(".c"))
116  return;
117 
118  SourceManager &SM = *Result.SourceManager;
119 
120  // Ignore code in .c files #included in other files (which shouldn't be done,
121  // but people still do this for test and other purposes).
122  if (SM.getFilename(SM.getSpellingLoc(CastExpr->getBeginLoc())).endswith(".c"))
123  return;
124 
125  // Leave type spelling exactly as it was (unlike
126  // getTypeAsWritten().getAsString() which would spell enum types 'enum X').
127  StringRef DestTypeString =
128  Lexer::getSourceText(CharSourceRange::getTokenRange(
129  CastExpr->getLParenLoc().getLocWithOffset(1),
130  CastExpr->getRParenLoc().getLocWithOffset(-1)),
131  SM, getLangOpts());
132 
133  auto Diag =
134  diag(CastExpr->getBeginLoc(), "C-style casts are discouraged; use %0");
135 
136  auto ReplaceWithCast = [&](std::string CastText) {
137  const Expr *SubExpr = CastExpr->getSubExprAsWritten()->IgnoreImpCasts();
138  if (!isa<ParenExpr>(SubExpr)) {
139  CastText.push_back('(');
140  Diag << FixItHint::CreateInsertion(
141  Lexer::getLocForEndOfToken(SubExpr->getEndLoc(), 0, SM,
142  getLangOpts()),
143  ")");
144  }
145  Diag << FixItHint::CreateReplacement(ReplaceRange, CastText);
146  };
147  auto ReplaceWithNamedCast = [&](StringRef CastType) {
148  Diag << CastType;
149  ReplaceWithCast((CastType + "<" + DestTypeString + ">").str());
150  };
151  auto ReplaceWithConstructorCall = [&]() {
152  Diag << "constructor call syntax";
153  // FIXME: Validate DestTypeString, maybe.
154  ReplaceWithCast(DestTypeString.str());
155  };
156  // Suggest appropriate C++ cast. See [expr.cast] for cast notation semantics.
157  switch (CastExpr->getCastKind()) {
158  case CK_FunctionToPointerDecay:
159  ReplaceWithNamedCast("static_cast");
160  return;
161  case CK_ConstructorConversion:
162  if (ConstructorCast) {
163  ReplaceWithConstructorCall();
164  } else {
165  ReplaceWithNamedCast("static_cast");
166  }
167  return;
168  case CK_NoOp:
169  if (FnToFnCast) {
170  ReplaceWithNamedCast("static_cast");
171  return;
172  }
173  if (SourceType == DestType) {
174  Diag << "static_cast (if needed, the cast may be redundant)";
175  ReplaceWithCast(("static_cast<" + DestTypeString + ">").str());
176  return;
177  }
178  if (needsConstCast(SourceType, DestType) &&
179  pointedUnqualifiedTypesAreEqual(SourceType, DestType)) {
180  ReplaceWithNamedCast("const_cast");
181  return;
182  }
183  if (ConstructorCast) {
184  ReplaceWithConstructorCall();
185  return;
186  }
187  if (DestType->isReferenceType()) {
188  QualType Dest = DestType.getNonReferenceType();
189  QualType Source = SourceType.getNonReferenceType();
190  if (Source == Dest.withConst() ||
191  SourceType.getNonReferenceType() == DestType.getNonReferenceType()) {
192  ReplaceWithNamedCast("const_cast");
193  return;
194  }
195  break;
196  }
197  LLVM_FALLTHROUGH;
198  case clang::CK_IntegralCast:
199  // Convert integral and no-op casts between builtin types and enums to
200  // static_cast. A cast from enum to integer may be unnecessary, but it's
201  // still retained.
202  if ((SourceType->isBuiltinType() || SourceType->isEnumeralType()) &&
203  (DestType->isBuiltinType() || DestType->isEnumeralType())) {
204  ReplaceWithNamedCast("static_cast");
205  return;
206  }
207  break;
208  case CK_BitCast:
209  // FIXME: Suggest const_cast<...>(reinterpret_cast<...>(...)) replacement.
210  if (!needsConstCast(SourceType, DestType)) {
211  if (SourceType->isVoidPointerType())
212  ReplaceWithNamedCast("static_cast");
213  else
214  ReplaceWithNamedCast("reinterpret_cast");
215  return;
216  }
217  break;
218  default:
219  break;
220  }
221 
222  Diag << "static_cast/const_cast/reinterpret_cast";
223 }
224 
225 } // namespace readability
226 } // namespace google
227 } // namespace tidy
228 } // namespace clang
std::vector< std::string > match(const SymbolIndex &I, const FuzzyFindRequest &Req, bool *Incomplete)
Definition: TestIndex.cpp:94
static bool needsConstCast(QualType SourceType, QualType DestType)
llvm::Optional< Range > getTokenRange(const SourceManager &SM, const LangOptions &LangOpts, SourceLocation TokLoc)
Returns the taken range at TokLoc.
Definition: SourceCode.cpp:203
static bool pointedUnqualifiedTypesAreEqual(QualType T1, QualType T2)
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
llvm::Optional< llvm::Expected< tooling::AtomicChanges > > Result
Definition: Rename.cpp:36