11 #include "clang/AST/ASTContext.h"
12 #include "clang/ASTMatchers/ASTMatchers.h"
13 #include "clang/ASTMatchers/ASTMatchFinder.h"
15 using namespace clang;
16 using namespace clang::ast_matchers;
17 using namespace clang::ast_matchers::internal;
24 const char IteratorDeclStmtId[] =
"iterator_decl";
25 const char DeclWithNewId[] =
"decl_new";
40 AST_MATCHER(VarDecl, hasWrittenNonListInitializer) {
41 const Expr *Init = Node.getAnyInitializer();
45 Init = Init->IgnoreImplicit();
49 if (
const auto *Construct = dyn_cast<CXXConstructExpr>(Init)) {
50 return !Construct->isListInitialization() && Construct->getNumArgs() > 0 &&
51 !Construct->getArg(0)->isDefaultArgument();
53 return Node.getInitStyle() != VarDecl::ListInit;
68 AST_MATCHER_P(QualType, isSugarFor, Matcher<QualType>, SugarMatcher) {
71 if (SugarMatcher.matches(QT,
Finder, Builder))
74 QualType NewQT = QT.getSingleStepDesugaredType(
Finder->getASTContext());
92 static const char *
const IteratorNames[] = {
"iterator",
"reverse_iterator",
94 "const_reverse_iterator"};
96 for (
const char *
Name : IteratorNames) {
97 if (hasName(
Name).matches(Node,
Finder, Builder))
116 static const char *
const ContainerNames[] = {
"array",
"deque",
117 "forward_list",
"list",
124 "unordered_multimap",
126 "unordered_multiset",
128 "queue",
"priority_queue",
131 for (
const char *
Name : ContainerNames) {
132 if (hasName(
Name).matches(Node,
Finder, Builder))
159 const DeclContext *D = Node.getDeclContext();
161 while (D->isInlineNamespace())
164 if (!D->isNamespace() || !D->getParent()->isTranslationUnit())
167 const IdentifierInfo *Info = cast<NamespaceDecl>(D)->getIdentifier();
169 return (Info && Info->isStr(
"std"));
174 DeclarationMatcher standardIterator() {
176 namedDecl(hasStdIteratorName()),
177 hasDeclContext(recordDecl(hasStdContainerName(), isFromStdNamespace())));
182 TypeMatcher typedefIterator() {
183 return typedefType(hasDeclaration(standardIterator()));
188 TypeMatcher nestedIterator() {
189 return recordType(hasDeclaration(standardIterator()));
194 TypeMatcher iteratorFromUsingDeclaration() {
195 auto HasIteratorDecl = hasDeclaration(namedDecl(hasStdIteratorName()));
197 return elaboratedType(allOf(
200 hasQualifier(specifiesType(templateSpecializationType(hasDeclaration(
201 namedDecl(hasStdContainerName(), isFromStdNamespace()))))),
205 anyOf(typedefType(HasIteratorDecl), recordType(HasIteratorDecl)))));
210 StatementMatcher makeIteratorDeclMatcher() {
216 unless(has(varDecl(anyOf(
217 unless(hasWrittenNonListInitializer()), hasType(autoType()),
219 isSugarFor(anyOf(typedefIterator(), nestedIterator(),
220 iteratorFromUsingDeclaration())))))))))
221 .bind(IteratorDeclStmtId);
224 StatementMatcher makeDeclWithNewMatcher() {
227 unless(has(varDecl(anyOf(
228 unless(hasInitializer(ignoringParenImpCasts(cxxNewExpr()))),
230 anyOf(hasType(autoType()),
231 hasType(pointerType(pointee(autoType())))),
236 pointee(hasCanonicalType(hasLocalQualifiers())))),
242 pointsTo(parenType(innerType(functionType()))))))))))
243 .bind(DeclWithNewId);
248 UseAutoCheck::UseAutoCheck(StringRef
Name, ClangTidyContext *
Context)
249 : ClangTidyCheck(Name, Context),
250 RemoveStars(Options.get(
"RemoveStars", 0)) {}
253 Options.store(Opts,
"RemoveStars", RemoveStars ? 1 : 0);
256 void UseAutoCheck::registerMatchers(MatchFinder *
Finder) {
259 if (getLangOpts().CPlusPlus) {
260 Finder->addMatcher(makeIteratorDeclMatcher(),
this);
261 Finder->addMatcher(makeDeclWithNewMatcher(),
this);
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();
271 if (
const auto *E = dyn_cast<ExprWithCleanups>(ExprInit))
272 ExprInit = E->getSubExpr();
274 const auto *Construct = dyn_cast<CXXConstructExpr>(ExprInit);
279 if (Construct->getNumArgs() != 1)
283 const Expr *E = (*Construct->arg_begin())->IgnoreParenImpCasts();
284 if (E != E->IgnoreConversionOperator()) {
291 if (
const auto *NestedConstruct = dyn_cast<CXXConstructExpr>(E)) {
297 if (NestedConstruct->getConstructor()->isConvertingConstructor(
false))
300 if (!Context->hasSameType(V->getType(), E->getType()))
305 const auto *V = cast<VarDecl>(*D->decl_begin());
311 SourceRange
Range(V->getTypeSourceInfo()->getTypeLoc().getSourceRange());
312 diag(
Range.getBegin(),
"use auto when declaring iterators")
313 << FixItHint::CreateReplacement(
Range,
"auto");
316 void UseAutoCheck::replaceNew(
const DeclStmt *D, ASTContext *Context) {
317 const auto *FirstDecl = dyn_cast<VarDecl>(*D->decl_begin());
322 const QualType FirstDeclType = FirstDecl->getType().getCanonicalType();
324 std::vector<FixItHint> StarRemovals;
325 for (
const auto *Dec : D->decls()) {
326 const auto *V = cast<VarDecl>(Dec);
331 const auto *NewExpr = cast<CXXNewExpr>(V->getInit()->IgnoreParenImpCasts());
337 if (!Context->hasSameUnqualifiedType(V->getType(), NewExpr->getType()))
343 if (FirstDeclType != V->getType().getCanonicalType())
349 if (Dec == *D->decl_begin())
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>();
364 TypeLoc
Loc = FirstDecl->getTypeSourceInfo()->getTypeLoc();
366 while (Loc.getTypeLocClass() == TypeLoc::Pointer ||
367 Loc.getTypeLocClass() == TypeLoc::Qualified)
368 Loc = Loc.getNextTypeLoc();
370 SourceRange
Range(Loc.getSourceRange());
371 auto Diag = diag(
Range.getBegin(),
"use auto when initializing with new"
372 " to avoid duplicating the type name");
376 Diag << FixItHint::CreateReplacement(
Range, RemoveStars ?
"auto " :
"auto")
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);
387 llvm_unreachable(
"Bad Callback. No node provided.");
SourceLocation Loc
'#' location in the include directive
AST_MATCHER(Type, isStrictlyInteger)
std::unique_ptr< ast_matchers::MatchFinder > Finder
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