Author: Henrik G. Olsson
Date: 2026-04-08T09:54:04-07:00
New Revision: 430e1be254f90d177e997266bc1c2e700e16d1cc

URL: 
https://github.com/llvm/llvm-project/commit/430e1be254f90d177e997266bc1c2e700e16d1cc
DIFF: 
https://github.com/llvm/llvm-project/commit/430e1be254f90d177e997266bc1c2e700e16d1cc.diff

LOG: [clang-tidy] detect redundant uses of LLVM's cast, dyn_cast (#189274)

Warns when casting to the same pointee type, or when the target pointee
type is a super type of the argument's pointee type. Supported
functions:
 - cast
 - cast_if_present
 - cast_or_null
 - dyn_cast
 - dyn_cast_if_present
 - dyn_cast_or_null

---------

Co-authored-by: Victor Chernyakin <[email protected]>

Added: 
    clang-tools-extra/clang-tidy/llvm/RedundantCastingCheck.cpp
    clang-tools-extra/clang-tidy/llvm/RedundantCastingCheck.h
    clang-tools-extra/docs/clang-tidy/checks/llvm/redundant-casting.rst
    clang-tools-extra/test/clang-tidy/checkers/llvm/redundant-casting.cpp

Modified: 
    clang-tools-extra/clang-tidy/llvm/CMakeLists.txt
    clang-tools-extra/clang-tidy/llvm/LLVMTidyModule.cpp
    clang-tools-extra/docs/ReleaseNotes.rst
    clang-tools-extra/docs/clang-tidy/checks/list.rst
    clang/include/clang/ASTMatchers/ASTMatchers.h
    clang/include/clang/ASTMatchers/ASTMatchersInternal.h

Removed: 
    


