Index: clang-tidy/modernize/UseAutoCheck.cpp =================================================================== --- clang-tidy/modernize/UseAutoCheck.cpp +++ clang-tidy/modernize/UseAutoCheck.cpp @@ -24,6 +24,7 @@ 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,20 @@ return (Info && Info->isStr("std")); } +/// Matches the type that was substituted for the template parameter. +AST_MATCHER_P(SubstTemplateTypeParmType, hasReplacementType, + ast_matchers::internal::Matcher, InnerMatcher) { + return InnerMatcher.matches(Node.getReplacementType(), Finder, Builder); +} + +/// 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,6 +255,22 @@ .bind(DeclWithCastId); } +StatementMatcher makeDeclWithTemplateCastMatcher() { + auto ST = + substTemplateTypeParmType(hasReplacementType(equalsBoundNode("arg"))); + + return declStmt( + unless(has(varDecl(unless(hasInitializer(ignoringImplicit(callExpr( + anyOf(has(memberExpr(hasExplicitTemplateArgs())), + has(ignoringImpCasts( + declRefExpr(hasExplicitTemplateArgs())))), + callee(functionDecl( + hasTemplateArgument(0, + refersToType(qualType().bind("arg"))), + returns(anyOf(ST, pointsTo(ST), references(ST))))))))))))) + .bind(DeclWithTemplateCastId); +} + StatementMatcher makeCombinedMatcher() { return declStmt( // At least one varDecl should be a child of the declStmt to ensure @@ -251,7 +282,7 @@ hasType(pointerType(pointee(autoType()))), hasType(referenceType(pointee(autoType()))))))), anyOf(makeIteratorDeclMatcher(), makeDeclWithNewMatcher(), - makeDeclWithCastMatcher())); + makeDeclWithCastMatcher(), makeDeclWithTemplateCastMatcher())); } } // namespace @@ -411,6 +442,17 @@ }, "use auto when initializing with a cast to avoid duplicating the type " "name"); + } else if (const auto *Decl = + Result.Nodes.getNodeAs(DeclWithTemplateCastId)) { + replaceExpr( + Decl, Result.Context, + [](const Expr *Expr) { + return cast(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."); } Index: docs/ReleaseNotes.rst =================================================================== --- docs/ReleaseNotes.rst +++ docs/ReleaseNotes.rst @@ -89,7 +89,8 @@ - The `modernize-use-auto `_ check - now warns about variable declarations that are initialized with a cast. + now warns about variable declarations that are initialized with a cast, or by + calling a templated function that behaves as a cast. - New `modernize-use-equals-delete `_ check Index: docs/clang-tidy/checks/modernize-use-auto.rst =================================================================== --- docs/clang-tidy/checks/modernize-use-auto.rst +++ docs/clang-tidy/checks/modernize-use-auto.rst @@ -160,7 +160,9 @@ auto *my_pointer = static_cast(my_param); The check handles ``static_cast``, ``dynamic_cast``, ``const_cast``, -``reinterpret_cast``, functional casts and C-style casts. +``reinterpret_cast``, functional casts, C-style casts and function templates +that behave as casts, such as ``llvm::dyn_cast``, ``boost::lexical_cast`` and +``gsl::narrow_cast``. Known Limitations ----------------- @@ -170,9 +172,6 @@ * User-defined iterators are not handled at this time. -* Function templates that behave as casts, such as ``llvm::dyn_cast``, - ``boost::lexical_cast`` or ``gsl::narrow_cast`` are not handled. - Options ------- Index: test/clang-tidy/modernize-use-auto-cast-remove-stars.cpp =================================================================== --- test/clang-tidy/modernize-use-auto-cast-remove-stars.cpp +++ test/clang-tidy/modernize-use-auto-cast-remove-stars.cpp @@ -136,3 +136,64 @@ B b; A a = A(b); } + +class StringRef +{ +public: + StringRef(const char *); +}; + +template +T template_value_cast(const U &u); + +template +T *template_pointer_cast(U *u); + +template +T &template_reference_cast(U &u); + +template +const T *template_const_pointer_cast(const U *u); + +template +const T &template_const_reference_cast(const U &u); + +template +T template_value_get(StringRef s); + +template +T max(T t1, T t2); + +void f_template_cast() +{ + double d = 0; + int i1 = template_value_cast(d); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a template cast to avoid duplicating the type name + // CHECK-FIXES: auto i1 = template_value_cast(d); + + A *a = new B(); + B *b1 = template_value_cast(a); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a template cast to avoid duplicating the type name + // CHECK-FIXES: auto b1 = template_value_cast(a); + B &b2 = template_value_cast(*a); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a template cast to avoid duplicating the type name + // CHECK-FIXES: auto &b2 = template_value_cast(*a); + B *b3 = template_pointer_cast(a); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a template cast to avoid duplicating the type name + // CHECK-FIXES: auto b3 = template_pointer_cast(a); + B &b4 = template_reference_cast(*a); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a template cast to avoid duplicating the type name + // CHECK-FIXES: auto &b4 = template_reference_cast(*a); + const B *b5 = template_const_pointer_cast(a); + // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use auto when initializing with a template cast to avoid duplicating the type name + // CHECK-FIXES: const auto b5 = template_const_pointer_cast(a); + const B &b6 = template_const_reference_cast(*a); + // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use auto when initializing with a template cast to avoid duplicating the type name + // CHECK-FIXES: const auto &b6 = template_const_reference_cast(*a); + B *b7 = template_value_get("foo"); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a template cast to avoid duplicating the type name + // CHECK-FIXES: auto b7 = template_value_get("foo"); + + auto i2 = template_value_cast(d); + int i3 = max(i1, i2); +} Index: test/clang-tidy/modernize-use-auto-cast.cpp =================================================================== --- test/clang-tidy/modernize-use-auto-cast.cpp +++ test/clang-tidy/modernize-use-auto-cast.cpp @@ -138,3 +138,72 @@ B b; A a = A(b); } + +class StringRef +{ +public: + StringRef(const char *); +}; + +template +T template_value_cast(const U &u); + +template +T *template_pointer_cast(U *u); + +template +T &template_reference_cast(U &u); + +template +const T *template_const_pointer_cast(const U *u); + +template +const T &template_const_reference_cast(const U &u); + +template +T template_value_get(StringRef s); + +struct S { + template + const T *template_member_get(); +}; + +template +T max(T t1, T t2); + +void f_template_cast() +{ + double d = 0; + int i1 = template_value_cast(d); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a template cast to avoid duplicating the type name + // CHECK-FIXES: auto i1 = template_value_cast(d); + + A *a = new B(); + B *b1 = template_value_cast(a); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a template cast to avoid duplicating the type name + // CHECK-FIXES: auto *b1 = template_value_cast(a); + B &b2 = template_value_cast(*a); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a template cast to avoid duplicating the type name + // CHECK-FIXES: auto &b2 = template_value_cast(*a); + B *b3 = template_pointer_cast(a); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a template cast to avoid duplicating the type name + // CHECK-FIXES: auto *b3 = template_pointer_cast(a); + B &b4 = template_reference_cast(*a); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a template cast to avoid duplicating the type name + // CHECK-FIXES: auto &b4 = template_reference_cast(*a); + const B *b5 = template_const_pointer_cast(a); + // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use auto when initializing with a template cast to avoid duplicating the type name + // CHECK-FIXES: const auto *b5 = template_const_pointer_cast(a); + const B &b6 = template_const_reference_cast(*a); + // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use auto when initializing with a template cast to avoid duplicating the type name + // CHECK-FIXES: const auto &b6 = template_const_reference_cast(*a); + B *b7 = template_value_get("foo"); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a template cast to avoid duplicating the type name + // CHECK-FIXES: auto *b7 = template_value_get("foo"); + + S s; + const B *b8 = s.template_member_get(); + + auto i2 = template_value_cast(d); + int i3 = max(i1, i2); +}