diff options
Diffstat (limited to 'clang-tools-extra/clang-tidy/modernize/UseAutoCheck.cpp')
-rw-r--r-- | clang-tools-extra/clang-tidy/modernize/UseAutoCheck.cpp | 50 |
1 files changed, 46 insertions, 4 deletions
diff --git a/clang-tools-extra/clang-tidy/modernize/UseAutoCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseAutoCheck.cpp index e2ddceb33b5..12c19f6d283 100644 --- a/clang-tools-extra/clang-tidy/modernize/UseAutoCheck.cpp +++ b/clang-tools-extra/clang-tidy/modernize/UseAutoCheck.cpp @@ -24,6 +24,7 @@ namespace { const char IteratorDeclStmtId[] = "iterator_decl"; const char DeclWithNewId[] = "decl_new"; const char DeclWithCastId[] = "decl_cast"; +const char DeclWithTemplateCastId[] = "decl_template"; /// \brief Matches variable declarations that have explicit initializers that /// are not initializer lists. @@ -169,6 +170,14 @@ AST_MATCHER(Decl, isFromStdNamespace) { return (Info && Info->isStr("std")); } +/// Matches declaration reference or member expressions with explicit template +/// arguments. +AST_POLYMORPHIC_MATCHER(hasExplicitTemplateArgs, + AST_POLYMORPHIC_SUPPORTED_TYPES(DeclRefExpr, + MemberExpr)) { + return Node.hasExplicitTemplateArgs(); +} + /// \brief Returns a DeclarationMatcher that matches standard iterators nested /// inside records with a standard container name. DeclarationMatcher standardIterator() { @@ -240,18 +249,38 @@ StatementMatcher makeDeclWithCastMatcher() { .bind(DeclWithCastId); } +StatementMatcher makeDeclWithTemplateCastMatcher() { + auto ST = + substTemplateTypeParmType(hasReplacementType(equalsBoundNode("arg"))); + + auto ExplicitCall = + anyOf(has(memberExpr(hasExplicitTemplateArgs())), + has(ignoringImpCasts(declRefExpr(hasExplicitTemplateArgs())))); + + auto TemplateArg = + hasTemplateArgument(0, refersToType(qualType().bind("arg"))); + + auto TemplateCall = callExpr( + ExplicitCall, + callee(functionDecl(TemplateArg, + returns(anyOf(ST, pointsTo(ST), references(ST)))))); + + return declStmt(unless(has(varDecl( + unless(hasInitializer(ignoringImplicit(TemplateCall))))))) + .bind(DeclWithTemplateCastId); +} + StatementMatcher makeCombinedMatcher() { return declStmt( // At least one varDecl should be a child of the declStmt to ensure // it's a declaration list and avoid matching other declarations, // e.g. using directives. - has(varDecl()), + has(varDecl(unless(isImplicit()))), // Skip declarations that are already using auto. unless(has(varDecl(anyOf(hasType(autoType()), - hasType(pointerType(pointee(autoType()))), - hasType(referenceType(pointee(autoType()))))))), + hasType(qualType(hasDescendant(autoType()))))))), anyOf(makeIteratorDeclMatcher(), makeDeclWithNewMatcher(), - makeDeclWithCastMatcher())); + makeDeclWithCastMatcher(), makeDeclWithTemplateCastMatcher())); } } // namespace @@ -389,6 +418,8 @@ void UseAutoCheck::replaceExpr(const DeclStmt *D, ASTContext *Context, // Space after 'auto' to handle cases where the '*' in the pointer type is // next to the identifier. This avoids changing 'int *p' into 'autop'. + // FIXME: This doesn't work for function pointers because the variable name + // is inside the type. Diag << FixItHint::CreateReplacement(Range, RemoveStars ? "auto " : "auto") << StarRemovals; } @@ -411,6 +442,17 @@ void UseAutoCheck::check(const MatchFinder::MatchResult &Result) { }, "use auto when initializing with a cast to avoid duplicating the type " "name"); + } else if (const auto *Decl = + Result.Nodes.getNodeAs<DeclStmt>(DeclWithTemplateCastId)) { + replaceExpr( + Decl, Result.Context, + [](const Expr *Expr) { + return cast<CallExpr>(Expr->IgnoreImplicit()) + ->getDirectCallee() + ->getReturnType(); + }, + "use auto when initializing with a template cast to avoid duplicating " + "the type name"); } else { llvm_unreachable("Bad Callback. No node provided."); } |