################################################################################
diff  --git a/clang-tools-extra/clang-tidy/llvm/CMakeLists.txt 
b/clang-tools-extra/clang-tidy/llvm/CMakeLists.txt
index a807f0ab65f87..c81882e0e2024 100644
--- a/clang-tools-extra/clang-tidy/llvm/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/llvm/CMakeLists.txt
@@ -10,6 +10,7 @@ add_clang_library(clangTidyLLVMModule STATIC
   PreferIsaOrDynCastInConditionalsCheck.cpp
   PreferRegisterOverUnsignedCheck.cpp
   PreferStaticOverAnonymousNamespaceCheck.cpp
+  RedundantCastingCheck.cpp
   TwineLocalCheck.cpp
   TypeSwitchCaseTypesCheck.cpp
   UseNewMLIROpBuilderCheck.cpp

diff  --git a/clang-tools-extra/clang-tidy/llvm/LLVMTidyModule.cpp 
b/clang-tools-extra/clang-tidy/llvm/LLVMTidyModule.cpp
index c180574bdeed6..104fcf63712f7 100644
--- a/clang-tools-extra/clang-tidy/llvm/LLVMTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/llvm/LLVMTidyModule.cpp
@@ -16,6 +16,7 @@
 #include "PreferIsaOrDynCastInConditionalsCheck.h"
 #include "PreferRegisterOverUnsignedCheck.h"
 #include "PreferStaticOverAnonymousNamespaceCheck.h"
+#include "RedundantCastingCheck.h"
 #include "TwineLocalCheck.h"
 #include "TypeSwitchCaseTypesCheck.h"
 #include "UseNewMLIROpBuilderCheck.h"
@@ -43,6 +44,8 @@ class LLVMModule : public ClangTidyModule {
         "llvm-prefer-static-over-anonymous-namespace");
     CheckFactories.registerCheck<readability::QualifiedAutoCheck>(
         "llvm-qualified-auto");
+    CheckFactories.registerCheck<RedundantCastingCheck>(
+        "llvm-redundant-casting");
     CheckFactories.registerCheck<TwineLocalCheck>("llvm-twine-local");
     CheckFactories.registerCheck<TypeSwitchCaseTypesCheck>(
         "llvm-type-switch-case-types");

diff  --git a/clang-tools-extra/clang-tidy/llvm/RedundantCastingCheck.cpp 
b/clang-tools-extra/clang-tidy/llvm/RedundantCastingCheck.cpp
new file mode 100644
index 0000000000000..8f8c152b5de86
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/llvm/RedundantCastingCheck.cpp
@@ -0,0 +1,183 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 "RedundantCastingCheck.h"
+#include "clang/AST/ASTTypeTraits.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/ExprCXX.h"
+#include "clang/AST/NestedNameSpecifierBase.h"
+#include "clang/AST/ParentMapContext.h"
+#include "clang/AST/TemplateBase.h"
+#include "clang/AST/TypeBase.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Lex/Lexer.h"
+#include "llvm/ADT/STLExtras.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::llvm_check {
+
+namespace {
+AST_MATCHER(Expr, isMacroID) { return Node.getExprLoc().isMacroID(); }
+AST_MATCHER_P(OverloadExpr, hasAnyUnresolvedName, ArrayRef<StringRef>, Names) {
+  auto DeclName = Node.getName();
+  if (!DeclName.isIdentifier())
+    return false;
+  const IdentifierInfo *II = DeclName.getAsIdentifierInfo();
+  return llvm::any_of(Names, [II](StringRef Name) { return II->isStr(Name); });
+}
+} // namespace
+
+static constexpr StringRef FunctionNames[] = {
+    "cast",     "cast_or_null",     "cast_if_present",
+    "dyn_cast", "dyn_cast_or_null", "dyn_cast_if_present"};
+
+void RedundantCastingCheck::registerMatchers(MatchFinder *Finder) {
+  auto IsInLLVMNamespace = hasDeclContext(
+      namespaceDecl(hasName("llvm"), hasDeclContext(translationUnitDecl())));
+  auto AnyCalleeName =
+      allOf(unless(isMacroID()), unless(cxxMemberCallExpr()),
+            callee(expr(ignoringImpCasts(
+                declRefExpr(
+                    to(namedDecl(hasAnyName(FunctionNames), 
IsInLLVMNamespace)),
+                    templateArgumentLocCountIs(1))
+                    .bind("callee")))));
+  auto AnyCalleeNameInUninstantiatedTemplate =
+      allOf(unless(isMacroID()), unless(cxxMemberCallExpr()),
+            callee(expr(ignoringImpCasts(
+                unresolvedLookupExpr(hasAnyUnresolvedName(FunctionNames),
+                                     templateArgumentLocCountIs(1))
+                    .bind("callee")))));
+  Finder->addMatcher(callExpr(AnyCalleeName, argumentCountIs(1),
+                              optionally(hasParent(
+                                  
callExpr(AnyCalleeName).bind("parent_cast"))))
+                         .bind("call"),
+                     this);
+  Finder->addMatcher(
+      callExpr(
+          AnyCalleeNameInUninstantiatedTemplate, argumentCountIs(1),
+          optionally(hasAncestor(
+              namespaceDecl(hasName("llvm"), hasParent(translationUnitDecl()))
+                  .bind("llvm_ns"))))
+          .bind("call"),
+      this);
+}
+
+static QualType stripPointerOrReference(QualType Ty) {
+  QualType Pointee = Ty->getPointeeType();
+  if (Pointee.isNull())
+    return Ty;
+  return Pointee;
+}
+
+static bool isLLVMNamespace(NestedNameSpecifier NNS) {
+  if (NNS.getKind() != NestedNameSpecifier::Kind::Namespace)
+    return false;
+  auto Pair = NNS.getAsNamespaceAndPrefix();
+  if (Pair.Namespace->getNamespace()->getName() != "llvm")
+    return false;
+  const NestedNameSpecifier::Kind Kind = Pair.Prefix.getKind();
+  return Kind == NestedNameSpecifier::Kind::Null ||
+         Kind == NestedNameSpecifier::Kind::Global;
+}
+
+void RedundantCastingCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto &Nodes = Result.Nodes;
+  const auto *Call = Nodes.getNodeAs<CallExpr>("call");
+
+  CanQualType RetTy;
+  std::string FuncName;
+  if (const auto *ResolvedCallee = Nodes.getNodeAs<DeclRefExpr>("callee")) {
+    const auto *F = cast<FunctionDecl>(ResolvedCallee->getDecl());
+    RetTy = stripPointerOrReference(F->getReturnType())
+                ->getCanonicalTypeUnqualified();
+    FuncName = F->getName();
+  } else if (const auto *UnresolvedCallee =
+                 Nodes.getNodeAs<UnresolvedLookupExpr>("callee")) {
+    const bool IsExplicitlyLLVM =
+        isLLVMNamespace(UnresolvedCallee->getQualifier());
+    const auto *CallerNS = Nodes.getNodeAs<NamedDecl>("llvm_ns");
+    if (!IsExplicitlyLLVM && !CallerNS)
+      return;
+    auto TArg = UnresolvedCallee->template_arguments()[0].getArgument();
+    if (TArg.getKind() != TemplateArgument::Type)
+      return;
+
+    RetTy = TArg.getAsType()->getCanonicalTypeUnqualified();
+    FuncName = UnresolvedCallee->getName().getAsString();
+  } else {
+    llvm_unreachable("");
+  }
+
+  const auto *Arg = Call->getArg(0);
+  const QualType ArgTy = Arg->getType();
+  const QualType ArgPointeeTy = stripPointerOrReference(ArgTy);
+  const CanQualType FromTy = ArgPointeeTy->getCanonicalTypeUnqualified();
+  const auto *FromDecl = FromTy->getAsCXXRecordDecl();
+  const auto *RetDecl = RetTy->getAsCXXRecordDecl();
+  const bool IsDerived =
+      FromDecl && RetDecl && FromDecl->isDerivedFrom(RetDecl);
+  if (FromTy != RetTy && !IsDerived)
+    return;
+
+  QualType ParentTy;
+  if (const auto *ParentCast = Nodes.getNodeAs<Expr>("parent_cast")) {
+    ParentTy = ParentCast->getType();
+  } else {
+    // IgnoreUnlessSpelledInSource prevents matching implicit casts
+    const TraversalKindScope TmpTraversalKind(*Result.Context, TK_AsIs);
+    for (const DynTypedNode Parent : Result.Context->getParents(*Call)) {
+      if (const auto *ParentCastExpr = Parent.get<CastExpr>()) {
+        ParentTy = ParentCastExpr->getType();
+        break;
+      }
+    }
+  }
+  if (!ParentTy.isNull()) {
+    const CXXRecordDecl *ParentDecl = ParentTy->getAsCXXRecordDecl();
+    if (FromDecl && ParentDecl) {
+      CXXBasePaths Paths(/*FindAmbiguities=*/true,
+                         /*RecordPaths=*/false,
+                         /*DetectVirtual=*/false);
+      const bool IsDerivedFromParent =
+          FromDecl && ParentDecl && FromDecl->isDerivedFrom(ParentDecl, Paths);
+      // For the following case a direct `cast<A>(d)` would be ambiguous:
+      //   struct A {};
+      //   struct B : A {};
+      //   struct C : A {};
+      //   struct D : B, C {};
+      // So we should not warn for `A *a = cast<C>(d)`.
+      if (IsDerivedFromParent &&
+          Paths.isAmbiguous(ParentTy->getCanonicalTypeUnqualified()))
+        return;
+    }
+  }
+
+  auto GetText = [&](SourceRange R) {
+    return Lexer::getSourceText(CharSourceRange::getTokenRange(R),
+                                *Result.SourceManager, getLangOpts());
+  };
+  StringRef ArgText = GetText(Arg->getSourceRange());
+  diag(Call->getExprLoc(), "redundant use of '%0'")
+      << FuncName
+      << FixItHint::CreateReplacement(Call->getSourceRange(), ArgText);
+  // printing the canonical type for a template parameter prints as e.g.
+  // 'type-parameter-0-0'
+  const QualType DiagFromTy(ArgPointeeTy->getUnqualifiedDesugaredType(), 0);
+  diag(Arg->getExprLoc(),
+       "source expression has%select{| pointee}0 type %1%select{|, which is a "
+       "subtype of %3}2",
+       DiagnosticIDs::Note)
+      << Arg->getSourceRange() << ArgTy->isPointerType() << DiagFromTy
+      << (FromTy != RetTy) << RetTy;
+}
+
+} // namespace clang::tidy::llvm_check

