blob: 1f1c140f027ef68edc5f263309354cfd46ffc548 [file] [log] [blame]
Angel Garcia Gomez5b9d33a2015-08-21 15:08:51 +00001//===--- 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
15using namespace clang;
16using namespace clang::ast_matchers;
17using namespace clang::ast_matchers::internal;
18
19namespace clang {
20namespace tidy {
21namespace modernize {
22namespace {
23
24const char IteratorDeclStmtId[] = "iterator_decl";
25const 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.
40AST_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.
66AST_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.
89AST_MATCHER(NamedDecl, hasStdIteratorName) {
Craig Topper45857d42015-10-18 05:14:41 +000090 static const char *const IteratorNames[] = {"iterator", "reverse_iterator",
91 "const_iterator",
92 "const_reverse_iterator"};
Angel Garcia Gomez5b9d33a2015-08-21 15:08:51 +000093
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.
113AST_MATCHER(NamedDecl, hasStdContainerName) {
Craig Topper45857d42015-10-18 05:14:41 +0000114 static const char *const ContainerNames[] = {"array", "deque",
115 "forward_list", "list",
116 "vector",
Angel Garcia Gomez5b9d33a2015-08-21 15:08:51 +0000117
Craig Topper45857d42015-10-18 05:14:41 +0000118 "map", "multimap",
119 "set", "multiset",
Angel Garcia Gomez5b9d33a2015-08-21 15:08:51 +0000120
Craig Topper45857d42015-10-18 05:14:41 +0000121 "unordered_map",
122 "unordered_multimap",
123 "unordered_set",
124 "unordered_multiset",
Angel Garcia Gomez5b9d33a2015-08-21 15:08:51 +0000125
Craig Topper45857d42015-10-18 05:14:41 +0000126 "queue", "priority_queue",
127 "stack"};
Angel Garcia Gomez5b9d33a2015-08-21 15:08:51 +0000128
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".
156AST_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.
172DeclarationMatcher 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.
180TypeMatcher 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.
186TypeMatcher 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.
192TypeMatcher 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.
208StatementMatcher 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
222StatementMatcher makeDeclWithNewMatcher() {
Aaron Ballmanb9ea09c2015-09-17 13:31:25 +0000223 return declStmt(
224 has(varDecl()),
225 unless(has(varDecl(anyOf(
226 unless(hasInitializer(ignoringParenImpCasts(cxxNewExpr()))),
Angel Garcia Gomez2df36482015-10-14 09:29:55 +0000227 // Skip declarations that are already using auto.
228 anyOf(hasType(autoType()),
229 hasType(pointerType(pointee(autoType())))),
Aaron Ballmanb9ea09c2015-09-17 13:31:25 +0000230 // 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())))),
Angel Garcia Gomez5b9d33a2015-08-21 15:08:51 +0000235
Aaron Ballmanb9ea09c2015-09-17 13:31:25 +0000236 // 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()))))))))))
Angel Garcia Gomez5b9d33a2015-08-21 15:08:51 +0000241 .bind(DeclWithNewId);
242}
243
244} // namespace
245
Alexander Kornienkoc0308c42016-06-03 21:22:58 +0000246UseAutoCheck::UseAutoCheck(StringRef Name, ClangTidyContext *Context)
247 : ClangTidyCheck(Name, Context),
248 RemoveStars(Options.get("RemoveStars", 0)) {}
249
250void UseAutoCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
251 Options.store(Opts, "RemoveStars", RemoveStars ? 1 : 0);
252}
253
Angel Garcia Gomez5b9d33a2015-08-21 15:08:51 +0000254void UseAutoCheck::registerMatchers(MatchFinder *Finder) {
Aaron Ballman8b0583e2015-08-28 17:58:10 +0000255 // Only register the matchers for C++; the functionality currently does not
256 // provide any benefit to other languages, despite being benign.
257 if (getLangOpts().CPlusPlus) {
258 Finder->addMatcher(makeIteratorDeclMatcher(), this);
259 Finder->addMatcher(makeDeclWithNewMatcher(), this);
260 }
Angel Garcia Gomez5b9d33a2015-08-21 15:08:51 +0000261}
262
263void UseAutoCheck::replaceIterators(const DeclStmt *D, ASTContext *Context) {
264 for (const auto *Dec : D->decls()) {
265 const auto *V = cast<VarDecl>(Dec);
266 const Expr *ExprInit = V->getInit();
267
268 // Skip expressions with cleanups from the intializer expression.
269 if (const auto *E = dyn_cast<ExprWithCleanups>(ExprInit))
270 ExprInit = E->getSubExpr();
271
272 const auto *Construct = dyn_cast<CXXConstructExpr>(ExprInit);
273 if (!Construct)
274 continue;
275
276 // Ensure that the constructor receives a single argument.
277 if (Construct->getNumArgs() != 1)
278 return;
279
280 // Drill down to the as-written initializer.
281 const Expr *E = (*Construct->arg_begin())->IgnoreParenImpCasts();
282 if (E != E->IgnoreConversionOperator()) {
283 // We hit a conversion operator. Early-out now as they imply an implicit
284 // conversion from a different type. Could also mean an explicit
285 // conversion from the same type but that's pretty rare.
286 return;
287 }
288
289 if (const auto *NestedConstruct = dyn_cast<CXXConstructExpr>(E)) {
290 // If we ran into an implicit conversion contructor, can't convert.
291 //
292 // FIXME: The following only checks if the constructor can be used
293 // implicitly, not if it actually was. Cases where the converting
294 // constructor was used explicitly won't get converted.
295 if (NestedConstruct->getConstructor()->isConvertingConstructor(false))
296 return;
297 }
298 if (!Context->hasSameType(V->getType(), E->getType()))
299 return;
300 }
301
302 // Get the type location using the first declaration.
303 const auto *V = cast<VarDecl>(*D->decl_begin());
304
305 // WARNING: TypeLoc::getSourceRange() will include the identifier for things
306 // like function pointers. Not a concern since this action only works with
307 // iterators but something to keep in mind in the future.
308
309 SourceRange Range(V->getTypeSourceInfo()->getTypeLoc().getSourceRange());
310 diag(Range.getBegin(), "use auto when declaring iterators")
311 << FixItHint::CreateReplacement(Range, "auto");
312}
313
314void UseAutoCheck::replaceNew(const DeclStmt *D, ASTContext *Context) {
Angel Garcia Gomeze6035de2015-09-02 10:20:00 +0000315 const auto *FirstDecl = dyn_cast<VarDecl>(*D->decl_begin());
Angel Garcia Gomez5b9d33a2015-08-21 15:08:51 +0000316 // Ensure that there is at least one VarDecl within the DeclStmt.
317 if (!FirstDecl)
318 return;
319
320 const QualType FirstDeclType = FirstDecl->getType().getCanonicalType();
321
Alexander Kornienkoc0308c42016-06-03 21:22:58 +0000322 std::vector<FixItHint> StarRemovals;
Angel Garcia Gomez5b9d33a2015-08-21 15:08:51 +0000323 for (const auto *Dec : D->decls()) {
324 const auto *V = cast<VarDecl>(Dec);
325 // Ensure that every DeclStmt child is a VarDecl.
326 if (!V)
327 return;
328
329 const auto *NewExpr = cast<CXXNewExpr>(V->getInit()->IgnoreParenImpCasts());
330 // Ensure that every VarDecl has a CXXNewExpr initializer.
331 if (!NewExpr)
332 return;
333
334 // If VarDecl and Initializer have mismatching unqualified types.
335 if (!Context->hasSameUnqualifiedType(V->getType(), NewExpr->getType()))
336 return;
337
Alexander Kornienkoc0308c42016-06-03 21:22:58 +0000338 // All subsequent variables in this declaration should have the same
339 // canonical type. For example, we don't want to use `auto` in
340 // `T *p = new T, **pp = new T*;`.
Angel Garcia Gomez5b9d33a2015-08-21 15:08:51 +0000341 if (FirstDeclType != V->getType().getCanonicalType())
342 return;
343
Alexander Kornienkoc0308c42016-06-03 21:22:58 +0000344 if (RemoveStars) {
345 // Remove explicitly written '*' from declarations where there's more than
346 // one declaration in the declaration list.
347 if (Dec == *D->decl_begin())
348 continue;
349
350 auto Q = V->getTypeSourceInfo()->getTypeLoc().getAs<PointerTypeLoc>();
351 while (!Q.isNull()) {
352 StarRemovals.push_back(FixItHint::CreateRemoval(Q.getStarLoc()));
353 Q = Q.getNextTypeLoc().getAs<PointerTypeLoc>();
354 }
Angel Garcia Gomez5b9d33a2015-08-21 15:08:51 +0000355 }
356 }
357
358 // FIXME: There is, however, one case we can address: when the VarDecl pointee
359 // is the same as the initializer, just more CV-qualified. However, TypeLoc
360 // information is not reliable where CV qualifiers are concerned so we can't
361 // do anything about this case for now.
Alexander Kornienkoc0308c42016-06-03 21:22:58 +0000362 TypeLoc Loc = FirstDecl->getTypeSourceInfo()->getTypeLoc();
363 if (!RemoveStars) {
364 while (Loc.getTypeLocClass() == TypeLoc::Pointer ||
365 Loc.getTypeLocClass() == TypeLoc::Qualified)
366 Loc = Loc.getNextTypeLoc();
367 }
368 SourceRange Range(Loc.getSourceRange());
Angel Garcia Gomez5b9d33a2015-08-21 15:08:51 +0000369 auto Diag = diag(Range.getBegin(), "use auto when initializing with new"
370 " to avoid duplicating the type name");
371
372 // Space after 'auto' to handle cases where the '*' in the pointer type is
373 // next to the identifier. This avoids changing 'int *p' into 'autop'.
Alexander Kornienkoc0308c42016-06-03 21:22:58 +0000374 Diag << FixItHint::CreateReplacement(Range, RemoveStars ? "auto " : "auto")
375 << StarRemovals;
Angel Garcia Gomez5b9d33a2015-08-21 15:08:51 +0000376}
377
378void UseAutoCheck::check(const MatchFinder::MatchResult &Result) {
379 if (const auto *Decl = Result.Nodes.getNodeAs<DeclStmt>(IteratorDeclStmtId)) {
380 replaceIterators(Decl, Result.Context);
381 } else if (const auto *Decl =
382 Result.Nodes.getNodeAs<DeclStmt>(DeclWithNewId)) {
383 replaceNew(Decl, Result.Context);
384 } else {
385 llvm_unreachable("Bad Callback. No node provided.");
386 }
387}
388
389} // namespace modernize
390} // namespace tidy
391} // namespace clang