clang-tools  3.9.0
UseAutoCheck.cpp
Go to the documentation of this file.
1 //===--- UseAutoCheck.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 "UseAutoCheck.h"
11 #include "clang/AST/ASTContext.h"
12 #include "clang/ASTMatchers/ASTMatchers.h"
13 #include "clang/ASTMatchers/ASTMatchFinder.h"
14 
15 using namespace clang;
16 using namespace clang::ast_matchers;
17 using namespace clang::ast_matchers::internal;
18 
19 namespace clang {
20 namespace tidy {
21 namespace modernize {
22 namespace {
23 
24 const char IteratorDeclStmtId[] = "iterator_decl";
25 const char DeclWithNewId[] = "decl_new";
26 
27 /// \brief Matches variable declarations that have explicit initializers that
28 /// are not initializer lists.
29 ///
30 /// Given
31 /// \code
32 /// iterator I = Container.begin();
33 /// MyType A(42);
34 /// MyType B{2};
35 /// MyType C;
36 /// \endcode
37 ///
38 /// varDecl(hasWrittenNonListInitializer()) maches \c I and \c A but not \c B
39 /// or \c C.
40 AST_MATCHER(VarDecl, hasWrittenNonListInitializer) {
41  const Expr *Init = Node.getAnyInitializer();
42  if (!Init)
43  return false;
44 
45  Init = Init->IgnoreImplicit();
46 
47  // The following test is based on DeclPrinter::VisitVarDecl() to find if an
48  // initializer is implicit or not.
49  if (const auto *Construct = dyn_cast<CXXConstructExpr>(Init)) {
50  return !Construct->isListInitialization() && Construct->getNumArgs() > 0 &&
51  !Construct->getArg(0)->isDefaultArgument();
52  }
53  return Node.getInitStyle() != VarDecl::ListInit;
54 }
55 
56 /// \brief Matches QualTypes that are type sugar for QualTypes that match \c
57 /// SugarMatcher.
58 ///
59 /// Given
60 /// \code
61 /// class C {};
62 /// typedef C my_type;
63 /// typedef my_type my_other_type;
64 /// \endcode
65 ///
66 /// qualType(isSugarFor(recordType(hasDeclaration(namedDecl(hasName("C"))))))
67 /// matches \c my_type and \c my_other_type.
68 AST_MATCHER_P(QualType, isSugarFor, Matcher<QualType>, SugarMatcher) {
69  QualType QT = Node;
70  while (true) {
71  if (SugarMatcher.matches(QT, Finder, Builder))
72  return true;
73 
74  QualType NewQT = QT.getSingleStepDesugaredType(Finder->getASTContext());
75  if (NewQT == QT)
76  return false;
77  QT = NewQT;
78  }
79 }
80 
81 /// \brief Matches named declarations that have one of the standard iterator
82 /// names: iterator, reverse_iterator, const_iterator, const_reverse_iterator.
83 ///
84 /// Given
85 /// \code
86 /// iterator I;
87 /// const_iterator CI;
88 /// \endcode
89 ///
90 /// namedDecl(hasStdIteratorName()) matches \c I and \c CI.
91 AST_MATCHER(NamedDecl, hasStdIteratorName) {
92  static const char *const IteratorNames[] = {"iterator", "reverse_iterator",
93  "const_iterator",
94  "const_reverse_iterator"};
95 
96  for (const char *Name : IteratorNames) {
97  if (hasName(Name).matches(Node, Finder, Builder))
98  return true;
99  }
100  return false;
101 }
102 
103 /// \brief Matches named declarations that have one of the standard container
104 /// names.
105 ///
106 /// Given
107 /// \code
108 /// class vector {};
109 /// class forward_list {};
110 /// class my_ver{};
111 /// \endcode
112 ///
113 /// recordDecl(hasStdContainerName()) matches \c vector and \c forward_list
114 /// but not \c my_vec.
115 AST_MATCHER(NamedDecl, hasStdContainerName) {
116  static const char *const ContainerNames[] = {"array", "deque",
117  "forward_list", "list",
118  "vector",
119 
120  "map", "multimap",
121  "set", "multiset",
122 
123  "unordered_map",
124  "unordered_multimap",
125  "unordered_set",
126  "unordered_multiset",
127 
128  "queue", "priority_queue",
129  "stack"};
130 
131  for (const char *Name : ContainerNames) {
132  if (hasName(Name).matches(Node, Finder, Builder))
133  return true;
134  }
135  return false;
136 }
137 
138 /// Matches declarations whose declaration context is the C++ standard library
139 /// namespace std.
140 ///
141 /// Note that inline namespaces are silently ignored during the lookup since
142 /// both libstdc++ and libc++ are known to use them for versioning purposes.
143 ///
144 /// Given:
145 /// \code
146 /// namespace ns {
147 /// struct my_type {};
148 /// using namespace std;
149 /// }
150 ///
151 /// using std::vector;
152 /// using ns:my_type;
153 /// using ns::list;
154 /// \code
155 ///
156 /// usingDecl(hasAnyUsingShadowDecl(hasTargetDecl(isFromStdNamespace())))
157 /// matches "using std::vector" and "using ns::list".
158 AST_MATCHER(Decl, isFromStdNamespace) {
159  const DeclContext *D = Node.getDeclContext();
160 
161  while (D->isInlineNamespace())
162  D = D->getParent();
163 
164  if (!D->isNamespace() || !D->getParent()->isTranslationUnit())
165  return false;
166 
167  const IdentifierInfo *Info = cast<NamespaceDecl>(D)->getIdentifier();
168 
169  return (Info && Info->isStr("std"));
170 }
171 
172 /// \brief Returns a DeclarationMatcher that matches standard iterators nested
173 /// inside records with a standard container name.
174 DeclarationMatcher standardIterator() {
175  return allOf(
176  namedDecl(hasStdIteratorName()),
177  hasDeclContext(recordDecl(hasStdContainerName(), isFromStdNamespace())));
178 }
179 
180 /// \brief Returns a TypeMatcher that matches typedefs for standard iterators
181 /// inside records with a standard container name.
182 TypeMatcher typedefIterator() {
183  return typedefType(hasDeclaration(standardIterator()));
184 }
185 
186 /// \brief Returns a TypeMatcher that matches records named for standard
187 /// iterators nested inside records named for standard containers.
188 TypeMatcher nestedIterator() {
189  return recordType(hasDeclaration(standardIterator()));
190 }
191 
192 /// \brief Returns a TypeMatcher that matches types declared with using
193 /// declarations and which name standard iterators for standard containers.
194 TypeMatcher iteratorFromUsingDeclaration() {
195  auto HasIteratorDecl = hasDeclaration(namedDecl(hasStdIteratorName()));
196  // Types resulting from using declarations are represented by elaboratedType.
197  return elaboratedType(allOf(
198  // Unwrap the nested name specifier to test for one of the standard
199  // containers.
200  hasQualifier(specifiesType(templateSpecializationType(hasDeclaration(
201  namedDecl(hasStdContainerName(), isFromStdNamespace()))))),
202  // the named type is what comes after the final '::' in the type. It
203  // should name one of the standard iterator names.
204  namesType(
205  anyOf(typedefType(HasIteratorDecl), recordType(HasIteratorDecl)))));
206 }
207 
208 /// \brief This matcher returns declaration statements that contain variable
209 /// declarations with written non-list initializer for standard iterators.
210 StatementMatcher makeIteratorDeclMatcher() {
211  return declStmt(
212  // At least one varDecl should be a child of the declStmt to ensure
213  // it's a declaration list and avoid matching other declarations,
214  // e.g. using directives.
215  has(varDecl()),
216  unless(has(varDecl(anyOf(
217  unless(hasWrittenNonListInitializer()), hasType(autoType()),
218  unless(hasType(
219  isSugarFor(anyOf(typedefIterator(), nestedIterator(),
220  iteratorFromUsingDeclaration())))))))))
221  .bind(IteratorDeclStmtId);
222 }
223 
224 StatementMatcher makeDeclWithNewMatcher() {
225  return declStmt(
226  has(varDecl()),
227  unless(has(varDecl(anyOf(
228  unless(hasInitializer(ignoringParenImpCasts(cxxNewExpr()))),
229  // Skip declarations that are already using auto.
230  anyOf(hasType(autoType()),
231  hasType(pointerType(pointee(autoType())))),
232  // FIXME: TypeLoc information is not reliable where CV
233  // qualifiers are concerned so these types can't be
234  // handled for now.
235  hasType(pointerType(
236  pointee(hasCanonicalType(hasLocalQualifiers())))),
237 
238  // FIXME: Handle function pointers. For now we ignore them
239  // because the replacement replaces the entire type
240  // specifier source range which includes the identifier.
241  hasType(pointsTo(
242  pointsTo(parenType(innerType(functionType()))))))))))
243  .bind(DeclWithNewId);
244 }
245 
246 } // namespace
247 
248 UseAutoCheck::UseAutoCheck(StringRef Name, ClangTidyContext *Context)
249  : ClangTidyCheck(Name, Context),
250  RemoveStars(Options.get("RemoveStars", 0)) {}
251 
252 void UseAutoCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
253  Options.store(Opts, "RemoveStars", RemoveStars ? 1 : 0);
254 }
255 
256 void UseAutoCheck::registerMatchers(MatchFinder *Finder) {
257  // Only register the matchers for C++; the functionality currently does not
258  // provide any benefit to other languages, despite being benign.
259  if (getLangOpts().CPlusPlus) {
260  Finder->addMatcher(makeIteratorDeclMatcher(), this);
261  Finder->addMatcher(makeDeclWithNewMatcher(), this);
262  }
263 }
264 
265 void UseAutoCheck::replaceIterators(const DeclStmt *D, ASTContext *Context) {
266  for (const auto *Dec : D->decls()) {
267  const auto *V = cast<VarDecl>(Dec);
268  const Expr *ExprInit = V->getInit();
269 
270  // Skip expressions with cleanups from the intializer expression.
271  if (const auto *E = dyn_cast<ExprWithCleanups>(ExprInit))
272  ExprInit = E->getSubExpr();
273 
274  const auto *Construct = dyn_cast<CXXConstructExpr>(ExprInit);
275  if (!Construct)
276  continue;
277 
278  // Ensure that the constructor receives a single argument.
279  if (Construct->getNumArgs() != 1)
280  return;
281 
282  // Drill down to the as-written initializer.
283  const Expr *E = (*Construct->arg_begin())->IgnoreParenImpCasts();
284  if (E != E->IgnoreConversionOperator()) {
285  // We hit a conversion operator. Early-out now as they imply an implicit
286  // conversion from a different type. Could also mean an explicit
287  // conversion from the same type but that's pretty rare.
288  return;
289  }
290 
291  if (const auto *NestedConstruct = dyn_cast<CXXConstructExpr>(E)) {
292  // If we ran into an implicit conversion contructor, can't convert.
293  //
294  // FIXME: The following only checks if the constructor can be used
295  // implicitly, not if it actually was. Cases where the converting
296  // constructor was used explicitly won't get converted.
297  if (NestedConstruct->getConstructor()->isConvertingConstructor(false))
298  return;
299  }
300  if (!Context->hasSameType(V->getType(), E->getType()))
301  return;
302  }
303 
304  // Get the type location using the first declaration.
305  const auto *V = cast<VarDecl>(*D->decl_begin());
306 
307  // WARNING: TypeLoc::getSourceRange() will include the identifier for things
308  // like function pointers. Not a concern since this action only works with
309  // iterators but something to keep in mind in the future.
310 
311  SourceRange Range(V->getTypeSourceInfo()->getTypeLoc().getSourceRange());
312  diag(Range.getBegin(), "use auto when declaring iterators")
313  << FixItHint::CreateReplacement(Range, "auto");
314 }
315 
316 void UseAutoCheck::replaceNew(const DeclStmt *D, ASTContext *Context) {
317  const auto *FirstDecl = dyn_cast<VarDecl>(*D->decl_begin());
318  // Ensure that there is at least one VarDecl within the DeclStmt.
319  if (!FirstDecl)
320  return;
321 
322  const QualType FirstDeclType = FirstDecl->getType().getCanonicalType();
323 
324  std::vector<FixItHint> StarRemovals;
325  for (const auto *Dec : D->decls()) {
326  const auto *V = cast<VarDecl>(Dec);
327  // Ensure that every DeclStmt child is a VarDecl.
328  if (!V)
329  return;
330 
331  const auto *NewExpr = cast<CXXNewExpr>(V->getInit()->IgnoreParenImpCasts());
332  // Ensure that every VarDecl has a CXXNewExpr initializer.
333  if (!NewExpr)
334  return;
335 
336  // If VarDecl and Initializer have mismatching unqualified types.
337  if (!Context->hasSameUnqualifiedType(V->getType(), NewExpr->getType()))
338  return;
339 
340  // All subsequent variables in this declaration should have the same
341  // canonical type. For example, we don't want to use `auto` in
342  // `T *p = new T, **pp = new T*;`.
343  if (FirstDeclType != V->getType().getCanonicalType())
344  return;
345 
346  if (RemoveStars) {
347  // Remove explicitly written '*' from declarations where there's more than
348  // one declaration in the declaration list.
349  if (Dec == *D->decl_begin())
350  continue;
351 
352  auto Q = V->getTypeSourceInfo()->getTypeLoc().getAs<PointerTypeLoc>();
353  while (!Q.isNull()) {
354  StarRemovals.push_back(FixItHint::CreateRemoval(Q.getStarLoc()));
355  Q = Q.getNextTypeLoc().getAs<PointerTypeLoc>();
356  }
357  }
358  }
359 
360  // FIXME: There is, however, one case we can address: when the VarDecl pointee
361  // is the same as the initializer, just more CV-qualified. However, TypeLoc
362  // information is not reliable where CV qualifiers are concerned so we can't
363  // do anything about this case for now.
364  TypeLoc Loc = FirstDecl->getTypeSourceInfo()->getTypeLoc();
365  if (!RemoveStars) {
366  while (Loc.getTypeLocClass() == TypeLoc::Pointer ||
367  Loc.getTypeLocClass() == TypeLoc::Qualified)
368  Loc = Loc.getNextTypeLoc();
369  }
370  SourceRange Range(Loc.getSourceRange());
371  auto Diag = diag(Range.getBegin(), "use auto when initializing with new"
372  " to avoid duplicating the type name");
373 
374  // Space after 'auto' to handle cases where the '*' in the pointer type is
375  // next to the identifier. This avoids changing 'int *p' into 'autop'.
376  Diag << FixItHint::CreateReplacement(Range, RemoveStars ? "auto " : "auto")
377  << StarRemovals;
378 }
379 
380 void UseAutoCheck::check(const MatchFinder::MatchResult &Result) {
381  if (const auto *Decl = Result.Nodes.getNodeAs<DeclStmt>(IteratorDeclStmtId)) {
382  replaceIterators(Decl, Result.Context);
383  } else if (const auto *Decl =
384  Result.Nodes.getNodeAs<DeclStmt>(DeclWithNewId)) {
385  replaceNew(Decl, Result.Context);
386  } else {
387  llvm_unreachable("Bad Callback. No node provided.");
388  }
389 }
390 
391 } // namespace modernize
392 } // namespace tidy
393 } // namespace clang
SourceLocation Loc
'#' location in the include directive
AST_MATCHER(Type, isStrictlyInteger)
const std::string Name
Definition: USRFinder.cpp:140
std::unique_ptr< ast_matchers::MatchFinder > Finder
Definition: ClangTidy.cpp:210
AST_MATCHER_P(CXXForRangeStmt, hasRangeBeginEndStmt, ast_matchers::internal::Matcher< DeclStmt >, InnerMatcher)
std::map< std::string, std::string > OptionMap
CharSourceRange Range
SourceRange for the file name.
ClangTidyContext & Context
Definition: ClangTidy.cpp:93
const NamedDecl * Result
Definition: USRFinder.cpp:137