diff  --git a/clang-tools-extra/clang-tidy/llvm/RedundantCastingCheck.h 
b/clang-tools-extra/clang-tidy/llvm/RedundantCastingCheck.h
new file mode 100644
index 0000000000000..2ab7369c0358e
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/llvm/RedundantCastingCheck.h
@@ -0,0 +1,39 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_LLVM_REDUNDANTCASTINGCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_LLVM_REDUNDANTCASTINGCHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::llvm_check {
+
+/// Detect redundant uses of LLVM's cast and dyn_cast functions.
+///
+/// For the user-facing documentation see:
+/// https://clang.llvm.org/extra/clang-tidy/checks/llvm/redundant-casting.html
+class RedundantCastingCheck : public ClangTidyCheck {
+public:
+  RedundantCastingCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+  bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+    return LangOpts.CPlusPlus;
+  }
+
+  std::optional<TraversalKind> getCheckTraversalKind() const override {
+    // Casts can be redundant for some instantiations but not others.
+    // Only emit warnings in templates in the uninstantated versions.
+    return TK_IgnoreUnlessSpelledInSource;
+  }
+};
+
+} // namespace clang::tidy::llvm_check
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_LLVM_REDUNDANTCASTINGCHECK_H

diff  --git a/clang-tools-extra/docs/ReleaseNotes.rst 
b/clang-tools-extra/docs/ReleaseNotes.rst
index 914b4a1dbbff9..78a7ff6ff7561 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -125,6 +125,13 @@ New checks
   Finds functions where throwing exceptions is unsafe but the function is still
   marked as potentially throwing.
 
