Skip to content

Commit

Permalink
[clang-tidy] Enhance modernize-use-auto to templated function casts
Browse files Browse the repository at this point in the history
Summary:
Use auto when declaring variables that are initialized by calling a templated
function that returns its explicit first argument.

Fixes PR26763.

Reviewers: aaron.ballman, alexfh, staronj, Prazek

Subscribers: Eugene.Zelenko, JDevlieghere, cfe-commits

Differential Revision: https://reviews.llvm.org/D27166

llvm-svn: 289797
  • Loading branch information
pepsiman committed Dec 15, 2016
1 parent 232d11c commit 8e67aa9
Show file tree
Hide file tree
Showing 5 changed files with 241 additions and 10 deletions.
50 changes: 46 additions & 4 deletions clang-tools-extra/clang-tidy/modernize/UseAutoCheck.cpp
Expand Up @@ -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.
Expand Down Expand Up @@ -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() {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;
}
Expand All @@ -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.");
}
Expand Down
3 changes: 2 additions & 1 deletion clang-tools-extra/docs/ReleaseNotes.rst
Expand Up @@ -93,7 +93,8 @@ Improvements to clang-tidy

- The `modernize-use-auto
<http://clang.llvm.org/extra/clang-tidy/checks/modernize-use-auto.html>`_ 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.

- The modernize-use-default check has been renamed to `modernize-use-equals-default
<http://clang.llvm.org/extra/clang-tidy/checks/modernize-use-equals-default.html>`_.
Expand Down
Expand Up @@ -160,7 +160,11 @@ cast expression. In this cases, the declaration type can be replaced with
auto *my_pointer = static_cast<TypeName>(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``. Calls to function templates are considered to behave as
casts if the first template argument is explicit and is a type, and the function
returns that type, or a pointer or reference to it.

Known Limitations
-----------------
Expand All @@ -170,9 +174,6 @@ Known Limitations

* 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
-------

Expand Down
Expand Up @@ -44,7 +44,7 @@ void f_static_cast() {

const B *b3 = static_cast<const B *>(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<const B *>(a);
// CHECK-FIXES: const auto b3 = static_cast<const B *>(a);

B &b4 = static_cast<B &>(*a);
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name
Expand All @@ -58,6 +58,9 @@ void f_static_cast() {
// 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<B &>(*a), &b7 = static_cast<B &>(*a);

// Don't warn when non-cast involved
long double cast = static_cast<long double>(l), noncast = 5;

// Don't warn when auto is already being used.
auto i3 = static_cast<int>(l);
auto *b8 = static_cast<B *>(a);
Expand Down Expand Up @@ -136,3 +139,95 @@ void f_functional_cast() {
B b;
A a = A(b);
}

class StringRef
{
public:
StringRef(const char *);
const char *begin() const;
const char *end() const;
};

template <typename T, typename U>
T template_value_cast(const U &u);

template <typename T, typename U>
T *template_pointer_cast(U *u);

template <typename T, typename U>
T &template_reference_cast(U &u);

template <typename T, typename U>
const T *template_const_pointer_cast(const U *u);

template <typename T, typename U>
const T &template_const_reference_cast(const U &u);

template <typename T>
T template_value_get(StringRef s);

struct S {
template <typename T>
const T *template_member_get();
};

template <typename T>
T max(T t1, T t2);

void f_template_cast()
{
double d = 0;
int i1 = template_value_cast<int>(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<int>(d);

A *a = new B();
B *b1 = template_value_cast<B *>(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<B *>(a);
B &b2 = template_value_cast<B &>(*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<B &>(*a);
B *b3 = template_pointer_cast<B>(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<B>(a);
B &b4 = template_reference_cast<B>(*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<B>(*a);
const B *b5 = template_const_pointer_cast<B>(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<B>(a);
const B &b6 = template_const_reference_cast<B>(*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<B>(*a);
B *b7 = template_value_get<B *>("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<B *>("foo");
B *b8 = template_value_get<B *>("foo"), *b9 = template_value_get<B *>("bar");
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a template cast to avoid duplicating the type name
// CHECK-FIXES: auto b8 = template_value_get<B *>("foo"), b9 = template_value_get<B *>("bar");

S s;
const B *b10 = s.template_member_get<B>();
// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use auto when initializing with a template cast to avoid duplicating the type name
// CHECK-FIXES: const auto b10 = s.template_member_get<B>();

// Don't warn when auto is already being used.
auto i2 = template_value_cast<int>(d);
auto *i3 = template_value_cast<int *>(d);
auto **i4 = template_value_cast<int **>(d);
auto &i5 = template_reference_cast<int>(d);

// Don't warn for implicit template arguments.
int i6 = max(i1, i2);

// Don't warn for mismatched var and initializer types.
A *a1 = template_value_cast<B *>(a);

// Don't warn for mismatched var types.
B *b11 = template_value_get<B *>("foo"), b12 = template_value_get<B>("bar");

// Don't warn for implicit variables.
for (auto &c : template_reference_cast<StringRef>(*a)) {
}
}
92 changes: 92 additions & 0 deletions clang-tools-extra/test/clang-tidy/modernize-use-auto-cast.cpp
Expand Up @@ -138,3 +138,95 @@ void f_functional_cast() {
B b;
A a = A(b);
}

class StringRef
{
public:
StringRef(const char *);
const char *begin() const;
const char *end() const;
};

template <typename T, typename U>
T template_value_cast(const U &u);

template <typename T, typename U>
T *template_pointer_cast(U *u);

template <typename T, typename U>
T &template_reference_cast(U &u);

template <typename T, typename U>
const T *template_const_pointer_cast(const U *u);

template <typename T, typename U>
const T &template_const_reference_cast(const U &u);

template <typename T>
T template_value_get(StringRef s);

struct S {
template <typename T>
const T *template_member_get();
};

template <typename T>
T max(T t1, T t2);

void f_template_cast()
{
double d = 0;
int i1 = template_value_cast<int>(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<int>(d);

A *a = new B();
B *b1 = template_value_cast<B *>(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<B *>(a);
B &b2 = template_value_cast<B &>(*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<B &>(*a);
B *b3 = template_pointer_cast<B>(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<B>(a);
B &b4 = template_reference_cast<B>(*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<B>(*a);
const B *b5 = template_const_pointer_cast<B>(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<B>(a);
const B &b6 = template_const_reference_cast<B>(*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<B>(*a);
B *b7 = template_value_get<B *>("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<B *>("foo");
B *b8 = template_value_get<B *>("foo"), *b9 = template_value_get<B *>("bar");
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a template cast to avoid duplicating the type name
// CHECK-FIXES: auto *b8 = template_value_get<B *>("foo"), *b9 = template_value_get<B *>("bar");

S s;
const B *b10 = s.template_member_get<B>();
// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use auto when initializing with a template cast to avoid duplicating the type name
// CHECK-FIXES: const auto *b10 = s.template_member_get<B>();

// Don't warn when auto is already being used.
auto i2 = template_value_cast<int>(d);
auto *i3 = template_value_cast<int *>(d);
auto **i4 = template_value_cast<int **>(d);
auto &i5 = template_reference_cast<int>(d);

// Don't warn for implicit template arguments.
int i6 = max(i1, i2);

// Don't warn for mismatched var and initializer types.
A *a1 = template_value_cast<B *>(a);

// Don't warn for mismatched var types.
B *b11 = template_value_get<B *>("foo"), b12 = template_value_get<B>("bar");

// Don't warn for implicit variables.
for (auto &c : template_reference_cast<StringRef>(*a)) {
}
}

0 comments on commit 8e67aa9

Please sign in to comment.