Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions clang-tools-extra/clang-tidy/readability/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ add_clang_library(clangTidyReadabilityModule STATIC
RedundantSmartptrGetCheck.cpp
RedundantStringCStrCheck.cpp
RedundantStringInitCheck.cpp
RedundantTypenameCheck.cpp
ReferenceToConstructedTemporaryCheck.cpp
SimplifyBooleanExprCheck.cpp
SimplifySubscriptExprCheck.cpp
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
#include "RedundantSmartptrGetCheck.h"
#include "RedundantStringCStrCheck.h"
#include "RedundantStringInitCheck.h"
#include "RedundantTypenameCheck.h"
#include "ReferenceToConstructedTemporaryCheck.h"
#include "SimplifyBooleanExprCheck.h"
#include "SimplifySubscriptExprCheck.h"
Expand Down Expand Up @@ -143,6 +144,8 @@ class ReadabilityModule : public ClangTidyModule {
"readability-redundant-parentheses");
CheckFactories.registerCheck<RedundantPreprocessorCheck>(
"readability-redundant-preprocessor");
CheckFactories.registerCheck<RedundantTypenameCheck>(
"readability-redundant-typename");
CheckFactories.registerCheck<ReferenceToConstructedTemporaryCheck>(
"readability-reference-to-constructed-temporary");
CheckFactories.registerCheck<SimplifySubscriptExprCheck>(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "RedundantTypenameCheck.h"
#include "clang/AST/TypeLoc.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/Sema/DeclSpec.h"

using namespace clang::ast_matchers;
using namespace clang::ast_matchers::internal;

namespace clang::tidy::readability {

void RedundantTypenameCheck::registerMatchers(MatchFinder *Finder) {
// NOLINTNEXTLINE(readability-identifier-naming)
const VariadicDynCastAllOfMatcher<TypeLoc, TypedefTypeLoc> typedefTypeLoc;
Finder->addMatcher(typedefTypeLoc().bind("typeloc"), this);

if (!getLangOpts().CPlusPlus20)
return;

// NOLINTBEGIN(readability-identifier-naming)
const VariadicDynCastAllOfMatcher<Stmt, CXXNamedCastExpr> cxxNamedCastExpr;
const auto inImplicitTypenameContext = [&] {
return anyOf(hasParent(typedefNameDecl()),
hasParent(templateTypeParmDecl()),
hasParent(nonTypeTemplateParmDecl()),
hasParent(cxxNamedCastExpr()), hasParent(cxxNewExpr()),
hasParent(friendDecl()), hasParent(fieldDecl()),
hasParent(parmVarDecl(hasParent(expr(requiresExpr())))),
hasParent(parmVarDecl(hasParent(typeLoc(hasParent(namedDecl(
anyOf(cxxMethodDecl(), hasParent(friendDecl()),
functionDecl(has(nestedNameSpecifier()))))))))),
// Match return types.
hasParent(functionDecl(unless(cxxConversionDecl()))));
};
// NOLINTEND(readability-identifier-naming)
Finder->addMatcher(typeLoc(inImplicitTypenameContext()).bind("typeloc"),
this);
}

void RedundantTypenameCheck::check(const MatchFinder::MatchResult &Result) {
const SourceLocation TypenameKeywordLoc = [&] {
if (const auto *TTL = Result.Nodes.getNodeAs<TypedefTypeLoc>("typeloc"))
return TTL->getElaboratedKeywordLoc();

TypeLoc InnermostTypeLoc = *Result.Nodes.getNodeAs<TypeLoc>("typeloc");
while (const TypeLoc Next = InnermostTypeLoc.getNextTypeLoc())
InnermostTypeLoc = Next;

if (const auto DNTL = InnermostTypeLoc.getAs<DependentNameTypeLoc>())
return DNTL.getElaboratedKeywordLoc();

return SourceLocation();
}();

if (!TypenameKeywordLoc.isValid())
return;

diag(TypenameKeywordLoc, "redundant 'typename'")
<< FixItHint::CreateRemoval(TypenameKeywordLoc);
}

} // namespace clang::tidy::readability
37 changes: 37 additions & 0 deletions clang-tools-extra/clang-tidy/readability/RedundantTypenameCheck.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@

//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_REDUNDANTTYPENAMECHECK_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_REDUNDANTTYPENAMECHECK_H

#include "../ClangTidyCheck.h"

namespace clang::tidy::readability {

/// Finds unnecessary uses of the `typename` keyword.
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/readability/redundant-typename.html
class RedundantTypenameCheck : public ClangTidyCheck {
public:
RedundantTypenameCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
return LangOpts.CPlusPlus;
}
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
std::optional<TraversalKind> getCheckTraversalKind() const override {
return TK_IgnoreUnlessSpelledInSource;
}
};

} // namespace clang::tidy::readability

#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_REDUNDANTTYPENAMECHECK_H
5 changes: 5 additions & 0 deletions clang-tools-extra/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,11 @@ New checks

Detect redundant parentheses.

- New :doc:`readability-redundant-typename
<clang-tidy/checks/readability/redundant-typename>` check.

Finds unnecessary uses of the ``typename`` keyword.

New check aliases
^^^^^^^^^^^^^^^^^

Expand Down
1 change: 1 addition & 0 deletions clang-tools-extra/docs/clang-tidy/checks/list.rst
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,7 @@ Clang-Tidy Checks
:doc:`readability-redundant-smartptr-get <readability/redundant-smartptr-get>`, "Yes"
:doc:`readability-redundant-string-cstr <readability/redundant-string-cstr>`, "Yes"
:doc:`readability-redundant-string-init <readability/redundant-string-init>`, "Yes"
:doc:`readability-redundant-typename <readability/redundant-typename>`, "Yes"
:doc:`readability-reference-to-constructed-temporary <readability/reference-to-constructed-temporary>`,
:doc:`readability-simplify-boolean-expr <readability/simplify-boolean-expr>`, "Yes"
:doc:`readability-simplify-subscript-expr <readability/simplify-subscript-expr>`, "Yes"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
.. title:: clang-tidy - readability-redundant-typename

readability-redundant-typename
==============================

Finds unnecessary uses of the ``typename`` keyword.

``typename`` is unnecessary in two cases. First, before non-dependent names:

.. code-block:: c++

/* typename */ std::vector<int>::size_type size;

And second, since C++20, before dependent names that appear in a context
where only a type is allowed (the following example shows just a few of them):

.. code-block:: c++

template <typename T>
using trait = /* typename */ T::type;

template <typename T>
struct S {
/* typename */ T::type variable;
/* typename */ T::type function(/* typename */ T::type);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
// RUN: %check_clang_tidy -std=c++11,c++14,c++17 %s readability-redundant-typename %t \
// RUN: -- -- -fno-delayed-template-parsing
// RUN: %check_clang_tidy -std=c++20-or-later -check-suffixes=,20 %s readability-redundant-typename %t \
// RUN: -- -- -fno-delayed-template-parsing

struct NotDependent {
using R = int;
};

auto f(typename NotDependent::R)
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: redundant 'typename' [readability-redundant-typename]
// CHECK-FIXES: auto f(NotDependent::R)
-> typename NotDependent::R
// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: redundant 'typename' [readability-redundant-typename]
// CHECK-FIXES: -> NotDependent::R
{
return typename NotDependent::R();
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: redundant 'typename' [readability-redundant-typename]
// return NotDependent::R();
}

template <
typename T,
typename T::R V,
// CHECK-MESSAGES-20: :[[@LINE-1]]:3: warning: redundant 'typename' [readability-redundant-typename]
// CHECK-FIXES-20: T::R V,
typename U = typename T::R
// CHECK-MESSAGES-20: :[[@LINE-1]]:16: warning: redundant 'typename' [readability-redundant-typename]
// CHECK-FIXES-20: typename U = T::R
>
auto f() -> typename T::R
// CHECK-MESSAGES-20: :[[@LINE-1]]:13: warning: redundant 'typename' [readability-redundant-typename]
// CHECK-FIXES-20: auto f() -> T::R
{
static_cast<typename T::R>(0);
// CHECK-MESSAGES-20: :[[@LINE-1]]:15: warning: redundant 'typename' [readability-redundant-typename]
// CHECK-FIXES-20: static_cast<T::R>(0);

dynamic_cast<typename T::R>(0);
// CHECK-MESSAGES-20: :[[@LINE-1]]:16: warning: redundant 'typename' [readability-redundant-typename]
// CHECK-FIXES-20: dynamic_cast<T::R>(0);

reinterpret_cast<typename T::R>(0);
// CHECK-MESSAGES-20: :[[@LINE-1]]:20: warning: redundant 'typename' [readability-redundant-typename]
// CHECK-FIXES-20: reinterpret_cast<T::R>(0);

const_cast<typename T::R>(0);
// CHECK-MESSAGES-20: :[[@LINE-1]]:14: warning: redundant 'typename' [readability-redundant-typename]
// CHECK-FIXES-20: const_cast<T::R>(0);

static_cast<typename T::R&>(0);
// CHECK-MESSAGES-20: :[[@LINE-1]]:15: warning: redundant 'typename' [readability-redundant-typename]
// CHECK-FIXES-20: static_cast<T::R&>(0);

dynamic_cast<typename T::R const volatile &&>(0);
// CHECK-MESSAGES-20: :[[@LINE-1]]:16: warning: redundant 'typename' [readability-redundant-typename]
// CHECK-FIXES-20: dynamic_cast<T::R const volatile &&>(0);

reinterpret_cast<const typename T::template M<42>::R *>(0);
// CHECK-MESSAGES-20: :[[@LINE-1]]:26: warning: redundant 'typename' [readability-redundant-typename]
// CHECK-FIXES-20: reinterpret_cast<const T::template M<42>::R *>(0);

const_cast<const typename T::R *const[100]>(0);
// CHECK-MESSAGES-20: :[[@LINE-1]]:20: warning: redundant 'typename' [readability-redundant-typename]
// CHECK-FIXES-20: const_cast<const T::R *const[100]>(0);

(typename T::R)(0);

alignof(typename T::R);

new typename T::R();
// CHECK-MESSAGES-20: :[[@LINE-1]]:7: warning: redundant 'typename' [readability-redundant-typename]
// CHECK-FIXES-20: new T::R();

// CHECK-MESSAGES-20: :[[@LINE+2]]:15: warning: redundant 'typename' [readability-redundant-typename]
// CHECK-FIXES-20: static_cast<decltype([] {
static_cast<typename decltype([] {
return typename T::R(); // Inner typename must stay.
})::R>(0);

auto localFunctionDeclaration() -> typename T::R;
// CHECK-MESSAGES-20: :[[@LINE-1]]:38: warning: redundant 'typename' [readability-redundant-typename]
// CHECK-FIXES-20: auto localFunctionDeclaration() -> T::R;

void (*PointerToFunction)(typename T::R);
void anotherLocalFunctionDeclaration(typename T::R);

typename T::R DependentVar;
typename NotDependent::R NotDependentVar;
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: redundant 'typename' [readability-redundant-typename]
// CHECK-FIXES: NotDependent::R NotDependentVar;

return typename T::R();
}

template <typename T>
using trait = const typename T::R ****;
// CHECK-MESSAGES-20: :[[@LINE-1]]:21: warning: redundant 'typename' [readability-redundant-typename]
// CHECK-FIXES-20: using trait = const T::R ****;

template <typename T>
trait<typename T::R> m();

#if __cplusplus >= 202002L

template <typename T>
concept c = requires(typename T::R) {
// CHECK-MESSAGES-20: :[[@LINE-1]]:22: warning: redundant 'typename' [readability-redundant-typename]
// CHECK-FIXES-20: concept c = requires(T::R) {
typename T::R;
};

template <typename T>
requires c<typename T::R>
void b();

#endif // __cplusplus >= 202002L

template <typename T, typename>
struct PartiallySpecializedType {};

template <typename T>
struct PartiallySpecializedType<T, typename T::R> {};

template <typename T>
auto v = typename T::type();

template <typename T>
typename T::R f();
// CHECK-MESSAGES-20: :[[@LINE-1]]:1: warning: redundant 'typename' [readability-redundant-typename]
// CHECK-FIXES-20: T::R f();

template <typename T>
void n(typename T::R);

namespace ns {

template <typename T>
void f(typename T::R1, typename T::R2);

} // namespace ns

template <typename T>
void ns::f(
typename T::R1,
// CHECK-MESSAGES-20: :[[@LINE-1]]:3: warning: redundant 'typename' [readability-redundant-typename]
// CHECK-FIXES-20: T::R1,
typename T::R2
// CHECK-MESSAGES-20: :[[@LINE-1]]:3: warning: redundant 'typename' [readability-redundant-typename]
// CHECK-FIXES-20: T::R2
);

template <typename T>
class A {
public:
friend typename T::R;
// CHECK-MESSAGES-20: :[[@LINE-1]]:10: warning: redundant 'typename' [readability-redundant-typename]
// CHECK-FIXES-20: friend T::R;

typedef typename T::R a;
// CHECK-MESSAGES-20: :[[@LINE-1]]:11: warning: redundant 'typename' [readability-redundant-typename]
// CHECK-FIXES-20: typedef T::R a;

const typename T::R typedef b;
// CHECK-MESSAGES-20: :[[@LINE-1]]:9: warning: redundant 'typename' [readability-redundant-typename]
// CHECK-FIXES-20: const T::R typedef b;

typename T::R v;
// CHECK-MESSAGES-20: :[[@LINE-1]]:3: warning: redundant 'typename' [readability-redundant-typename]
// CHECK-FIXES-20: T::R v;

typename T::R
// CHECK-MESSAGES-20: :[[@LINE-1]]:3: warning: redundant 'typename' [readability-redundant-typename]
// CHECK-FIXES-20: T::R
g(typename T::R) {}
// CHECK-MESSAGES-20: :[[@LINE-1]]:5: warning: redundant 'typename' [readability-redundant-typename]
// CHECK-FIXES-20: g(T::R) {}

void h(typename T::R = typename T::R()) {}
// CHECK-MESSAGES-20: :[[@LINE-1]]:10: warning: redundant 'typename' [readability-redundant-typename]
// CHECK-FIXES-20: void h(T::R = typename T::R()) {}

friend void k(typename T::R) {}
// CHECK-MESSAGES-20: :[[@LINE-1]]:17: warning: redundant 'typename' [readability-redundant-typename]
// CHECK-FIXES-20: friend void k(T::R) {}

enum E1 : typename T::R {};
enum class E2 : typename T::R {};
operator typename T::R();
void m() { this->operator typename T::R(); }
};