+- New :doc:`llvm-redundant-casting
+  <clang-tidy/checks/llvm/redundant-casting>` check.
+
+  Points out uses of ``cast<>``, ``dyn_cast<>`` and their ``or_null`` variants
+  that are unnecessary because the argument already is of the target type, or a
+  derived type thereof.
+
 - New :doc:`llvm-type-switch-case-types
   <clang-tidy/checks/llvm/type-switch-case-types>` check.
 

diff  --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst 
b/clang-tools-extra/docs/clang-tidy/checks/list.rst
index fcf8ff43a25e6..7a64101848971 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -252,6 +252,7 @@ Clang-Tidy Checks
    :doc:`llvm-prefer-isa-or-dyn-cast-in-conditionals 
<llvm/prefer-isa-or-dyn-cast-in-conditionals>`, "Yes"
    :doc:`llvm-prefer-register-over-unsigned 
<llvm/prefer-register-over-unsigned>`, "Yes"
    :doc:`llvm-prefer-static-over-anonymous-namespace 
<llvm/prefer-static-over-anonymous-namespace>`,
+   :doc:`llvm-redundant-casting <llvm/redundant-casting>`, "Yes"
    :doc:`llvm-twine-local <llvm/twine-local>`, "Yes"
    :doc:`llvm-type-switch-case-types <llvm/type-switch-case-types>`, "Yes"
    :doc:`llvm-use-new-mlir-op-builder <llvm/use-new-mlir-op-builder>`, "Yes"

diff  --git 
a/clang-tools-extra/docs/clang-tidy/checks/llvm/redundant-casting.rst 
b/clang-tools-extra/docs/clang-tidy/checks/llvm/redundant-casting.rst
new file mode 100644
index 0000000000000..84596b26ee382
--- /dev/null
+++ b/clang-tools-extra/docs/clang-tidy/checks/llvm/redundant-casting.rst
@@ -0,0 +1,32 @@
+.. title:: clang-tidy - llvm-redundant-casting
+
+llvm-redundant-casting
+======================
+
+Points out uses of ``cast<>``, ``dyn_cast<>`` and their ``or_null`` variants
+that are unnecessary because the argument already is of the target type, or a
+derived type thereof.
+
+.. code-block:: c++
+
+  struct A {};
+  A a;
+  // Finds:
+  A x = cast<A>(a);
+  // replaced by:
+  A x = a;
+
+  struct B : public A {};
+  B b;
+  // Finds:
+  A y = cast<A>(b);
+  // replaced by:
+  A y = b;
+
+Supported functions:
+ - ``llvm::cast``
+ - ``llvm::cast_or_null``
+ - ``llvm::cast_if_present``
+ - ``llvm::dyn_cast``
+ - ``llvm::dyn_cast_or_null``
+ - ``llvm::dyn_cast_if_present``

