[clang] [clang-tools-extra] [clang-tidy] Add new check: `readability-redundant-typename` (PR #161574)

Victor Chernyakin via cfe-commits cfe-commits at lists.llvm.org
Wed Oct 8 08:06:31 PDT 2025


https://github.com/localspook updated https://github.com/llvm/llvm-project/pull/161574

>From 82c842a3e8f0f2e9dcfacca0eada9f6aeacd38d8 Mon Sep 17 00:00:00 2001
From: Victor Chernyakin <chernyakin.victor.j at outlook.com>
Date: Wed, 1 Oct 2025 18:35:35 +0000
Subject: [PATCH 01/22] [clang-tidy] Add new check:
 `readability-redundant-typename`

---
 .../clang-tidy/readability/CMakeLists.txt     |   1 +
 .../readability/ReadabilityTidyModule.cpp     |   3 +
 .../readability/RedundantTypenameCheck.cpp    |  71 +++++++
 .../readability/RedundantTypenameCheck.h      |  37 ++++
 clang-tools-extra/docs/ReleaseNotes.rst       |   5 +
 .../docs/clang-tidy/checks/list.rst           |   1 +
 .../checks/readability/redundant-typename.rst |  26 +++
 .../readability/redundant-typename.cpp        | 191 ++++++++++++++++++
 8 files changed, 335 insertions(+)
 create mode 100644 clang-tools-extra/clang-tidy/readability/RedundantTypenameCheck.cpp
 create mode 100644 clang-tools-extra/clang-tidy/readability/RedundantTypenameCheck.h
 create mode 100644 clang-tools-extra/docs/clang-tidy/checks/readability/redundant-typename.rst
 create mode 100644 clang-tools-extra/test/clang-tidy/checkers/readability/redundant-typename.cpp

diff --git a/clang-tools-extra/clang-tidy/readability/CMakeLists.txt b/clang-tools-extra/clang-tidy/readability/CMakeLists.txt
index 4b4c49d3b17d1..881672e36eb7d 100644
--- a/clang-tools-extra/clang-tidy/readability/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/readability/CMakeLists.txt
@@ -48,6 +48,7 @@ add_clang_library(clangTidyReadabilityModule STATIC
   RedundantSmartptrGetCheck.cpp
   RedundantStringCStrCheck.cpp
   RedundantStringInitCheck.cpp
+  RedundantTypenameCheck.cpp
   ReferenceToConstructedTemporaryCheck.cpp
   SimplifyBooleanExprCheck.cpp
   SimplifySubscriptExprCheck.cpp
diff --git a/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp b/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp
index d01882dfc9daa..6ff64209a2b0e 100644
--- a/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp
@@ -51,6 +51,7 @@
 #include "RedundantSmartptrGetCheck.h"
 #include "RedundantStringCStrCheck.h"
 #include "RedundantStringInitCheck.h"
+#include "RedundantTypenameCheck.h"
 #include "ReferenceToConstructedTemporaryCheck.h"
 #include "SimplifyBooleanExprCheck.h"
 #include "SimplifySubscriptExprCheck.h"
@@ -140,6 +141,8 @@ class ReadabilityModule : public ClangTidyModule {
         "readability-redundant-member-init");
     CheckFactories.registerCheck<RedundantPreprocessorCheck>(
         "readability-redundant-preprocessor");
+    CheckFactories.registerCheck<RedundantTypenameCheck>(
+        "readability-redundant-typename");
     CheckFactories.registerCheck<ReferenceToConstructedTemporaryCheck>(
         "readability-reference-to-constructed-temporary");
     CheckFactories.registerCheck<SimplifySubscriptExprCheck>(
diff --git a/clang-tools-extra/clang-tidy/readability/RedundantTypenameCheck.cpp b/clang-tools-extra/clang-tidy/readability/RedundantTypenameCheck.cpp
new file mode 100644
index 0000000000000..2861cbb19e534
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/readability/RedundantTypenameCheck.cpp
@@ -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
diff --git a/clang-tools-extra/clang-tidy/readability/RedundantTypenameCheck.h b/clang-tools-extra/clang-tidy/readability/RedundantTypenameCheck.h
new file mode 100644
index 0000000000000..2df5b38dcef0b
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/readability/RedundantTypenameCheck.h
@@ -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
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 3f403c42a168a..8f87864eff036 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -199,6 +199,11 @@ New checks
   Finds virtual function overrides with different visibility than the function
   in the base class.
 
+- New :doc:`readability-redundant-typename
+  <clang-tidy/checks/readability/redundant-typename>` check.
+
+  Finds unnecessary uses of the ``typename`` keyword.
+
 New check aliases
 ^^^^^^^^^^^^^^^^^
 
diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst
index e06849c419389..2373edc3a2e96 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -408,6 +408,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"
diff --git a/clang-tools-extra/docs/clang-tidy/checks/readability/redundant-typename.rst b/clang-tools-extra/docs/clang-tidy/checks/readability/redundant-typename.rst
new file mode 100644
index 0000000000000..1c4040fbd12df
--- /dev/null
+++ b/clang-tools-extra/docs/clang-tidy/checks/readability/redundant-typename.rst
@@ -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);
+  };
diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/redundant-typename.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/redundant-typename.cpp
new file mode 100644
index 0000000000000..3e77213564f89
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/readability/redundant-typename.cpp
@@ -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(); }
+};

>From 4bae981f5a1946d01f4581dc50ad44007dba1832 Mon Sep 17 00:00:00 2001
From: Victor Chernyakin <chernyakin.victor.j at outlook.com>
Date: Wed, 1 Oct 2025 20:51:07 +0000
Subject: [PATCH 02/22] Move matcher out of lambda

---
 .../readability/RedundantTypenameCheck.cpp    | 26 ++++++++-----------
 1 file changed, 11 insertions(+), 15 deletions(-)

