blob: e2ddceb33b567cfea441838a1fad5662440c4ffa [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"
Angel Garcia Gomez5b9d33a2015-08-21 15:08:51 +000012#include "clang/ASTMatchers/ASTMatchFinder.h"
Mandeep Singh Grang7c7ea7d2016-11-08 07:50:19 +000013#include "clang/ASTMatchers/ASTMatchers.h"
Angel Garcia Gomez5b9d33a2015-08-21 15:08:51 +000014
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";
Malcolm Parsonsdb048332016-10-31 14:43:37 +000026const char DeclWithCastId[] = "decl_cast";
Angel Garcia Gomez5b9d33a2015-08-21 15:08:51 +000027
28/// \brief Matches variable declarations that have explicit initializers that
29/// are not initializer lists.
30///
31/// Given
32/// \code
33/// iterator I = Container.begin();
34/// MyType A(42);
35/// MyType B{2};
36/// MyType C;
37/// \endcode
38///
39/// varDecl(hasWrittenNonListInitializer()) maches \c I and \c A but not \c B
40/// or \c C.
41AST_MATCHER(VarDecl, hasWrittenNonListInitializer) {
42 const Expr *Init = Node.getAnyInitializer();
43 if (!Init)
44 return false;
45
Tim Shen325c7272016-06-21 20:11:20 +000046 Init = Init->IgnoreImplicit();
47
Angel Garcia Gomez5b9d33a2015-08-21 15:08:51 +000048 // The following test is based on DeclPrinter::VisitVarDecl() to find if an
49 // initializer is implicit or not.
50 if (const auto *Construct = dyn_cast<CXXConstructExpr>(Init)) {
51 return !Construct->isListInitialization() && Construct->getNumArgs() > 0 &&
52 !Construct->getArg(0)->isDefaultArgument();
53 }
54 return Node.getInitStyle() != VarDecl::ListInit;
55}
56
57/// \brief Matches QualTypes that are type sugar for QualTypes that match \c
58/// SugarMatcher.
59///
60/// Given
61/// \code
62/// class C {};
63/// typedef C my_type;
64/// typedef my_type my_other_type;
65/// \endcode
66///
67/// qualType(isSugarFor(recordType(hasDeclaration(namedDecl(hasName("C"))))))
68/// matches \c my_type and \c my_other_type.
69AST_MATCHER_P(QualType, isSugarFor, Matcher<QualType>, SugarMatcher) {
70 QualType QT = Node;
71 while (true) {
72 if (SugarMatcher.matches(QT, Finder, Builder))
73 return true;
74
75 QualType NewQT = QT.getSingleStepDesugaredType(Finder->getASTContext());
76 if (NewQT == QT)
77 return false;
78 QT = NewQT;
79 }
80}
81
82/// \brief Matches named declarations that have one of the standard iterator
83/// names: iterator, reverse_iterator, const_iterator, const_reverse_iterator.
84///
85/// Given
86/// \code
87/// iterator I;
88/// const_iterator CI;
89/// \endcode
90///
91/// namedDecl(hasStdIteratorName()) matches \c I and \c CI.
92AST_MATCHER(NamedDecl, hasStdIteratorName) {
Craig Topper45857d42015-10-18 05:14:41 +000093 static const char *const IteratorNames[] = {"iterator", "reverse_iterator",
94 "const_iterator",
95 "const_reverse_iterator"};
Angel Garcia Gomez5b9d33a2015-08-21 15:08:51 +000096
97 for (const char *Name : IteratorNames) {
98 if (hasName(Name).matches(Node, Finder, Builder))
99 return true;
100 }
101 return false;
102}
103
104/// \brief Matches named declarations that have one of the standard container
105/// names.
106///
107/// Given
108/// \code
109/// class vector {};
110/// class forward_list {};
111/// class my_ver{};
112/// \endcode
113///
114/// recordDecl(hasStdContainerName()) matches \c vector and \c forward_list
115/// but not \c my_vec.
116AST_MATCHER(NamedDecl, hasStdContainerName) {
Mandeep Singh Grang7c7ea7d2016-11-08 07:50:19 +0000117 static const char *const ContainerNames[] = {
118 "array", "deque",
119 "forward_list", "list",
120 "vector",
Angel Garcia Gomez5b9d33a2015-08-21 15:08:51 +0000121
Mandeep Singh Grang7c7ea7d2016-11-08 07:50:19 +0000122 "map", "multimap",
123 "set", "multiset",
Angel Garcia Gomez5b9d33a2015-08-21 15:08:51 +0000124
Mandeep Singh Grang7c7ea7d2016-11-08 07:50:19 +0000125 "unordered_map", "unordered_multimap",
126 "unordered_set", "unordered_multiset",
Angel Garcia Gomez5b9d33a2015-08-21 15:08:51 +0000127
Mandeep Singh Grang7c7ea7d2016-11-08 07:50:19 +0000128 "queue", "priority_queue",
129 "stack"};
Angel Garcia Gomez5b9d33a2015-08-21 15:08:51 +0000130
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".
158AST_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.
174DeclarationMatcher 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.
182TypeMatcher 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.
188TypeMatcher 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.
194TypeMatcher 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.
210StatementMatcher makeIteratorDeclMatcher() {
Malcolm Parsonsdb048332016-10-31 14:43:37 +0000211 return declStmt(unless(has(
212 varDecl(anyOf(unless(hasWrittenNonListInitializer()),
213 unless(hasType(isSugarFor(anyOf(
214 typedefIterator(), nestedIterator(),
215 iteratorFromUsingDeclaration())))))))))
Angel Garcia Gomez5b9d33a2015-08-21 15:08:51 +0000216 .bind(IteratorDeclStmtId);
217}
218
219StatementMatcher makeDeclWithNewMatcher() {
Aaron Ballmanb9ea09c2015-09-17 13:31:25 +0000220 return declStmt(
Aaron Ballmanb9ea09c2015-09-17 13:31:25 +0000221 unless(has(varDecl(anyOf(
222 unless(hasInitializer(ignoringParenImpCasts(cxxNewExpr()))),
223 // FIXME: TypeLoc information is not reliable where CV
224 // qualifiers are concerned so these types can't be
225 // handled for now.
226 hasType(pointerType(
227 pointee(hasCanonicalType(hasLocalQualifiers())))),
Angel Garcia Gomez5b9d33a2015-08-21 15:08:51 +0000228
Aaron Ballmanb9ea09c2015-09-17 13:31:25 +0000229 // FIXME: Handle function pointers. For now we ignore them
230 // because the replacement replaces the entire type
231 // specifier source range which includes the identifier.
232 hasType(pointsTo(
233 pointsTo(parenType(innerType(functionType()))))))))))
Angel Garcia Gomez5b9d33a2015-08-21 15:08:51 +0000234 .bind(DeclWithNewId);
235}
236
Malcolm Parsonsdb048332016-10-31 14:43:37 +0000237StatementMatcher makeDeclWithCastMatcher() {
238 return declStmt(
239 unless(has(varDecl(unless(hasInitializer(explicitCastExpr()))))))
240 .bind(DeclWithCastId);
241}
242
243StatementMatcher makeCombinedMatcher() {
244 return declStmt(
245 // At least one varDecl should be a child of the declStmt to ensure
246 // it's a declaration list and avoid matching other declarations,
247 // e.g. using directives.
248 has(varDecl()),
249 // Skip declarations that are already using auto.
250 unless(has(varDecl(anyOf(hasType(autoType()),
251 hasType(pointerType(pointee(autoType()))),
252 hasType(referenceType(pointee(autoType()))))))),
253 anyOf(makeIteratorDeclMatcher(), makeDeclWithNewMatcher(),
254 makeDeclWithCastMatcher()));
255}
256
Angel Garcia Gomez5b9d33a2015-08-21 15:08:51 +0000257} // namespace
258
Alexander Kornienkoc0308c42016-06-03 21:22:58 +0000259UseAutoCheck::UseAutoCheck(StringRef Name, ClangTidyContext *Context)
260 : ClangTidyCheck(Name, Context),
261 RemoveStars(Options.get("RemoveStars", 0)) {}
262
263void UseAutoCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
264 Options.store(Opts, "RemoveStars", RemoveStars ? 1 : 0);
265}
266
Angel Garcia Gomez5b9d33a2015-08-21 15:08:51 +0000267void UseAutoCheck::registerMatchers(MatchFinder *Finder) {
Aaron Ballman8b0583e2015-08-28 17:58:10 +0000268 // Only register the matchers for C++; the functionality currently does not
269 // provide any benefit to other languages, despite being benign.
270 if (getLangOpts().CPlusPlus) {
Malcolm Parsonsdb048332016-10-31 14:43:37 +0000271 Finder->addMatcher(makeCombinedMatcher(), this);
Aaron Ballman8b0583e2015-08-28 17:58:10 +0000272 }
Angel Garcia Gomez5b9d33a2015-08-21 15:08:51 +0000273}
274
275void UseAutoCheck::replaceIterators(const DeclStmt *D, ASTContext *Context) {
276 for (const auto *Dec : D->decls()) {
277 const auto *V = cast<VarDecl>(Dec);
278 const Expr *ExprInit = V->getInit();
279
280 // Skip expressions with cleanups from the intializer expression.
281 if (const auto *E = dyn_cast<ExprWithCleanups>(ExprInit))
282 ExprInit = E->getSubExpr();
283
284 const auto *Construct = dyn_cast<CXXConstructExpr>(ExprInit);
285 if (!Construct)
286 continue;
287
288 // Ensure that the constructor receives a single argument.
289 if (Construct->getNumArgs() != 1)
290 return;
291
292 // Drill down to the as-written initializer.
293 const Expr *E = (*Construct->arg_begin())->IgnoreParenImpCasts();
294 if (E != E->IgnoreConversionOperator()) {
295 // We hit a conversion operator. Early-out now as they imply an implicit
296 // conversion from a different type. Could also mean an explicit
297 // conversion from the same type but that's pretty rare.
298 return;
299 }
300
301 if (const auto *NestedConstruct = dyn_cast<CXXConstructExpr>(E)) {
302 // If we ran into an implicit conversion contructor, can't convert.
303 //
304 // FIXME: The following only checks if the constructor can be used
305 // implicitly, not if it actually was. Cases where the converting
306 // constructor was used explicitly won't get converted.
307 if (NestedConstruct->getConstructor()->isConvertingConstructor(false))
308 return;
309 }
310 if (!Context->hasSameType(V->getType(), E->getType()))
311 return;
312 }
313
314 // Get the type location using the first declaration.
315 const auto *V = cast<VarDecl>(*D->decl_begin());
316
317 // WARNING: TypeLoc::getSourceRange() will include the identifier for things
318 // like function pointers. Not a concern since this action only works with
319 // iterators but something to keep in mind in the future.
320
321 SourceRange Range(V->getTypeSourceInfo()->getTypeLoc().getSourceRange());
322 diag(Range.getBegin(), "use auto when declaring iterators")
323 << FixItHint::CreateReplacement(Range, "auto");
324}
325
Malcolm Parsonsdb048332016-10-31 14:43:37 +0000326void UseAutoCheck::replaceExpr(const DeclStmt *D, ASTContext *Context,
327 std::function<QualType(const Expr *)> GetType,
328 StringRef Message) {
Angel Garcia Gomeze6035de2015-09-02 10:20:00 +0000329 const auto *FirstDecl = dyn_cast<VarDecl>(*D->decl_begin());
Angel Garcia Gomez5b9d33a2015-08-21 15:08:51 +0000330 // Ensure that there is at least one VarDecl within the DeclStmt.
331 if (!FirstDecl)
332 return;
333
334 const QualType FirstDeclType = FirstDecl->getType().getCanonicalType();
335
Alexander Kornienkoc0308c42016-06-03 21:22:58 +0000336 std::vector<FixItHint> StarRemovals;
Angel Garcia Gomez5b9d33a2015-08-21 15:08:51 +0000337 for (const auto *Dec : D->decls()) {
338 const auto *V = cast<VarDecl>(Dec);
339 // Ensure that every DeclStmt child is a VarDecl.
340 if (!V)
341 return;
342
Malcolm Parsonsdb048332016-10-31 14:43:37 +0000343 const auto *Expr = V->getInit()->IgnoreParenImpCasts();
344 // Ensure that every VarDecl has an initializer.
345 if (!Expr)
Angel Garcia Gomez5b9d33a2015-08-21 15:08:51 +0000346 return;
347
348 // If VarDecl and Initializer have mismatching unqualified types.
Malcolm Parsonsdb048332016-10-31 14:43:37 +0000349 if (!Context->hasSameUnqualifiedType(V->getType(), GetType(Expr)))
Angel Garcia Gomez5b9d33a2015-08-21 15:08:51 +0000350 return;
351
Alexander Kornienkoc0308c42016-06-03 21:22:58 +0000352 // All subsequent variables in this declaration should have the same
353 // canonical type. For example, we don't want to use `auto` in
354 // `T *p = new T, **pp = new T*;`.
Angel Garcia Gomez5b9d33a2015-08-21 15:08:51 +0000355 if (FirstDeclType != V->getType().getCanonicalType())
356 return;
357
Alexander Kornienkoc0308c42016-06-03 21:22:58 +0000358 if (RemoveStars) {
359 // Remove explicitly written '*' from declarations where there's more than
360 // one declaration in the declaration list.
361 if (Dec == *D->decl_begin())
362 continue;
363
364 auto Q = V->getTypeSourceInfo()->getTypeLoc().getAs<PointerTypeLoc>();
365 while (!Q.isNull()) {
366 StarRemovals.push_back(FixItHint::CreateRemoval(Q.getStarLoc()));
367 Q = Q.getNextTypeLoc().getAs<PointerTypeLoc>();
368 }
Angel Garcia Gomez5b9d33a2015-08-21 15:08:51 +0000369 }
370 }
371
372 // FIXME: There is, however, one case we can address: when the VarDecl pointee
373 // is the same as the initializer, just more CV-qualified. However, TypeLoc
374 // information is not reliable where CV qualifiers are concerned so we can't
375 // do anything about this case for now.
Alexander Kornienkoc0308c42016-06-03 21:22:58 +0000376 TypeLoc Loc = FirstDecl->getTypeSourceInfo()->getTypeLoc();
377 if (!RemoveStars) {
378 while (Loc.getTypeLocClass() == TypeLoc::Pointer ||
379 Loc.getTypeLocClass() == TypeLoc::Qualified)
380 Loc = Loc.getNextTypeLoc();
381 }
Malcolm Parsonsdb048332016-10-31 14:43:37 +0000382 while (Loc.getTypeLocClass() == TypeLoc::LValueReference ||
383 Loc.getTypeLocClass() == TypeLoc::RValueReference ||
384 Loc.getTypeLocClass() == TypeLoc::Qualified) {
385 Loc = Loc.getNextTypeLoc();
386 }
Alexander Kornienkoc0308c42016-06-03 21:22:58 +0000387 SourceRange Range(Loc.getSourceRange());
Malcolm Parsonsdb048332016-10-31 14:43:37 +0000388 auto Diag = diag(Range.getBegin(), Message);
Angel Garcia Gomez5b9d33a2015-08-21 15:08:51 +0000389
390 // Space after 'auto' to handle cases where the '*' in the pointer type is
391 // next to the identifier. This avoids changing 'int *p' into 'autop'.
Alexander Kornienkoc0308c42016-06-03 21:22:58 +0000392 Diag << FixItHint::CreateReplacement(Range, RemoveStars ? "auto " : "auto")
393 << StarRemovals;
Angel Garcia Gomez5b9d33a2015-08-21 15:08:51 +0000394}
395
396void UseAutoCheck::check(const MatchFinder::MatchResult &Result) {
397 if (const auto *Decl = Result.Nodes.getNodeAs<DeclStmt>(IteratorDeclStmtId)) {
398 replaceIterators(Decl, Result.Context);
399 } else if (const auto *Decl =
400 Result.Nodes.getNodeAs<DeclStmt>(DeclWithNewId)) {
Malcolm Parsonsdb048332016-10-31 14:43:37 +0000401 replaceExpr(Decl, Result.Context,
402 [](const Expr *Expr) { return Expr->getType(); },
403 "use auto when initializing with new to avoid "
404 "duplicating the type name");
405 } else if (const auto *Decl =
406 Result.Nodes.getNodeAs<DeclStmt>(DeclWithCastId)) {
407 replaceExpr(
408 Decl, Result.Context,
409 [](const Expr *Expr) {
410 return cast<ExplicitCastExpr>(Expr)->getTypeAsWritten();
411 },
412 "use auto when initializing with a cast to avoid duplicating the type "
413 "name");
Angel Garcia Gomez5b9d33a2015-08-21 15:08:51 +0000414 } else {
415 llvm_unreachable("Bad Callback. No node provided.");
416 }
417}
418
419} // namespace modernize
420} // namespace tidy
421} // namespace clang