diff  --git 
a/clang-tools-extra/test/clang-tidy/checkers/llvm/redundant-casting.cpp 
b/clang-tools-extra/test/clang-tidy/checkers/llvm/redundant-casting.cpp
new file mode 100644
index 0000000000000..933cbadc51701
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/llvm/redundant-casting.cpp
@@ -0,0 +1,286 @@
+// RUN: %check_clang_tidy -std=c++17-or-later %s llvm-redundant-casting %t -- 
-- -fno-delayed-template-parsing
+
+namespace llvm {
+#define CAST_FUNCTION(name)                                          \
+template <typename To, typename From>                                \
+[[nodiscard]] inline decltype(auto) name(const From &Val) {          \
+  return static_cast<const To&>(Val);                                \
+}                                                                    \
+template <typename To, typename From>                                \
+[[nodiscard]] inline decltype(auto) name(From &Val) {                \
+  return static_cast<To&>(Val);                                      \
+}                                                                    \
+template <typename To, typename From>                                \
+[[nodiscard]] inline decltype(auto) name(const From *Val) {          \
+  return static_cast<const To*>(Val);                                \
+}                                                                    \
+template <typename To, typename From>                                \
+[[nodiscard]] inline decltype(auto) name(From *Val) {                \
+  return static_cast<To*>(Val);                                      \
+}
+CAST_FUNCTION(cast)
+CAST_FUNCTION(dyn_cast)
+CAST_FUNCTION(cast_or_null)
+CAST_FUNCTION(cast_if_present)
+CAST_FUNCTION(dyn_cast_or_null)
+CAST_FUNCTION(dyn_cast_if_present)
+}
+
+struct A {};
+struct B : A {};
+A &getA();
+
+void testCast(A& value) {
+  A& a1 = llvm::cast<A>(value);
+  // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: redundant use of 'cast' 
[llvm-redundant-casting]
+  // CHECK-MESSAGES: :[[@LINE-2]]:25: note: source expression has type 'A'
+  // CHECK-FIXES: A& a1 = value;
+  (void)a1;
+}
+
+void testDynCast(A& value) {
+  A& a2 = llvm::dyn_cast<A>(value);
+  // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: redundant use of 'dyn_cast' 
[llvm-redundant-casting]
+  // CHECK-MESSAGES: :[[@LINE-2]]:29: note: source expression has type 'A'
+  // CHECK-FIXES: A& a2 = value;
+  (void)a2;
+}
+
+void testCastOrNull(A& value) {
+  A& a3 = llvm::cast_or_null<A>(value);
+  // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: redundant use of 'cast_or_null' 
[llvm-redundant-casting]
+  // CHECK-MESSAGES: :[[@LINE-2]]:33: note: source expression has type 'A'
+  // CHECK-FIXES: A& a3 = value;
+  (void)a3;
+}
+
+void testCastIfPresent(A& value) {
+  A& a4 = llvm::cast_if_present<A>(value);
+  // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: redundant use of 
'cast_if_present' [llvm-redundant-casting]
+  // CHECK-MESSAGES: :[[@LINE-2]]:36: note: source expression has type 'A'
+  // CHECK-FIXES: A& a4 = value;
+  (void)a4;
+}
+
+void testDynCastOrNull(A& value) {
+  A& a5 = llvm::dyn_cast_or_null<A>(value);
+  // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: redundant use of 
'dyn_cast_or_null' [llvm-redundant-casting]
+  // CHECK-MESSAGES: :[[@LINE-2]]:37: note: source expression has type 'A'
+  // CHECK-FIXES: A& a5 = value;
+  (void)a5;
+}
+
+void testDynCastIfPresent(A& value) {
+  A& a6 = llvm::dyn_cast_if_present<A>(value);
+  // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: redundant use of 
'dyn_cast_if_present' [llvm-redundant-casting]
+  // CHECK-MESSAGES: :[[@LINE-2]]:40: note: source expression has type 'A'
+  // CHECK-FIXES: A& a6 = value;
+  (void)a6;
+}
+
+void testCastNonDeclRef() {
+  A& a7 = llvm::cast<A>((getA()));
+  // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: redundant use of 'cast' 
[llvm-redundant-casting]
+  // CHECK-MESSAGES: :[[@LINE-2]]:25: note: source expression has type 'A'
+  // CHECK-FIXES: A& a7 = (getA());
+  (void)a7;
+}
+
+void testUpcast(B& value) {
+  A& a8 = llvm::cast<A>(value);
+  // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: redundant use of 'cast' 
[llvm-redundant-casting]
+  // CHECK-MESSAGES: :[[@LINE-2]]:25: note: source expression has type 'B', 
which is a subtype of 'A'
+  // CHECK-FIXES: A& a8 = value;
+  (void)a8;
+}
+
+void testDowncast(A& value) {
+  B& a9 = llvm::cast<B>(value);
+  // CHECK-MESSAGES-NOT: warning
+  // CHECK-FIXES-NOT: A& a9 = value;
+  (void)a9;
+}
+
+struct C : B {};
+
+void testUpcastTransitive(C& value) {
+  A& a10 = llvm::cast<A>(value);
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: redundant use of 'cast' 
[llvm-redundant-casting]
+  // CHECK-MESSAGES: :[[@LINE-2]]:26: note: source expression has type 'C', 
which is a subtype of 'A'
+  // CHECK-FIXES: A& a10 = value;
+  (void)a10;
+}
+
+namespace llvm {
+void testCastInLLVM(A& value) {
+  A& a11 = cast<A>(value);
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: redundant use of 'cast' 
[llvm-redundant-casting]
+  // CHECK-MESSAGES: :[[@LINE-2]]:20: note: source expression has type 'A'
+  // CHECK-FIXES: A& a11 = value;
+  (void)a11;
+}
+} // namespace llvm
+
+void testCastPointer(A* value) {
+  A *a12 = llvm::cast<A>(value);
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: redundant use of 'cast' 
[llvm-redundant-casting]
+  // CHECK-MESSAGES: :[[@LINE-2]]:26: note: source expression has pointee type 
'A'
+  // CHECK-FIXES: A *a12 = value;
+  (void)a12;
+}
+
+template <typename T>
+void testCastTemplateIgnore(T* value) {
+  A *a13 = llvm::cast<A>(value);
+  (void)a13;
+}
+template void testCastTemplateIgnore<A>(A *value);
+
+template <typename T>
+struct testCastTemplateIgnoreStruct {
+  void method(T* value) {
+    A *a14 = llvm::cast<A>(value);
+    (void)a14;
+  }
+};
+
+void call(testCastTemplateIgnoreStruct<A> s, A *a) {
+  s.method(a);
+}
+
+template <typename T>
+void testCastTemplateTrigger1(T* value) {
+  T *a15 = llvm::cast<T>(value);
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: redundant use of 'cast' 
[llvm-redundant-casting]
+  // CHECK-MESSAGES: :[[@LINE-2]]:26: note: source expression has pointee type 
'T'
+  // CHECK-FIXES: T *a15 = value;
+  (void)a15;
+}
+
+template <typename T>
+void testCastTemplateTrigger2(A* value, T other) {
+  A *a16 = llvm::cast<A>(value);
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: redundant use of 'cast' 
[llvm-redundant-casting]
+  // CHECK-MESSAGES: :[[@LINE-2]]:26: note: source expression has pointee type 
'A'
+  // CHECK-FIXES: A *a16 = value;
+  (void)a16; (void) other;
+}
+
+void testCastConst(const A& value) {
+  const A& a17 = llvm::cast<A>(value);
+  // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: redundant use of 'cast' 
[llvm-redundant-casting]
+  // CHECK-MESSAGES: :[[@LINE-2]]:32: note: source expression has type 'A'
+  // CHECK-FIXES: const A& a17 = value;
+  (void)a17;
+}
+
+void testCastConstPointer(const A* value) {
+  const A* a18 = llvm::cast<A>(value);
+  // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: redundant use of 'cast' 
[llvm-redundant-casting]
+  // CHECK-MESSAGES: :[[@LINE-2]]:32: note: source expression has pointee type 
'A'
+  // CHECK-FIXES: const A* a18 = value;
+  (void)a18;
+}
+
+void testCastExplicitDowncastImplicitUpcast(const A* value) {
+  const A* a19 = llvm::cast<C>(value);
+  (void)a19;
+}
+
+struct D : A {};
+struct E {};
+struct F : D, E {};
+
+void testCastUpcastMultipleInheritance(F& value) {
+  E& a20 = llvm::cast<E>(value);
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: redundant use of 'cast' 
[llvm-redundant-casting]
+  // CHECK-MESSAGES: :[[@LINE-2]]:26: note: source expression has type 'F', 
which is a subtype of 'E'
+  // CHECK-FIXES: E& a20 = value;
+  (void)a20;
+}
+
+struct G : D, C {};
+
+void testCastUpcastDiamondExplicit(G& value) {
+  A& a21 = llvm::cast<A>(llvm::cast<C>(value));
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: redundant use of 'cast' 
[llvm-redundant-casting]
+  // CHECK-MESSAGES: :[[@LINE-2]]:26: note: source expression has type 'C', 
which is a subtype of 'A'
+  // CHECK-FIXES: A& a21 = llvm::cast<C>(value);
+  (void)a21;
+}
+
+void testCastUpcastDiamondImplicit(G& value) {
+  A& a22 = llvm::cast<C>(value);
+  (void)a22;
+}
+
+void testCastUpcastDiamondSingleSide(G& value) {
+  C& a23 = llvm::cast<C>(value);
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: redundant use of 'cast' 
[llvm-redundant-casting]
+  // CHECK-MESSAGES: :[[@LINE-2]]:26: note: source expression has type 'G', 
which is a subtype of 'C'
+  // CHECK-FIXES: C& a23 = value;
+  (void)a23;
+}
+
+struct H : virtual A {};
+struct I : virtual A {};
+struct J : H, I {};
+
+void testCastUpcastDiamondVirtual(J& value) {
+  A& a24 = llvm::cast<I>(value);
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: redundant use of 'cast' 
[llvm-redundant-casting]
+  // CHECK-MESSAGES: :[[@LINE-2]]:26: note: source expression has type 'J', 
which is a subtype of 'I'
+  // CHECK-FIXES: A& a24 = value;
+  (void)a24;
+}
+
+template<typename T>
+struct K {};
+
+struct L : K<L> {};
+
+void testCastCRTPUpcast(L& value) {
+  K<L>& a24 = llvm::cast<K<L>>(value);
+  // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: redundant use of 'cast' 
[llvm-redundant-casting]
+  // CHECK-MESSAGES: :[[@LINE-2]]:32: note: source expression has type 'L', 
which is a subtype of 'K<L>'
+  // CHECK-FIXES: K<L>& a24 = value;
+  (void)a24;
+}
+
+CAST_FUNCTION(cast)
+CAST_FUNCTION(dyn_cast)
+
+void testCastNonLLVM(A& value) {
+  A& a25 = cast<A>(value);
+  (void)a25;
+}
+
+void testDynCastNonLLVM(A& value) {
+  A& a26 = dyn_cast<A>(value);
+  (void)a26;
+}
+
+template<typename T>
+void testCastNonLLVMUnresolved(T& value) {
+  T& a27 = cast<T>(value);
+  (void)a27;
+}
+
+namespace llvm {
+namespace magic {
+template<typename T>
+void testCastImplicitlyLLVMUnresolved(T& value) {
+  T& a28 = cast<T>(value);
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: redundant use of 'cast' 
[llvm-redundant-casting]
+  // CHECK-MESSAGES: :[[@LINE-2]]:20: note: source expression has type 'T'
+  // CHECK-FIXES: T& a28 = value;
+  (void)a28;
+}
+} // namespace magic
+} // namespace llvm
+
+// FIXME: this cast is redundant since it's immediately undone by the implicit 
cast
+void testCastUpdown(A& value) {
+  A& a29 = cast<C>(value);
+  (void)a29;
+}

