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