Index: clang-tidy/modernize/UseAutoCheck.h =================================================================== --- clang-tidy/modernize/UseAutoCheck.h +++ clang-tidy/modernize/UseAutoCheck.h @@ -25,7 +25,9 @@ private: void replaceIterators(const DeclStmt *D, ASTContext *Context); - void replaceNew(const DeclStmt *D, ASTContext *Context); + template + void replaceExpr(const DeclStmt *D, ASTContext *Context, TypeFn GetType, + StringRef message); const bool RemoveStars; }; Index: clang-tidy/modernize/UseAutoCheck.cpp =================================================================== --- clang-tidy/modernize/UseAutoCheck.cpp +++ clang-tidy/modernize/UseAutoCheck.cpp @@ -23,6 +23,7 @@ const char IteratorDeclStmtId[] = "iterator_decl"; const char DeclWithNewId[] = "decl_new"; +const char DeclWithCastId[] = "decl_cast"; /// \brief Matches variable declarations that have explicit initializers that /// are not initializer lists. @@ -243,6 +244,17 @@ .bind(DeclWithNewId); } +StatementMatcher makeDeclWithCastMatcher() { + return declStmt( + has(varDecl( + hasInitializer(explicitCastExpr()), + // Skip declarations that are already using auto. + unless(anyOf(hasType(autoType()), + hasType(pointerType(pointee(autoType()))), + hasType(referenceType(pointee(autoType())))))))) + .bind(DeclWithCastId); +} + } // namespace UseAutoCheck::UseAutoCheck(StringRef Name, ClangTidyContext *Context) @@ -259,6 +271,7 @@ if (getLangOpts().CPlusPlus) { Finder->addMatcher(makeIteratorDeclMatcher(), this); Finder->addMatcher(makeDeclWithNewMatcher(), this); + Finder->addMatcher(makeDeclWithCastMatcher(), this); } } @@ -313,7 +326,9 @@ << FixItHint::CreateReplacement(Range, "auto"); } -void UseAutoCheck::replaceNew(const DeclStmt *D, ASTContext *Context) { +template +void UseAutoCheck::replaceExpr(const DeclStmt *D, ASTContext *Context, + TypeFn GetType, StringRef message) { const auto *FirstDecl = dyn_cast(*D->decl_begin()); // Ensure that there is at least one VarDecl within the DeclStmt. if (!FirstDecl) @@ -328,13 +343,13 @@ if (!V) return; - const auto *NewExpr = cast(V->getInit()->IgnoreParenImpCasts()); - // Ensure that every VarDecl has a CXXNewExpr initializer. - if (!NewExpr) + const auto *Expr = cast(V->getInit()->IgnoreParenImpCasts()); + // Ensure that every VarDecl has an ExprType initializer. + if (!Expr) return; // If VarDecl and Initializer have mismatching unqualified types. - if (!Context->hasSameUnqualifiedType(V->getType(), NewExpr->getType())) + if (!Context->hasSameUnqualifiedType(V->getType(), GetType(Expr))) return; // All subsequent variables in this declaration should have the same @@ -367,9 +382,13 @@ Loc.getTypeLocClass() == TypeLoc::Qualified) Loc = Loc.getNextTypeLoc(); } + while (Loc.getTypeLocClass() == TypeLoc::LValueReference || + Loc.getTypeLocClass() == TypeLoc::RValueReference || + Loc.getTypeLocClass() == TypeLoc::Qualified) { + Loc = Loc.getNextTypeLoc(); + } SourceRange Range(Loc.getSourceRange()); - auto Diag = diag(Range.getBegin(), "use auto when initializing with new" - " to avoid duplicating the type name"); + auto Diag = diag(Range.getBegin(), message); // Space after 'auto' to handle cases where the '*' in the pointer type is // next to the identifier. This avoids changing 'int *p' into 'autop'. @@ -382,7 +401,22 @@ replaceIterators(Decl, Result.Context); } else if (const auto *Decl = Result.Nodes.getNodeAs(DeclWithNewId)) { - replaceNew(Decl, Result.Context); + auto GetType = [](const CXXNewExpr *Expr) { + return Expr->getType(); + }; + + replaceExpr(Decl, Result.Context, GetType, + "use auto when initializing with new to avoid " + "duplicating the type name"); + } else if (const auto *Decl = + Result.Nodes.getNodeAs(DeclWithCastId)) { + auto GetType = [](const ExplicitCastExpr *Expr) { + return Expr->getTypeAsWritten(); + }; + + replaceExpr(Decl, Result.Context, GetType, + "use auto when initializing with a cast to " + "avoid duplicating the type name"); } else { llvm_unreachable("Bad Callback. No node provided."); } 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 @@ -143,6 +143,27 @@ auto *my_first_pointer = new TypeName, *my_second_pointer = new TypeName; +Cast expressions +---------------- + +Frequently, when a variable is declared and initialized with a cast, the +variable type has to be written twice: in the declaration type and in the +cast expression. In this cases, the declaration type can be replaced with +``auto`` improving readability and maintainability. + +.. code-block:: c++ + + TypeName *my_pointer = static_cast(my_param); + + // becomes + + auto *my_pointer = static_cast(my_param); + +The check handles ``static_cast``, ``dynamic_cast``, ``const_cast``, +``reinterpret_cast``, functional casts and c style casts. +It does not handle function templates that behave as casts, such as +``llvm::dyn_cast``, ``boost::lexical_cast`` or ``gsl::narrow_cast``. + Known Limitations ----------------- Index: test/clang-tidy/modernize-use-auto-cast-remove-stars.cpp =================================================================== --- /dev/null +++ test/clang-tidy/modernize-use-auto-cast-remove-stars.cpp @@ -0,0 +1,118 @@ +// RUN: %check_clang_tidy %s modernize-use-auto %t -- \ +// RUN: -config="{CheckOptions: [{key: modernize-use-auto.RemoveStars, value: '1'}]}" \ +// RUN: -- -std=c++11 + +struct A { + virtual ~A() {} +}; + +struct B : public A {}; + +struct C {}; + +void f_static_cast() { + long l = 1; + int i1 = static_cast(l); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name + // CHECK-FIXES: auto i1 = static_cast(l); + + const int i2 = static_cast(l); + // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use auto when initializing with a cast to avoid duplicating the type name + // CHECK-FIXES: const auto i2 = static_cast(l); + + long long ll = static_cast(l); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name + // CHECK-FIXES: auto ll = static_cast(l); + unsigned long long ull = static_cast(l); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name + // CHECK-FIXES: auto ull = static_cast(l); + unsigned int ui = static_cast(l); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name + // CHECK-FIXES: auto ui = static_cast(l); + long double ld = static_cast(l); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name + // CHECK-FIXES: auto ld = static_cast(l); + + A *a = new B(); + B *b1 = static_cast(a); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name + // CHECK-FIXES: auto b1 = static_cast(a); + + B *const b2 = static_cast(a); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name + // CHECK-FIXES: auto const b2 = static_cast(a); + + const B *b3 = static_cast(a); + // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use auto when initializing with a cast to avoid duplicating the type name + // CHECK-FIXES: auto b3 = static_cast(a); + + B &b4 = static_cast(*a); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name + // CHECK-FIXES: auto &b4 = static_cast(*a); + + const B &b5 = static_cast(*a); + // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use auto when initializing with a cast to avoid duplicating the type name + // CHECK-FIXES: const auto &b5 = static_cast(*a); + + B &b6 = static_cast(*a), &b7 = static_cast(*a); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name + // CHECK-FIXES: auto &b6 = static_cast(*a), &b7 = static_cast(*a); + + // Don't warn when auto is already being used. + auto i3 = static_cast(l); + auto *b8 = static_cast(a); + auto &b9 = static_cast(*a); +} + +void f_dynamic_cast() { + A *a = new B(); + B *b1 = dynamic_cast(a); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name + // CHECK-FIXES: auto b1 = dynamic_cast(a); + + B &b2 = dynamic_cast(*a); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name + // CHECK-FIXES: auto &b2 = dynamic_cast(*a); +} + +void f_reinterpret_cast() { + auto *a = new A(); + C *c1 = reinterpret_cast(a); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name + // CHECK-FIXES: auto c1 = reinterpret_cast(a); + + C &c2 = reinterpret_cast(*a); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name + // CHECK-FIXES: auto &c2 = reinterpret_cast(*a); +} + +void f_const_cast() { + const A *a1 = new A(); + A *a2 = const_cast(a1); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name + // CHECK-FIXES: auto a2 = const_cast(a1); + A &a3 = const_cast(*a1); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name + // CHECK-FIXES: auto &a3 = const_cast(*a1); +} + +void f_cstyle_cast() { + auto *a = new A(); + C *c1 = (C *)a; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name + // CHECK-FIXES: auto c1 = (C *)a; + + C &c2 = (C &)*a; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name + // CHECK-FIXES: auto &c2 = (C &)*a; +} + +void f_functional_cast() { + long l = 1; + int i1 = int(l); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name + // CHECK-FIXES: auto i1 = int(l); + + B b; + A a = A(b); +} Index: test/clang-tidy/modernize-use-auto-cast.cpp =================================================================== --- /dev/null +++ test/clang-tidy/modernize-use-auto-cast.cpp @@ -0,0 +1,117 @@ +// RUN: %check_clang_tidy %s modernize-use-auto %t -- -- \ +// RUN: -std=c++11 -I %S/Inputs/modernize-use-auto + +struct A { + virtual ~A() {} +}; + +struct B : public A {}; + +struct C {}; + +void f_static_cast() { + long l = 1; + int i1 = static_cast(l); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name + // CHECK-FIXES: auto i1 = static_cast(l); + + const int i2 = static_cast(l); + // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use auto when initializing with a cast to avoid duplicating the type name + // CHECK-FIXES: const auto i2 = static_cast(l); + + long long ll = static_cast(l); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name + // CHECK-FIXES: auto ll = static_cast(l); + unsigned long long ull = static_cast(l); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name + // CHECK-FIXES: auto ull = static_cast(l); + unsigned int ui = static_cast(l); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name + // CHECK-FIXES: auto ui = static_cast(l); + long double ld = static_cast(l); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name + // CHECK-FIXES: auto ld = static_cast(l); + + A *a = new B(); + B *b1 = static_cast(a); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name + // CHECK-FIXES: auto *b1 = static_cast(a); + + B *const b2 = static_cast(a); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name + // CHECK-FIXES: auto *const b2 = static_cast(a); + + const B *b3 = static_cast(a); + // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use auto when initializing with a cast to avoid duplicating the type name + // CHECK-FIXES: const auto *b3 = static_cast(a); + + B &b4 = static_cast(*a); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name + // CHECK-FIXES: auto &b4 = static_cast(*a); + + const B &b5 = static_cast(*a); + // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use auto when initializing with a cast to avoid duplicating the type name + // CHECK-FIXES: const auto &b5 = static_cast(*a); + + B &b6 = static_cast(*a), &b7 = static_cast(*a); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name + // CHECK-FIXES: auto &b6 = static_cast(*a), &b7 = static_cast(*a); + + // Don't warn when auto is already being used. + auto i3 = static_cast(l); + auto *b8 = static_cast(a); + auto &b9 = static_cast(*a); +} + +void f_dynamic_cast() { + A *a = new B(); + B *b1 = dynamic_cast(a); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name + // CHECK-FIXES: auto *b1 = dynamic_cast(a); + + B &b2 = dynamic_cast(*a); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name + // CHECK-FIXES: auto &b2 = dynamic_cast(*a); +} + +void f_reinterpret_cast() { + auto *a = new A(); + C *c1 = reinterpret_cast(a); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name + // CHECK-FIXES: auto *c1 = reinterpret_cast(a); + + C &c2 = reinterpret_cast(*a); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name + // CHECK-FIXES: auto &c2 = reinterpret_cast(*a); +} + +void f_const_cast() { + const A *a1 = new A(); + A *a2 = const_cast(a1); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name + // CHECK-FIXES: auto *a2 = const_cast(a1); + A &a3 = const_cast(*a1); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name + // CHECK-FIXES: auto &a3 = const_cast(*a1); +} + +void f_cstyle_cast() { + auto *a = new A(); + C *c1 = (C *)a; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name + // CHECK-FIXES: auto *c1 = (C *)a; + + C &c2 = (C &)*a; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name + // CHECK-FIXES: auto &c2 = (C &)*a; +} + +void f_functional_cast() { + long l = 1; + int i1 = int(l); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name + // CHECK-FIXES: auto i1 = int(l); + + B b; + A a = A(b); +} Index: test/clang-tidy/modernize-use-auto-new.cpp =================================================================== --- test/clang-tidy/modernize-use-auto-new.cpp +++ test/clang-tidy/modernize-use-auto-new.cpp @@ -15,6 +15,10 @@ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use auto when initializing with new // CHECK-FIXES: static auto *a_static = new MyType(); + long long *ll = new long long(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with new + // CHECK-FIXES: auto *ll = new long long(); + MyType *derived = new MyDerivedType(); void *vd = new MyType();