diff --git a/clang-tools-extra/clang-tidy/readability/RedundantTypenameCheck.cpp b/clang-tools-extra/clang-tidy/readability/RedundantTypenameCheck.cpp
index 2861cbb19e534..d464060273da2 100644
--- a/clang-tools-extra/clang-tidy/readability/RedundantTypenameCheck.cpp
+++ b/clang-tools-extra/clang-tidy/readability/RedundantTypenameCheck.cpp
@@ -28,22 +28,18 @@ void RedundantTypenameCheck::registerMatchers(MatchFinder *Finder) {
 
   // 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()))));
-  };
+  const auto inImplicitTypenameContext = 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);
+  Finder->addMatcher(typeLoc(inImplicitTypenameContext).bind("typeloc"), this);
 }
 
 void RedundantTypenameCheck::check(const MatchFinder::MatchResult &Result) {

>From 8f0118acefa993998724ac315699fa224cf90d56 Mon Sep 17 00:00:00 2001
From: Victor Chernyakin <chernyakin.victor.j at outlook.com>
Date: Wed, 1 Oct 2025 20:57:33 +0000
Subject: [PATCH 03/22] Use different names for bound nodes

---
 .../clang-tidy/readability/RedundantTypenameCheck.cpp        | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/clang-tools-extra/clang-tidy/readability/RedundantTypenameCheck.cpp b/clang-tools-extra/clang-tidy/readability/RedundantTypenameCheck.cpp
index d464060273da2..24f5b4c05892d 100644
--- a/clang-tools-extra/clang-tidy/readability/RedundantTypenameCheck.cpp
+++ b/clang-tools-extra/clang-tidy/readability/RedundantTypenameCheck.cpp
@@ -21,7 +21,7 @@ namespace clang::tidy::readability {
 void RedundantTypenameCheck::registerMatchers(MatchFinder *Finder) {
   // NOLINTNEXTLINE(readability-identifier-naming)
   const VariadicDynCastAllOfMatcher<TypeLoc, TypedefTypeLoc> typedefTypeLoc;
-  Finder->addMatcher(typedefTypeLoc().bind("typeloc"), this);
+  Finder->addMatcher(typedefTypeLoc().bind("typedefTypeLoc"), this);
 
   if (!getLangOpts().CPlusPlus20)
     return;
@@ -44,7 +44,8 @@ void RedundantTypenameCheck::registerMatchers(MatchFinder *Finder) {
 
 void RedundantTypenameCheck::check(const MatchFinder::MatchResult &Result) {
   const SourceLocation TypenameKeywordLoc = [&] {
-    if (const auto *TTL = Result.Nodes.getNodeAs<TypedefTypeLoc>("typeloc"))
+    if (const auto *TTL =
+            Result.Nodes.getNodeAs<TypedefTypeLoc>("typedefTypeLoc"))
       return TTL->getElaboratedKeywordLoc();
 
     TypeLoc InnermostTypeLoc = *Result.Nodes.getNodeAs<TypeLoc>("typeloc");

>From 21d3d44337e678bd09b6982635b9a008bc49430b Mon Sep 17 00:00:00 2001
From: Victor Chernyakin <chernyakin.victor.j at outlook.com>
Date: Wed, 1 Oct 2025 20:58:27 +0000
Subject: [PATCH 04/22] Use `isInvalid`

---
 .../clang-tidy/readability/RedundantTypenameCheck.cpp           | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang-tools-extra/clang-tidy/readability/RedundantTypenameCheck.cpp b/clang-tools-extra/clang-tidy/readability/RedundantTypenameCheck.cpp
index 24f5b4c05892d..543ef5e60ff3e 100644
--- a/clang-tools-extra/clang-tidy/readability/RedundantTypenameCheck.cpp
+++ b/clang-tools-extra/clang-tidy/readability/RedundantTypenameCheck.cpp
@@ -58,7 +58,7 @@ void RedundantTypenameCheck::check(const MatchFinder::MatchResult &Result) {
     return SourceLocation();
   }();
 
-  if (!TypenameKeywordLoc.isValid())
+  if (TypenameKeywordLoc.isInvalid())
     return;
 
   diag(TypenameKeywordLoc, "redundant 'typename'")

>From 7e100c333bc8040ccc0de6c20a391dd43f6cd1b5 Mon Sep 17 00:00:00 2001
From: Victor Chernyakin <chernyakin.victor.j at outlook.com>
Date: Wed, 1 Oct 2025 15:34:42 -0700
Subject: [PATCH 05/22] Update
 clang-tools-extra/clang-tidy/readability/RedundantTypenameCheck.h

Co-authored-by: EugeneZelenko <eugene.zelenko at gmail.com>
---
 .../clang-tidy/readability/RedundantTypenameCheck.h             | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang-tools-extra/clang-tidy/readability/RedundantTypenameCheck.h b/clang-tools-extra/clang-tidy/readability/RedundantTypenameCheck.h
index 2df5b38dcef0b..e34df450432f4 100644
--- a/clang-tools-extra/clang-tidy/readability/RedundantTypenameCheck.h
+++ b/clang-tools-extra/clang-tidy/readability/RedundantTypenameCheck.h
@@ -17,7 +17,7 @@ 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
+/// https://clang.llvm.org/extra/clang-tidy/checks/readability/redundant-typename.html
 class RedundantTypenameCheck : public ClangTidyCheck {
 public:
   RedundantTypenameCheck(StringRef Name, ClangTidyContext *Context)

>From 3e362cd9520d77574da4989c7e708dd28422a2df Mon Sep 17 00:00:00 2001
From: Victor Chernyakin <chernyakin.victor.j at outlook.com>
Date: Wed, 1 Oct 2025 23:11:29 +0000
Subject: [PATCH 06/22] Add C++98 tests, guard variable template behind C++14

---
 .../readability/redundant-typename-cxx98.cpp  | 19 +++++++++++++++++++
 .../readability/redundant-typename.cpp        |  8 +++++++-
 2 files changed, 26 insertions(+), 1 deletion(-)
 create mode 100644 clang-tools-extra/test/clang-tidy/checkers/readability/redundant-typename-cxx98.cpp

diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/redundant-typename-cxx98.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/redundant-typename-cxx98.cpp
new file mode 100644
index 0000000000000..5cad980ee271d
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/readability/redundant-typename-cxx98.cpp
@@ -0,0 +1,19 @@
+// RUN: %check_clang_tidy -std=c++98 %s readability-redundant-typename %t \
+// RUN:   -- -- -fno-delayed-template-parsing
+
+struct NotDependent {
+  typedef int R;
+};
+
+template <typename T>
+typename T::R f() {
+  static_cast<typename T::R>(0);
+
+  typename NotDependent::R NotDependentVar;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: redundant 'typename' [readability-redundant-typename]
+  // CHECK-FIXES: NotDependent::R NotDependentVar;
+
+  void notDependentFunctionDeclaration(typename NotDependent::R);
+  // CHECK-MESSAGES: :[[@LINE-1]]:40: warning: redundant 'typename' [readability-redundant-typename]
+  // CHECK-FIXES: void notDependentFunctionDeclaration(NotDependent::R);
+}
diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/redundant-typename.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/redundant-typename.cpp
index 3e77213564f89..6b440a22191ad 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/readability/redundant-typename.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/readability/redundant-typename.cpp
@@ -122,8 +122,14 @@ struct PartiallySpecializedType {};
 template <typename T>
 struct PartiallySpecializedType<T, typename T::R> {};
 
+#if __cplusplus >= 201402L
+
 template <typename T>
-auto v = typename T::type();
+typename T::R v = typename T::R();
+// CHECK-MESSAGES-20: :[[@LINE-1]]:1: warning: redundant 'typename' [readability-redundant-typename]
+// CHECK-FIXES-20: T::R v = typename T::R();
+
+#endif // __cplusplus >= 201402L
 
 template <typename T>
 typename T::R f();

>From b7a23d927cb9741ab3ef33136388ce963bc5cce5 Mon Sep 17 00:00:00 2001
From: Victor Chernyakin <chernyakin.victor.j at outlook.com>
Date: Wed, 1 Oct 2025 23:33:49 +0000
Subject: [PATCH 07/22] fix false negative with variable templates

---
 .../clang-tidy/readability/RedundantTypenameCheck.cpp            | 1 +
 1 file changed, 1 insertion(+)

diff --git a/clang-tools-extra/clang-tidy/readability/RedundantTypenameCheck.cpp b/clang-tools-extra/clang-tidy/readability/RedundantTypenameCheck.cpp
index 543ef5e60ff3e..70d46969f61f6 100644
--- a/clang-tools-extra/clang-tidy/readability/RedundantTypenameCheck.cpp
+++ b/clang-tools-extra/clang-tidy/readability/RedundantTypenameCheck.cpp
@@ -32,6 +32,7 @@ void RedundantTypenameCheck::registerMatchers(MatchFinder *Finder) {
       hasParent(typedefNameDecl()), hasParent(templateTypeParmDecl()),
       hasParent(nonTypeTemplateParmDecl()), hasParent(cxxNamedCastExpr()),
       hasParent(cxxNewExpr()), hasParent(friendDecl()), hasParent(fieldDecl()),
+      hasParent(varDecl(unless(hasDeclContext(functionDecl())))),
       hasParent(parmVarDecl(hasParent(expr(requiresExpr())))),
       hasParent(parmVarDecl(hasParent(typeLoc(hasParent(
           namedDecl(anyOf(cxxMethodDecl(), hasParent(friendDecl()),

>From fb68c3ca79c8f3036d80a965c15c423c6025f8b3 Mon Sep 17 00:00:00 2001
From: Victor Chernyakin <chernyakin.victor.j at outlook.com>
Date: Wed, 1 Oct 2025 23:47:06 +0000
Subject: [PATCH 08/22] fix false negative with variable templates: take two

---
 .../clang-tidy/readability/RedundantTypenameCheck.cpp          | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/clang-tools-extra/clang-tidy/readability/RedundantTypenameCheck.cpp b/clang-tools-extra/clang-tidy/readability/RedundantTypenameCheck.cpp
index 70d46969f61f6..91de1accb3dd9 100644
--- a/clang-tools-extra/clang-tidy/readability/RedundantTypenameCheck.cpp
+++ b/clang-tools-extra/clang-tidy/readability/RedundantTypenameCheck.cpp
@@ -32,7 +32,8 @@ void RedundantTypenameCheck::registerMatchers(MatchFinder *Finder) {
       hasParent(typedefNameDecl()), hasParent(templateTypeParmDecl()),
       hasParent(nonTypeTemplateParmDecl()), hasParent(cxxNamedCastExpr()),
       hasParent(cxxNewExpr()), hasParent(friendDecl()), hasParent(fieldDecl()),
-      hasParent(varDecl(unless(hasDeclContext(functionDecl())))),
+      hasParent(varDecl(
+          hasDeclContext(anyOf(namespaceDecl(), translationUnitDecl())))),
       hasParent(parmVarDecl(hasParent(expr(requiresExpr())))),
       hasParent(parmVarDecl(hasParent(typeLoc(hasParent(
           namedDecl(anyOf(cxxMethodDecl(), hasParent(friendDecl()),

>From 8f33b21096bb23898db6cffd47135662566692b5 Mon Sep 17 00:00:00 2001
From: Victor Chernyakin <chernyakin.victor.j at outlook.com>
Date: Wed, 1 Oct 2025 18:25:20 -0700
Subject: [PATCH 09/22] test fixes, move simple matchers into `ASTMatchers.h`

---
 .../readability/RedundantTypenameCheck.cpp    | 14 ++++-------
 clang/include/clang/ASTMatchers/ASTMatchers.h | 25 +++++++++++++++++++
 clang/lib/ASTMatchers/ASTMatchersInternal.cpp |  4 +++
 3 files changed, 34 insertions(+), 9 deletions(-)

diff --git a/clang-tools-extra/clang-tidy/readability/RedundantTypenameCheck.cpp b/clang-tools-extra/clang-tidy/readability/RedundantTypenameCheck.cpp
index 91de1accb3dd9..88cb0413e8186 100644
--- a/clang-tools-extra/clang-tidy/readability/RedundantTypenameCheck.cpp
+++ b/clang-tools-extra/clang-tidy/readability/RedundantTypenameCheck.cpp
@@ -19,29 +19,25 @@ 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("typedefTypeLoc"), this);
 
   if (!getLangOpts().CPlusPlus20)
     return;
 
-  // NOLINTBEGIN(readability-identifier-naming)
-  const VariadicDynCastAllOfMatcher<Stmt, CXXNamedCastExpr> cxxNamedCastExpr;
-  const auto inImplicitTypenameContext = anyOf(
+  const auto InImplicitTypenameContext = anyOf(
       hasParent(typedefNameDecl()), hasParent(templateTypeParmDecl()),
       hasParent(nonTypeTemplateParmDecl()), hasParent(cxxNamedCastExpr()),
       hasParent(cxxNewExpr()), hasParent(friendDecl()), hasParent(fieldDecl()),
-      hasParent(varDecl(
-          hasDeclContext(anyOf(namespaceDecl(), translationUnitDecl())))),
+      hasParent(
+          varDecl(hasDeclContext(anyOf(namespaceDecl(), translationUnitDecl())),
+                  unless(parmVarDecl()))),
       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);
+  Finder->addMatcher(typeLoc(InImplicitTypenameContext).bind("typeloc"), this);
 }
 
 void RedundantTypenameCheck::check(const MatchFinder::MatchResult &Result) {
diff --git a/clang/include/clang/ASTMatchers/ASTMatchers.h b/clang/include/clang/ASTMatchers/ASTMatchers.h
index 492863ddfc4a1..15abdd5758ea3 100644
--- a/clang/include/clang/ASTMatchers/ASTMatchers.h
+++ b/clang/include/clang/ASTMatchers/ASTMatchers.h
@@ -2763,6 +2763,17 @@ extern const internal::VariadicDynCastAllOfMatcher<Stmt, CXXDynamicCastExpr>
 extern const internal::VariadicDynCastAllOfMatcher<Stmt, CXXConstCastExpr>
     cxxConstCastExpr;
 
+/// Matches any named cast expression.
+///
+/// Example: Matches all four of the casts in
+/// \code
+///   struct S { virtual void f(); };
+///   void* ptr = dynamic_cast<void*>(reinterpret_cast<S*>(
+///                    const_cast<int*>(static_cast<int*>(nullptr))));
+/// \endcode
+extern const internal::VariadicDynCastAllOfMatcher<Stmt, CXXNamedCastExpr>
+    cxxNamedCastExpr;
+
 /// Matches a C-style cast expression.
 ///
 /// Example: Matches (int) 2.2f in
@@ -6987,6 +6998,20 @@ extern const internal::VariadicDynCastAllOfMatcher<
     TypeLoc, TemplateSpecializationTypeLoc>
     templateSpecializationTypeLoc;
 
+/// Matches `TypedefTypeLoc`s.
+///
+/// Given
+/// \code
+///   using t1 = int;
+///   template <typename T> class C { using t2 = int; };
+///   t1 var1;
+///   const C<char>::t2* var2;
+/// \endcode
+/// typedefTypeLoc()
+///   matches `t1` (in the declaration of var1) and `C<char>::t2`.
+extern const internal::VariadicDynCastAllOfMatcher<TypeLoc, TypedefTypeLoc>
+    typedefTypeLoc;
+
 /// Matches template specialization `TypeLoc`s, class template specializations,
 /// variable template specializations, and function template specializations
 /// that have at least one `TemplateArgumentLoc` matching the given
diff --git a/clang/lib/ASTMatchers/ASTMatchersInternal.cpp b/clang/lib/ASTMatchers/ASTMatchersInternal.cpp
index 1f0e007dafc65..516882232cdf5 100644
--- a/clang/lib/ASTMatchers/ASTMatchersInternal.cpp
+++ b/clang/lib/ASTMatchers/ASTMatchersInternal.cpp
@@ -810,6 +810,8 @@ const internal::VariadicDynCastAllOfMatcher<TypeLoc, ReferenceTypeLoc>
 const internal::VariadicDynCastAllOfMatcher<TypeLoc,
                                             TemplateSpecializationTypeLoc>
     templateSpecializationTypeLoc;
+const internal::VariadicDynCastAllOfMatcher<TypeLoc, TypedefTypeLoc>
+    typedefTypeLoc;
 
 const internal::VariadicDynCastAllOfMatcher<Stmt, UnaryExprOrTypeTraitExpr>
     unaryExprOrTypeTraitExpr;
@@ -1009,6 +1011,8 @@ const internal::VariadicDynCastAllOfMatcher<Stmt, CXXDynamicCastExpr>
     cxxDynamicCastExpr;
 const internal::VariadicDynCastAllOfMatcher<Stmt, CXXConstCastExpr>
     cxxConstCastExpr;
+const internal::VariadicDynCastAllOfMatcher<Stmt, CXXNamedCastExpr>
+    cxxNamedCastExpr;
 const internal::VariadicDynCastAllOfMatcher<Stmt, CStyleCastExpr>
     cStyleCastExpr;
 const internal::VariadicDynCastAllOfMatcher<Stmt, ExplicitCastExpr>

>From 99a7c14d4f1e4b08238aea21aee0825b9f13fe05 Mon Sep 17 00:00:00 2001
From: Victor Chernyakin <chernyakin.victor.j at outlook.com>
Date: Wed, 1 Oct 2025 19:21:52 -0700
Subject: [PATCH 10/22] reduce duplication in matchers

---
 .../readability/RedundantTypenameCheck.cpp    | 22 +++++++++----------
 1 file changed, 11 insertions(+), 11 deletions(-)

diff --git a/clang-tools-extra/clang-tidy/readability/RedundantTypenameCheck.cpp b/clang-tools-extra/clang-tidy/readability/RedundantTypenameCheck.cpp
index 88cb0413e8186..9a5fb78846bb5 100644
--- a/clang-tools-extra/clang-tidy/readability/RedundantTypenameCheck.cpp
+++ b/clang-tools-extra/clang-tidy/readability/RedundantTypenameCheck.cpp
@@ -25,18 +25,18 @@ void RedundantTypenameCheck::registerMatchers(MatchFinder *Finder) {
     return;
 
   const auto InImplicitTypenameContext = anyOf(
-      hasParent(typedefNameDecl()), hasParent(templateTypeParmDecl()),
-      hasParent(nonTypeTemplateParmDecl()), hasParent(cxxNamedCastExpr()),
-      hasParent(cxxNewExpr()), hasParent(friendDecl()), hasParent(fieldDecl()),
-      hasParent(
+      hasParent(decl(anyOf(
+          typedefNameDecl(), templateTypeParmDecl(), nonTypeTemplateParmDecl(),
+          friendDecl(), fieldDecl(),
           varDecl(hasDeclContext(anyOf(namespaceDecl(), translationUnitDecl())),
-                  unless(parmVarDecl()))),
-      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()))));
+                  unless(parmVarDecl())),
+          parmVarDecl(hasParent(expr(requiresExpr()))),
+          parmVarDecl(hasParent(typeLoc(hasParent(
+              decl(anyOf(cxxMethodDecl(), hasParent(friendDecl()),
+                         functionDecl(has(nestedNameSpecifier())))))))),
+          // Match return types.
+          functionDecl(unless(cxxConversionDecl()))))),
+      hasParent(expr(anyOf(cxxNamedCastExpr(), cxxNewExpr()))));
   Finder->addMatcher(typeLoc(InImplicitTypenameContext).bind("typeloc"), this);
 }
 

>From 2309f33b3a3da0f072e58510d1d967c0e3f7be6e Mon Sep 17 00:00:00 2001
From: Victor Chernyakin <chernyakin.victor.j at outlook.com>
Date: Wed, 1 Oct 2025 19:26:55 -0700
Subject: [PATCH 11/22] Remove stray newline

---
 .../clang-tidy/readability/RedundantTypenameCheck.h              | 1 -
 1 file changed, 1 deletion(-)

diff --git a/clang-tools-extra/clang-tidy/readability/RedundantTypenameCheck.h b/clang-tools-extra/clang-tidy/readability/RedundantTypenameCheck.h
index e34df450432f4..02224403dc47a 100644
--- a/clang-tools-extra/clang-tidy/readability/RedundantTypenameCheck.h
+++ b/clang-tools-extra/clang-tidy/readability/RedundantTypenameCheck.h
@@ -1,4 +1,3 @@
-
 //===----------------------------------------------------------------------===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.

>From 41b823cc5eefcc148824170ee8de18bea11acf8b Mon Sep 17 00:00:00 2001
From: Victor Chernyakin <chernyakin.victor.j at outlook.com>
Date: Wed, 1 Oct 2025 20:21:49 -0700
Subject: [PATCH 12/22] Add tests with pack expansions

---
 .../checkers/readability/redundant-typename.cpp          | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/redundant-typename.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/redundant-typename.cpp
index 6b440a22191ad..217f0e8210f6b 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/readability/redundant-typename.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/readability/redundant-typename.cpp
@@ -156,7 +156,10 @@ void ns::f(
   // CHECK-FIXES-20: T::R2
 );
 
-template <typename T>
+template <typename... Ts>
+void p(typename Ts::R...);
+
+template <typename T, typename... Ts>
 class A {
 public:
   friend typename T::R;
@@ -186,6 +189,10 @@ class A {
   // CHECK-MESSAGES-20: :[[@LINE-1]]:10: warning: redundant 'typename' [readability-redundant-typename]
   // CHECK-FIXES-20: void h(T::R = typename T::R()) {}
 
+  void p(typename Ts::R...);
+  // CHECK-MESSAGES-20: :[[@LINE-1]]:10: warning: redundant 'typename' [readability-redundant-typename]
+  // CHECK-FIXES-20: void p(Ts::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) {}

>From 893b3ae6e9eb7764901a9ccab9994cc258d3ba8d Mon Sep 17 00:00:00 2001
From: Victor Chernyakin <chernyakin.victor.j at outlook.com>
Date: Thu, 2 Oct 2025 17:06:22 -0700
Subject: [PATCH 13/22] Fix false negative and false positive

---
 .../readability/RedundantTypenameCheck.cpp    | 19 +++++++++++++++----
 .../readability/redundant-typename.cpp        |  6 ++++++
 2 files changed, 21 insertions(+), 4 deletions(-)

diff --git a/clang-tools-extra/clang-tidy/readability/RedundantTypenameCheck.cpp b/clang-tools-extra/clang-tidy/readability/RedundantTypenameCheck.cpp
index 9a5fb78846bb5..7da6c3decf90c 100644
--- a/clang-tools-extra/clang-tidy/readability/RedundantTypenameCheck.cpp
+++ b/clang-tools-extra/clang-tidy/readability/RedundantTypenameCheck.cpp
@@ -11,6 +11,7 @@
 #include "clang/ASTMatchers/ASTMatchFinder.h"
 #include "clang/ASTMatchers/ASTMatchers.h"
 #include "clang/Basic/Diagnostic.h"
+#include "clang/Lex/Lexer.h"
 #include "clang/Sema/DeclSpec.h"
 
 using namespace clang::ast_matchers;
@@ -41,7 +42,7 @@ void RedundantTypenameCheck::registerMatchers(MatchFinder *Finder) {
 }
 
 void RedundantTypenameCheck::check(const MatchFinder::MatchResult &Result) {
-  const SourceLocation TypenameKeywordLoc = [&] {
+  const SourceLocation ElaboratedKeywordLoc = [&] {
     if (const auto *TTL =
             Result.Nodes.getNodeAs<TypedefTypeLoc>("typedefTypeLoc"))
       return TTL->getElaboratedKeywordLoc();
@@ -53,14 +54,24 @@ void RedundantTypenameCheck::check(const MatchFinder::MatchResult &Result) {
     if (const auto DNTL = InnermostTypeLoc.getAs<DependentNameTypeLoc>())
       return DNTL.getElaboratedKeywordLoc();
 
+    if (const auto TSTL =
+            InnermostTypeLoc.getAs<TemplateSpecializationTypeLoc>())
+      return TSTL.getElaboratedKeywordLoc();
+
     return SourceLocation();
   }();
 
-  if (TypenameKeywordLoc.isInvalid())
+  if (ElaboratedKeywordLoc.isInvalid())
+    return;
+
+  if (Token ElaboratedKeyword;
+      Lexer::getRawToken(ElaboratedKeywordLoc, ElaboratedKeyword,
+                         *Result.SourceManager, getLangOpts()) ||
+      ElaboratedKeyword.getRawIdentifier() != "typename")
     return;
 
-  diag(TypenameKeywordLoc, "redundant 'typename'")
-      << FixItHint::CreateRemoval(TypenameKeywordLoc);
+  diag(ElaboratedKeywordLoc, "redundant 'typename'")
+      << FixItHint::CreateRemoval(ElaboratedKeywordLoc);
 }
 
 } // namespace clang::tidy::readability
diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/redundant-typename.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/redundant-typename.cpp
index 217f0e8210f6b..20ff86886080c 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/readability/redundant-typename.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/readability/redundant-typename.cpp
@@ -98,6 +98,11 @@ 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>
+using t = typename T::template R<T>;
+// CHECK-MESSAGES-20: :[[@LINE-1]]:11: warning: redundant 'typename' [readability-redundant-typename]
+// CHECK-FIXES-20: using t = T::template R<T>;
+
 template <typename T>
 trait<typename T::R> m();
 
@@ -197,6 +202,7 @@ class A {
   // CHECK-MESSAGES-20: :[[@LINE-1]]:17: warning: redundant 'typename' [readability-redundant-typename]
   // CHECK-FIXES-20: friend void k(T::R) {}
 
+  friend struct T::R;
   enum E1 : typename T::R {};
   enum class E2 : typename T::R {};
   operator typename T::R();

>From e6944fa0bff0b42e7b752c26cc3b8932452c321b Mon Sep 17 00:00:00 2001
From: Victor Chernyakin <chernyakin.victor.j at outlook.com>
Date: Thu, 2 Oct 2025 18:53:52 -0700
Subject: [PATCH 14/22] Add test case

---
 .../test/clang-tidy/checkers/readability/redundant-typename.cpp  | 1 +
 1 file changed, 1 insertion(+)

diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/redundant-typename.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/redundant-typename.cpp
index 20ff86886080c..2ac0e034b88bb 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/readability/redundant-typename.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/readability/redundant-typename.cpp
@@ -203,6 +203,7 @@ class A {
   // CHECK-FIXES-20: friend void k(T::R) {}
 
   friend struct T::R;
+  using typename T::R;
   enum E1 : typename T::R {};
   enum class E2 : typename T::R {};
   operator typename T::R();

>From e897eb78830cd966668f4caf3f426cc1009aaafe Mon Sep 17 00:00:00 2001
From: Victor Chernyakin <chernyakin.victor.j at outlook.com>
Date: Fri, 3 Oct 2025 13:34:01 -0700
Subject: [PATCH 15/22] Test C++03

---
 .../checkers/readability/redundant-typename-cxx98.cpp           | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/redundant-typename-cxx98.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/redundant-typename-cxx98.cpp
index 5cad980ee271d..74b0b49cdae8d 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/readability/redundant-typename-cxx98.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/readability/redundant-typename-cxx98.cpp
@@ -1,4 +1,4 @@
-// RUN: %check_clang_tidy -std=c++98 %s readability-redundant-typename %t \
+// RUN: %check_clang_tidy -std=c++98,c++03 %s readability-redundant-typename %t \
 // RUN:   -- -- -fno-delayed-template-parsing
 
 struct NotDependent {

>From efb594a61091466080ddf59f4049debf0d47c886 Mon Sep 17 00:00:00 2001
From: Victor Chernyakin <chernyakin.victor.j at outlook.com>
Date: Fri, 3 Oct 2025 13:34:30 -0700
Subject: [PATCH 16/22] Adjust matcher usage examples

---
 clang/include/clang/ASTMatchers/ASTMatchers.h | 11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)

diff --git a/clang/include/clang/ASTMatchers/ASTMatchers.h b/clang/include/clang/ASTMatchers/ASTMatchers.h
index 15abdd5758ea3..56186f7ee2342 100644
--- a/clang/include/clang/ASTMatchers/ASTMatchers.h
+++ b/clang/include/clang/ASTMatchers/ASTMatchers.h
@@ -2768,8 +2768,11 @@ extern const internal::VariadicDynCastAllOfMatcher<Stmt, CXXConstCastExpr>
 /// Example: Matches all four of the casts in
 /// \code
 ///   struct S { virtual void f(); };
-///   void* ptr = dynamic_cast<void*>(reinterpret_cast<S*>(
-///                    const_cast<int*>(static_cast<int*>(nullptr))));
+///   S* p = nullptr;
+///   S* ptr1 = static_cast<S*>(p);
+///   S* ptr2 = reinterpret_cast<S*>(p);
+///   S* ptr3 = dynamic_cast<S*>(p);
+///   S* ptr4 = const_cast<S*>(p);
 /// \endcode
 extern const internal::VariadicDynCastAllOfMatcher<Stmt, CXXNamedCastExpr>
     cxxNamedCastExpr;
@@ -7003,9 +7006,9 @@ extern const internal::VariadicDynCastAllOfMatcher<
 /// Given
 /// \code
 ///   using t1 = int;
-///   template <typename T> class C { using t2 = int; };
+///   template <typename T> struct S { using t2 = int; };
 ///   t1 var1;
-///   const C<char>::t2* var2;
+///   const S<char>::t2* var2;
 /// \endcode
 /// typedefTypeLoc()
 ///   matches `t1` (in the declaration of var1) and `C<char>::t2`.

>From 809c8a48c155ff158c5d78aab8ee968c1d65e929 Mon Sep 17 00:00:00 2001
From: Victor Chernyakin <chernyakin.victor.j at outlook.com>
Date: Fri, 3 Oct 2025 13:37:06 -0700
Subject: [PATCH 17/22] unnecessary -> redundant

---
 .../clang-tidy/readability/RedundantTypenameCheck.h           | 2 +-
 clang-tools-extra/docs/ReleaseNotes.rst                       | 2 +-
 .../docs/clang-tidy/checks/readability/redundant-typename.rst | 4 ++--
 3 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/clang-tools-extra/clang-tidy/readability/RedundantTypenameCheck.h b/clang-tools-extra/clang-tidy/readability/RedundantTypenameCheck.h
index 02224403dc47a..8e86b0c765fd7 100644
--- a/clang-tools-extra/clang-tidy/readability/RedundantTypenameCheck.h
+++ b/clang-tools-extra/clang-tidy/readability/RedundantTypenameCheck.h
@@ -13,7 +13,7 @@
 
 namespace clang::tidy::readability {
 
-/// Finds unnecessary uses of the `typename` keyword.
+/// Finds redundant uses of the `typename` keyword.
 ///
 /// For the user-facing documentation see:
 /// https://clang.llvm.org/extra/clang-tidy/checks/readability/redundant-typename.html
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 09a5ad0230128..52cf855ac9598 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -211,7 +211,7 @@ New checks
 - New :doc:`readability-redundant-typename
   <clang-tidy/checks/readability/redundant-typename>` check.
 
-  Finds unnecessary uses of the ``typename`` keyword.
+  Finds redundant uses of the ``typename`` keyword.
 
 New check aliases
 ^^^^^^^^^^^^^^^^^
diff --git a/clang-tools-extra/docs/clang-tidy/checks/readability/redundant-typename.rst b/clang-tools-extra/docs/clang-tidy/checks/readability/redundant-typename.rst
index 1c4040fbd12df..7f5737f4e72b4 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/readability/redundant-typename.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/readability/redundant-typename.rst
@@ -3,9 +3,9 @@
 readability-redundant-typename
 ==============================
 
-Finds unnecessary uses of the ``typename`` keyword.
+Finds redundant uses of the ``typename`` keyword.
 
-``typename`` is unnecessary in two cases. First, before non-dependent names:
+``typename`` is redundant in two cases. First, before non-dependent names:
 
 .. code-block:: c++
 

>From beb67d4aa008c4c0b45765f6c4865391866d73f8 Mon Sep 17 00:00:00 2001
From: Victor Chernyakin <chernyakin.victor.j at outlook.com>
Date: Fri, 3 Oct 2025 13:39:26 -0700
Subject: [PATCH 18/22] Fix typo

---
 clang/include/clang/ASTMatchers/ASTMatchers.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/include/clang/ASTMatchers/ASTMatchers.h b/clang/include/clang/ASTMatchers/ASTMatchers.h
index 56186f7ee2342..9e833bf2f96a2 100644
--- a/clang/include/clang/ASTMatchers/ASTMatchers.h
+++ b/clang/include/clang/ASTMatchers/ASTMatchers.h
@@ -7011,7 +7011,7 @@ extern const internal::VariadicDynCastAllOfMatcher<
 ///   const S<char>::t2* var2;
 /// \endcode
 /// typedefTypeLoc()
-///   matches `t1` (in the declaration of var1) and `C<char>::t2`.
+///   matches `t1` (in the declaration of var1) and `S<char>::t2`.
 extern const internal::VariadicDynCastAllOfMatcher<TypeLoc, TypedefTypeLoc>
     typedefTypeLoc;
 

>From 2f3eee7aef66cedf85a68c8f8d296d48162190bd Mon Sep 17 00:00:00 2001
From: Victor Chernyakin <chernyakin.victor.j at outlook.com>
Date: Wed, 8 Oct 2025 06:21:57 -0700
Subject: [PATCH 19/22] Fix false positive and negative

---
 .../readability/RedundantTypenameCheck.cpp    | 35 ++++++++++++-------
 .../readability/redundant-typename.cpp        |  9 +++--
 clang/include/clang/ASTMatchers/ASTMatchers.h | 14 --------
 clang/lib/ASTMatchers/ASTMatchersInternal.cpp |  2 --
 4 files changed, 28 insertions(+), 32 deletions(-)

diff --git a/clang-tools-extra/clang-tidy/readability/RedundantTypenameCheck.cpp b/clang-tools-extra/clang-tidy/readability/RedundantTypenameCheck.cpp
index 7da6c3decf90c..42f1d2e660339 100644
--- a/clang-tools-extra/clang-tidy/readability/RedundantTypenameCheck.cpp
+++ b/clang-tools-extra/clang-tidy/readability/RedundantTypenameCheck.cpp
@@ -20,7 +20,9 @@ using namespace clang::ast_matchers::internal;
 namespace clang::tidy::readability {
 
 void RedundantTypenameCheck::registerMatchers(MatchFinder *Finder) {
-  Finder->addMatcher(typedefTypeLoc().bind("typedefTypeLoc"), this);
+  Finder->addMatcher(typeLoc(unless(hasAncestor(decl(isInstantiated()))))
+                         .bind("nonDependentTypeLoc"),
+                     this);
 
   if (!getLangOpts().CPlusPlus20)
     return;
@@ -38,25 +40,32 @@ void RedundantTypenameCheck::registerMatchers(MatchFinder *Finder) {
           // Match return types.
           functionDecl(unless(cxxConversionDecl()))))),
       hasParent(expr(anyOf(cxxNamedCastExpr(), cxxNewExpr()))));
-  Finder->addMatcher(typeLoc(InImplicitTypenameContext).bind("typeloc"), this);
+  Finder->addMatcher(
+      typeLoc(InImplicitTypenameContext).bind("dependentTypeLoc"), this);
 }
 
 void RedundantTypenameCheck::check(const MatchFinder::MatchResult &Result) {
   const SourceLocation ElaboratedKeywordLoc = [&] {
-    if (const auto *TTL =
-            Result.Nodes.getNodeAs<TypedefTypeLoc>("typedefTypeLoc"))
-      return TTL->getElaboratedKeywordLoc();
+    if (const auto *NonDependentTypeLoc =
+            Result.Nodes.getNodeAs<TypeLoc>("nonDependentTypeLoc")) {
+      if (const auto TTL = NonDependentTypeLoc->getAs<TypedefTypeLoc>())
+        return TTL.getElaboratedKeywordLoc();
 
-    TypeLoc InnermostTypeLoc = *Result.Nodes.getNodeAs<TypeLoc>("typeloc");
-    while (const TypeLoc Next = InnermostTypeLoc.getNextTypeLoc())
-      InnermostTypeLoc = Next;
+      if (const auto TTL = NonDependentTypeLoc->getAs<TagTypeLoc>())
+        return TTL.getElaboratedKeywordLoc();
+    } else {
+      TypeLoc InnermostTypeLoc =
+          *Result.Nodes.getNodeAs<TypeLoc>("dependentTypeLoc");
+      while (const TypeLoc Next = InnermostTypeLoc.getNextTypeLoc())
+        InnermostTypeLoc = Next;
 
-    if (const auto DNTL = InnermostTypeLoc.getAs<DependentNameTypeLoc>())
-      return DNTL.getElaboratedKeywordLoc();
+      if (const auto DNTL = InnermostTypeLoc.getAs<DependentNameTypeLoc>())
+        return DNTL.getElaboratedKeywordLoc();
 
-    if (const auto TSTL =
-            InnermostTypeLoc.getAs<TemplateSpecializationTypeLoc>())
-      return TSTL.getElaboratedKeywordLoc();
+      if (const auto TSTL =
+              InnermostTypeLoc.getAs<TemplateSpecializationTypeLoc>())
+        return TSTL.getElaboratedKeywordLoc();
+    }
 
     return SourceLocation();
   }();
diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/redundant-typename.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/redundant-typename.cpp
index 2ac0e034b88bb..e5ba955e5fdbd 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/readability/redundant-typename.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/readability/redundant-typename.cpp
@@ -5,11 +5,12 @@
 
 struct NotDependent {
   using R = int;
+  struct S {};
 };
 
-auto f(typename NotDependent::R)
+auto f(typename NotDependent::S)
   // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: redundant 'typename' [readability-redundant-typename]
-  // CHECK-FIXES: auto f(NotDependent::R)
+  // CHECK-FIXES: auto f(NotDependent::S)
   -> typename NotDependent::R
   // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: redundant 'typename' [readability-redundant-typename]
   // CHECK-FIXES: -> NotDependent::R
@@ -142,7 +143,9 @@ typename T::R f();
 // CHECK-FIXES-20: T::R f();
 
 template <typename T>
-void n(typename T::R);
+void n(typename T::R *) {}
+
+template void n<NotDependent>(int *);
 
 namespace ns {
 
diff --git a/clang/include/clang/ASTMatchers/ASTMatchers.h b/clang/include/clang/ASTMatchers/ASTMatchers.h
index 9e833bf2f96a2..98e62de2a9bfb 100644
--- a/clang/include/clang/ASTMatchers/ASTMatchers.h
+++ b/clang/include/clang/ASTMatchers/ASTMatchers.h
@@ -7001,20 +7001,6 @@ extern const internal::VariadicDynCastAllOfMatcher<
     TypeLoc, TemplateSpecializationTypeLoc>
     templateSpecializationTypeLoc;
 
-/// Matches `TypedefTypeLoc`s.
-///
-/// Given
-/// \code
-///   using t1 = int;
-///   template <typename T> struct S { using t2 = int; };
-///   t1 var1;
-///   const S<char>::t2* var2;
-/// \endcode
-/// typedefTypeLoc()
-///   matches `t1` (in the declaration of var1) and `S<char>::t2`.
-extern const internal::VariadicDynCastAllOfMatcher<TypeLoc, TypedefTypeLoc>
-    typedefTypeLoc;
-
 /// Matches template specialization `TypeLoc`s, class template specializations,
 /// variable template specializations, and function template specializations
 /// that have at least one `TemplateArgumentLoc` matching the given
diff --git a/clang/lib/ASTMatchers/ASTMatchersInternal.cpp b/clang/lib/ASTMatchers/ASTMatchersInternal.cpp
index 516882232cdf5..42f124ba852ed 100644
--- a/clang/lib/ASTMatchers/ASTMatchersInternal.cpp
+++ b/clang/lib/ASTMatchers/ASTMatchersInternal.cpp
@@ -810,8 +810,6 @@ const internal::VariadicDynCastAllOfMatcher<TypeLoc, ReferenceTypeLoc>
 const internal::VariadicDynCastAllOfMatcher<TypeLoc,
                                             TemplateSpecializationTypeLoc>
     templateSpecializationTypeLoc;
-const internal::VariadicDynCastAllOfMatcher<TypeLoc, TypedefTypeLoc>
-    typedefTypeLoc;
 
 const internal::VariadicDynCastAllOfMatcher<Stmt, UnaryExprOrTypeTraitExpr>
     unaryExprOrTypeTraitExpr;

>From 994d620f8f8ca6ba03a3754ad8044f8e06b283f4 Mon Sep 17 00:00:00 2001
From: Victor Chernyakin <chernyakin.victor.j at outlook.com>
Date: Wed, 8 Oct 2025 07:36:44 -0700
Subject: [PATCH 20/22] Make release note a bit more detailed

---
 clang-tools-extra/docs/ReleaseNotes.rst | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 52cf855ac9598..929148337704d 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -211,7 +211,9 @@ New checks
 - New :doc:`readability-redundant-typename
   <clang-tidy/checks/readability/redundant-typename>` check.
 
-  Finds redundant uses of the ``typename`` keyword.
+  Finds redundant uses of the ``typename`` keyword. Can be used
+  to modernize code to take advantage of the C++20 rules that make
+  ``typename`` redundant in many cases where it was mandatory before.
 
 New check aliases
 ^^^^^^^^^^^^^^^^^

>From 93e27b358783f225eeeaee9a409a1e9d7351fe91 Mon Sep 17 00:00:00 2001
From: Victor Chernyakin <chernyakin.victor.j at outlook.com>
Date: Wed, 8 Oct 2025 07:45:25 -0700
Subject: [PATCH 21/22] adjust test

---
 .../test/clang-tidy/checkers/readability/redundant-typename.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/redundant-typename.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/redundant-typename.cpp
index e5ba955e5fdbd..cbff6e2ec8bb7 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/readability/redundant-typename.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/readability/redundant-typename.cpp
@@ -145,7 +145,7 @@ typename T::R f();
 template <typename T>
 void n(typename T::R *) {}
 
-template void n<NotDependent>(int *);
+template void n<NotDependent>(NotDependent::R *);
 
 namespace ns {
 

>From de7e96c6852cb3a1fc3865fd150877849d130b04 Mon Sep 17 00:00:00 2001
From: Victor Chernyakin <chernyakin.victor.j at outlook.com>
Date: Wed, 8 Oct 2025 08:05:21 -0700
Subject: [PATCH 22/22] Revert "Make release note a bit more detailed"

---
 clang-tools-extra/docs/ReleaseNotes.rst | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 929148337704d..52cf855ac9598 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -211,9 +211,7 @@ New checks
 - New :doc:`readability-redundant-typename
   <clang-tidy/checks/readability/redundant-typename>` check.
 
-  Finds redundant uses of the ``typename`` keyword. Can be used
-  to modernize code to take advantage of the C++20 rules that make
-  ``typename`` redundant in many cases where it was mandatory before.
+  Finds redundant uses of the ``typename`` keyword.
 
 New check aliases
 ^^^^^^^^^^^^^^^^^



More information about the cfe-commits mailing list