diff  --git a/clang/include/clang/ASTMatchers/ASTMatchers.h 
b/clang/include/clang/ASTMatchers/ASTMatchers.h
index e8e7643e0dddd..09232ee463b51 100644
--- a/clang/include/clang/ASTMatchers/ASTMatchers.h
+++ b/clang/include/clang/ASTMatchers/ASTMatchers.h
@@ -7043,9 +7043,9 @@ extern const internal::VariadicDynCastAllOfMatcher<
     templateSpecializationTypeLoc;
 
 /// Matches template specialization `TypeLoc`s, class template specializations,
-/// variable template specializations, and function template specializations
-/// that have at least one `TemplateArgumentLoc` matching the given
-/// `InnerMatcher`.
+/// variable template specializations, unresolved overloads, and function
+/// template specializations that have at least one `TemplateArgumentLoc`
+/// matching the given `InnerMatcher`.
 ///
 /// Given
 /// \code
@@ -7059,7 +7059,8 @@ AST_POLYMORPHIC_MATCHER_P(
     hasAnyTemplateArgumentLoc,
     AST_POLYMORPHIC_SUPPORTED_TYPES(ClassTemplateSpecializationDecl,
                                     VarTemplateSpecializationDecl, 
FunctionDecl,
-                                    DeclRefExpr, 
TemplateSpecializationTypeLoc),
+                                    DeclRefExpr, TemplateSpecializationTypeLoc,
+                                    OverloadExpr),
     internal::Matcher<TemplateArgumentLoc>, InnerMatcher) {
   auto Args = internal::getTemplateArgsWritten(Node);
   return matchesFirstInRange(InnerMatcher, Args.begin(), Args.end(), Finder,
@@ -7068,8 +7069,9 @@ AST_POLYMORPHIC_MATCHER_P(
 }
 
 /// Matches template specialization `TypeLoc`s, class template specializations,
-/// variable template specializations, and function template specializations
-/// where the n'th `TemplateArgumentLoc` matches the given `InnerMatcher`.
+/// variable template specializations, unresolved overloads, and function
+/// template specializations where the n'th `TemplateArgumentLoc` matches the
+/// given `InnerMatcher`.
 ///
 /// Given
 /// \code
@@ -7084,13 +7086,37 @@ AST_POLYMORPHIC_MATCHER_P2(
     hasTemplateArgumentLoc,
     AST_POLYMORPHIC_SUPPORTED_TYPES(ClassTemplateSpecializationDecl,
                                     VarTemplateSpecializationDecl, 
FunctionDecl,
-                                    DeclRefExpr, 
TemplateSpecializationTypeLoc),
+                                    DeclRefExpr, TemplateSpecializationTypeLoc,
+                                    OverloadExpr),
     unsigned, Index, internal::Matcher<TemplateArgumentLoc>, InnerMatcher) {
   auto Args = internal::getTemplateArgsWritten(Node);
   return Index < Args.size() &&
          InnerMatcher.matches(Args[Index], Finder, Builder);
 }
 
+/// Matches template specialization `TypeLoc`s, class template specializations,
+/// variable template specializations, unresolved overloads, and function
+/// template specializations that have exactly `MatchCount` number of
+/// `TemplateArgumentLoc`s.
+///
+/// Given
+/// \code
+///   template<typename T> class A {};
+///   A<int> a;
+/// \endcode
+/// 
varDecl(hasTypeLoc(templateSpecializationTypeLoc(templateArgumentLocCountIs(1))))
+///   matches `A<int> a`.
+AST_POLYMORPHIC_MATCHER_P(
+    templateArgumentLocCountIs,
+    AST_POLYMORPHIC_SUPPORTED_TYPES(ClassTemplateSpecializationDecl,
+                                    VarTemplateSpecializationDecl, 
FunctionDecl,
+                                    DeclRefExpr, TemplateSpecializationTypeLoc,
+                                    OverloadExpr),
+    unsigned, MatchCount) {
+  unsigned Count = internal::getNumTemplateArgsWritten(Node);
+  return Count == MatchCount;
+}
+
 /// Matches type \c bool.
 ///
 /// Given

diff  --git a/clang/include/clang/ASTMatchers/ASTMatchersInternal.h 
b/clang/include/clang/ASTMatchers/ASTMatchersInternal.h
index c050fb7d797e3..cb12d10d628b6 100644
--- a/clang/include/clang/ASTMatchers/ASTMatchersInternal.h
+++ b/clang/include/clang/ASTMatchers/ASTMatchersInternal.h
@@ -1986,6 +1986,46 @@ getTemplateArgsWritten(const 
TemplateSpecializationTypeLoc &T) {
   return Args;
 }
 
+inline ArrayRef<TemplateArgumentLoc>
+getTemplateArgsWritten(const OverloadExpr &OE) {
+  return OE.template_arguments();
+}
+
+inline unsigned
+getNumTemplateArgsWritten(const ClassTemplateSpecializationDecl &D) {
+  if (const ASTTemplateArgumentListInfo *Args = D.getTemplateArgsAsWritten())
+    return Args->getNumTemplateArgs();
+  return 0;
+}
+
+inline unsigned
+getNumTemplateArgsWritten(const VarTemplateSpecializationDecl &D) {
+  if (const ASTTemplateArgumentListInfo *Args = D.getTemplateArgsAsWritten())
+    return Args->getNumTemplateArgs();
+  return 0;
+}
+
+inline unsigned getNumTemplateArgsWritten(const FunctionDecl &FD) {
+  if (const auto *Args = FD.getTemplateSpecializationArgsAsWritten())
+    return Args->getNumTemplateArgs();
+  return 0;
+}
+
+inline unsigned getNumTemplateArgsWritten(const DeclRefExpr &DRE) {
+  return DRE.getNumTemplateArgs();
+}
+
+inline unsigned
+getNumTemplateArgsWritten(const TemplateSpecializationTypeLoc &T) {
+  if (!T.isNull())
+    return T.getNumArgs();
+  return 0;
+}
+
+inline unsigned getNumTemplateArgsWritten(const OverloadExpr &OE) {
+  return OE.getNumTemplateArgs();
+}
+
 struct NotEqualsBoundNodePredicate {
   bool operator()(const internal::BoundNodesMap &Nodes) const {
     return Nodes.getNode(ID) != Node;


        
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to