11 #include "clang/AST/ASTContext.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 #include "clang/ASTMatchers/ASTMatchers.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";
26 const char DeclWithCastId[] =
"decl_cast";
27 const char DeclWithTemplateCastId[] =
"decl_template";
42 AST_MATCHER(VarDecl, hasWrittenNonListInitializer) {
43 const Expr *Init = Node.getAnyInitializer();
47 Init = Init->IgnoreImplicit();
51 if (
const auto *Construct = dyn_cast<CXXConstructExpr>(Init)) {
52 return !Construct->isListInitialization() && Construct->getNumArgs() > 0 &&
53 !Construct->getArg(0)->isDefaultArgument();
55 return Node.getInitStyle() != VarDecl::ListInit;
70 AST_MATCHER_P(QualType, isSugarFor, Matcher<QualType>, SugarMatcher) {
73 if (SugarMatcher.matches(QT,
Finder, Builder))
76 QualType NewQT = QT.getSingleStepDesugaredType(
Finder->getASTContext());
94 static const char *
const IteratorNames[] = {
"iterator",
"reverse_iterator",
96 "const_reverse_iterator"};
98 for (
const char *
Name : IteratorNames) {
99 if (hasName(
Name).matches(Node,
Finder, Builder))
118 static const char *
const ContainerNames[] = {
120 "forward_list",
"list",
126 "unordered_map",
"unordered_multimap",
127 "unordered_set",
"unordered_multiset",
129 "queue",
"priority_queue",
132 for (
const char *
Name : ContainerNames) {
133 if (hasName(
Name).matches(Node,
Finder, Builder))
160 const DeclContext *D = Node.getDeclContext();
162 while (D->isInlineNamespace())
165 if (!D->isNamespace() || !D->getParent()->isTranslationUnit())
168 const IdentifierInfo *Info = cast<NamespaceDecl>(D)->getIdentifier();
170 return (Info && Info->isStr(
"std"));
175 AST_POLYMORPHIC_MATCHER(hasExplicitTemplateArgs,
176 AST_POLYMORPHIC_SUPPORTED_TYPES(DeclRefExpr,
178 return Node.hasExplicitTemplateArgs();
183 DeclarationMatcher standardIterator() {
185 namedDecl(hasStdIteratorName()),
186 hasDeclContext(recordDecl(hasStdContainerName(), isFromStdNamespace())));
191 TypeMatcher typedefIterator() {
192 return typedefType(hasDeclaration(standardIterator()));
197 TypeMatcher nestedIterator() {
198 return recordType(hasDeclaration(standardIterator()));
203 TypeMatcher iteratorFromUsingDeclaration() {
204 auto HasIteratorDecl = hasDeclaration(namedDecl(hasStdIteratorName()));
206 return elaboratedType(allOf(
209 hasQualifier(specifiesType(templateSpecializationType(hasDeclaration(
210 namedDecl(hasStdContainerName(), isFromStdNamespace()))))),
214 anyOf(typedefType(HasIteratorDecl), recordType(HasIteratorDecl)))));
219 StatementMatcher makeIteratorDeclMatcher() {
220 return declStmt(unless(has(
221 varDecl(anyOf(unless(hasWrittenNonListInitializer()),
222 unless(hasType(isSugarFor(anyOf(
223 typedefIterator(), nestedIterator(),
224 iteratorFromUsingDeclaration())))))))))
225 .bind(IteratorDeclStmtId);
228 StatementMatcher makeDeclWithNewMatcher() {
230 unless(has(varDecl(anyOf(
231 unless(hasInitializer(ignoringParenImpCasts(cxxNewExpr()))),
236 pointee(hasCanonicalType(hasLocalQualifiers())))),
242 pointsTo(parenType(innerType(functionType()))))))))))
243 .bind(DeclWithNewId);
246 StatementMatcher makeDeclWithCastMatcher() {
248 unless(has(varDecl(unless(hasInitializer(explicitCastExpr()))))))
249 .bind(DeclWithCastId);
252 StatementMatcher makeDeclWithTemplateCastMatcher() {
254 substTemplateTypeParmType(hasReplacementType(equalsBoundNode(
"arg")));
257 anyOf(has(memberExpr(hasExplicitTemplateArgs())),
258 has(ignoringImpCasts(declRefExpr(hasExplicitTemplateArgs()))));
261 hasTemplateArgument(0, refersToType(qualType().bind(
"arg")));
263 auto TemplateCall = callExpr(
265 callee(functionDecl(TemplateArg,
266 returns(anyOf(ST, pointsTo(ST), references(ST))))));
268 return declStmt(unless(has(varDecl(
269 unless(hasInitializer(ignoringImplicit(TemplateCall)))))))
270 .bind(DeclWithTemplateCastId);
273 StatementMatcher makeCombinedMatcher() {
278 has(varDecl(unless(isImplicit()))),
280 unless(has(varDecl(anyOf(hasType(autoType()),
281 hasType(qualType(hasDescendant(autoType()))))))),
282 anyOf(makeIteratorDeclMatcher(), makeDeclWithNewMatcher(),
283 makeDeclWithCastMatcher(), makeDeclWithTemplateCastMatcher()));
288 UseAutoCheck::UseAutoCheck(StringRef
Name, ClangTidyContext *
Context)
289 : ClangTidyCheck(Name, Context),
290 RemoveStars(Options.get(
"RemoveStars", 0)) {}
293 Options.store(Opts,
"RemoveStars", RemoveStars ? 1 : 0);
296 void UseAutoCheck::registerMatchers(MatchFinder *
Finder) {
299 if (getLangOpts().CPlusPlus) {
300 Finder->addMatcher(makeCombinedMatcher(),
this);
304 void UseAutoCheck::replaceIterators(
const DeclStmt *D, ASTContext *
Context) {
305 for (
const auto *Dec : D->decls()) {
306 const auto *V = cast<VarDecl>(Dec);
307 const Expr *ExprInit = V->getInit();
310 if (
const auto *E = dyn_cast<ExprWithCleanups>(ExprInit))
311 ExprInit = E->getSubExpr();
313 const auto *Construct = dyn_cast<CXXConstructExpr>(ExprInit);
318 if (Construct->getNumArgs() != 1)
322 const Expr *E = (*Construct->arg_begin())->IgnoreParenImpCasts();
323 if (E != E->IgnoreConversionOperator()) {
330 if (
const auto *NestedConstruct = dyn_cast<CXXConstructExpr>(E)) {
336 if (NestedConstruct->getConstructor()->isConvertingConstructor(
false))
339 if (!Context->hasSameType(V->getType(), E->getType()))
344 const auto *V = cast<VarDecl>(*D->decl_begin());
350 SourceRange
Range(V->getTypeSourceInfo()->getTypeLoc().getSourceRange());
351 diag(
Range.getBegin(),
"use auto when declaring iterators")
352 << FixItHint::CreateReplacement(
Range,
"auto");
355 void UseAutoCheck::replaceExpr(
356 const DeclStmt *D, ASTContext *Context,
357 llvm::function_ref<QualType(
const Expr *)> GetType, StringRef
Message) {
358 const auto *FirstDecl = dyn_cast<VarDecl>(*D->decl_begin());
363 const QualType FirstDeclType = FirstDecl->getType().getCanonicalType();
365 std::vector<FixItHint> StarRemovals;
366 for (
const auto *Dec : D->decls()) {
367 const auto *V = cast<VarDecl>(Dec);
372 const auto *Expr = V->getInit()->IgnoreParenImpCasts();
378 if (!Context->hasSameUnqualifiedType(V->getType(), GetType(Expr)))
384 if (FirstDeclType != V->getType().getCanonicalType())
390 if (Dec == *D->decl_begin())
393 auto Q = V->getTypeSourceInfo()->getTypeLoc().getAs<PointerTypeLoc>();
394 while (!Q.isNull()) {
395 StarRemovals.push_back(FixItHint::CreateRemoval(Q.getStarLoc()));
396 Q = Q.getNextTypeLoc().getAs<PointerTypeLoc>();
405 TypeLoc
Loc = FirstDecl->getTypeSourceInfo()->getTypeLoc();
407 while (Loc.getTypeLocClass() == TypeLoc::Pointer ||
408 Loc.getTypeLocClass() == TypeLoc::Qualified)
409 Loc = Loc.getNextTypeLoc();
411 while (Loc.getTypeLocClass() == TypeLoc::LValueReference ||
412 Loc.getTypeLocClass() == TypeLoc::RValueReference ||
413 Loc.getTypeLocClass() == TypeLoc::Qualified) {
414 Loc = Loc.getNextTypeLoc();
416 SourceRange
Range(Loc.getSourceRange());
423 Diag << FixItHint::CreateReplacement(
Range, RemoveStars ?
"auto " :
"auto")
427 void UseAutoCheck::check(
const MatchFinder::MatchResult &Result) {
428 if (
const auto *Decl = Result.Nodes.getNodeAs<DeclStmt>(IteratorDeclStmtId)) {
429 replaceIterators(Decl, Result.Context);
430 }
else if (
const auto *Decl =
431 Result.Nodes.getNodeAs<DeclStmt>(DeclWithNewId)) {
432 replaceExpr(Decl, Result.Context,
433 [](
const Expr *Expr) {
return Expr->getType(); },
434 "use auto when initializing with new to avoid "
435 "duplicating the type name");
436 }
else if (
const auto *Decl =
437 Result.Nodes.getNodeAs<DeclStmt>(DeclWithCastId)) {
439 Decl, Result.Context,
440 [](
const Expr *Expr) {
441 return cast<ExplicitCastExpr>(Expr)->getTypeAsWritten();
443 "use auto when initializing with a cast to avoid duplicating the type "
445 }
else if (
const auto *Decl =
446 Result.Nodes.getNodeAs<DeclStmt>(DeclWithTemplateCastId)) {
448 Decl, Result.Context,
449 [](
const Expr *Expr) {
450 return cast<CallExpr>(Expr->IgnoreImplicit())
454 "use auto when initializing with a template cast to avoid duplicating "
457 llvm_unreachable(
"Bad Callback. No node provided.");
SourceLocation Loc
'#' location in the include directive
std::unique_ptr< ast_matchers::MatchFinder > Finder
AST_MATCHER_P(CXXForRangeStmt, hasRangeBeginEndStmt, ast_matchers::internal::Matcher< DeclStmt >, InnerMatcher)
static const StringRef Message
std::map< std::string, std::string > OptionMap
CharSourceRange Range
SourceRange for the file name.
ClangTidyContext & Context
AST_MATCHER(VarDecl, isAsm)