[clang-tools-extra] [clang-tidy] detect redundant uses of LLVM's cast, dyn_cast (PR #189274)
Henrik G. Olsson via cfe-commits
cfe-commits at lists.llvm.org
Sun Mar 29 17:25:55 PDT 2026
https://github.com/hnrklssn updated https://github.com/llvm/llvm-project/pull/189274
>From 3c91a0e1744ce531af38404ffa7c20409937d79b Mon Sep 17 00:00:00 2001
From: "Henrik G. Olsson" <h_olsson at apple.com>
Date: Sun, 29 Mar 2026 10:39:21 -0700
Subject: [PATCH 01/11] [clang-tidy] detect redundant uses of LLVM's cast,
dyn_cast
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
---
.../clang-tidy/llvm/CMakeLists.txt | 1 +
.../clang-tidy/llvm/LLVMTidyModule.cpp | 3 +
.../clang-tidy/llvm/RedundantCastingCheck.cpp | 131 ++++++++++++++
.../clang-tidy/llvm/RedundantCastingCheck.h | 33 ++++
clang-tools-extra/docs/ReleaseNotes.rst | 5 +
.../docs/clang-tidy/checks/list.rst | 1 +
.../checks/llvm/redundant-casting.rst | 24 +++
.../checkers/llvm/redundant-casting.cpp | 163 ++++++++++++++++++
8 files changed, 361 insertions(+)
create mode 100644 clang-tools-extra/clang-tidy/llvm/RedundantCastingCheck.cpp
create mode 100644 clang-tools-extra/clang-tidy/llvm/RedundantCastingCheck.h
create mode 100644 clang-tools-extra/docs/clang-tidy/checks/llvm/redundant-casting.rst
create mode 100644 clang-tools-extra/test/clang-tidy/checkers/llvm/redundant-casting.cpp
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..a29701cc012b6
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/llvm/RedundantCastingCheck.cpp
@@ -0,0 +1,131 @@
+//===----------------------------------------------------------------------===//
+//
+// 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/Expr.h"
+#include "clang/AST/ExprCXX.h"
+#include "clang/AST/TemplateBase.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 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 AnyCalleeName = [](ArrayRef<StringRef> CalleeName) {
+ return allOf(unless(isMacroID()), unless(cxxMemberCallExpr()),
+ callee(expr(ignoringImpCasts(
+ declRefExpr(to(namedDecl(hasAnyName(CalleeName))),
+ hasAnyTemplateArgumentLoc(anything()))
+ .bind("callee")))));
+ };
+ auto AnyCalleeNameInUninstantiatedTemplate =
+ [](ArrayRef<StringRef> CalleeName) {
+ return allOf(unless(isMacroID()), unless(cxxMemberCallExpr()),
+ callee(expr(ignoringImpCasts(
+ unresolvedLookupExpr(hasAnyUnresolvedName(CalleeName))
+ .bind("callee")))));
+ };
+ Finder->addMatcher(
+ callExpr(
+ AnyCalleeName(FunctionNames),
+ hasAncestor(functionDecl().bind("context")))
+ .bind("call"),
+ this);
+ Finder->addMatcher(
+ callExpr(AnyCalleeNameInUninstantiatedTemplate(FunctionNames))
+ .bind("call"),
+ this);
+}
+
+static QualType stripPointerOrReference(QualType Ty) {
+ QualType Pointee = Ty->getPointeeType();
+ if (Pointee.isNull())
+ return Ty;
+ return Pointee;
+}
+
+void RedundantCastingCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto &Nodes = Result.Nodes;
+ const auto *Call = Nodes.getNodeAs<CallExpr>("call");
+ if (Call->getNumArgs() != 1)
+ return;
+
+ CanQualType RetTy;
+ std::string FuncName;
+ if (const auto *ResolvedCallee = Nodes.getNodeAs<DeclRefExpr>("callee")) {
+ const auto *F = dyn_cast<FunctionDecl>(ResolvedCallee->getDecl());
+ const auto *SurroundingFunc = Nodes.getNodeAs<FunctionDecl>("context");
+ // Casts can be redundant for some instantiations but not others.
+ // Only emit warnings in templates in the uninstantated versions.
+ if (SurroundingFunc->isTemplateInstantiation())
+ return;
+
+ RetTy = stripPointerOrReference(F->getReturnType())
+ ->getCanonicalTypeUnqualified();
+ FuncName = F->getName();
+ } else if (const auto *UnresolvedCallee =
+ Nodes.getNodeAs<UnresolvedLookupExpr>("callee")) {
+ if (UnresolvedCallee->getNumTemplateArgs() != 1)
+ return;
+ auto TArg = UnresolvedCallee->template_arguments()[0].getArgument();
+ if (TArg.getKind() != TemplateArgument::Type)
+ return;
+
+ RetTy = TArg.getAsType()->getCanonicalTypeUnqualified();
+ FuncName = UnresolvedCallee->getName().getAsString();
+ } else {
+ return;
+ }
+
+ const auto *Arg = Call->getArg(0);
+ const CanQualType FromTy =
+ stripPointerOrReference(Arg->getType())->getCanonicalTypeUnqualified();
+ const auto *FromDecl = FromTy->getAsCXXRecordDecl();
+ const auto *RetDecl = RetTy->getAsCXXRecordDecl();
+ const bool IsDerived = FromDecl && RetDecl && FromDecl->isDerivedFrom(RetDecl);
+ if (FromTy != RetTy && !IsDerived)
+ 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);
+ diag(Arg->getExprLoc(), "source expression %0 has type %1",
+ DiagnosticIDs::Note)
+ << Arg << Arg->IgnoreParenImpCasts()->getType();
+
+ if (FromTy != RetTy) {
+ diag(Arg->getExprLoc(), "%0 is a subtype of %1", DiagnosticIDs::Note)
+ << FromTy << 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..3243616976014
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/llvm/RedundantCastingCheck.h
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+// 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;
+ }
+};
+
+} // 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 eb735e6e62ee4..43d67e0831fd9 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -114,6 +114,11 @@ 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.
+
+ Detect redundant uses of LLVM's cast functions.
+
- 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 ceab1e9414951..03ed10217fe45 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -251,6 +251,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..c09bc40dd207e
--- /dev/null
+++ b/clang-tools-extra/docs/clang-tidy/checks/llvm/redundant-casting.rst
@@ -0,0 +1,24 @@
+.. 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;
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..4027933f95c4b
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/llvm/redundant-casting.cpp
@@ -0,0 +1,163 @@
+// RUN: %check_clang_tidy -std=c++17 %s llvm-redundant-casting %t
+
+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 'value' 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 'value' 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 'value' 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 'value' 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 'value' 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 'value' 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 '(getA())' 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 'value' has type 'B'
+ // CHECK-MESSAGES: :[[@LINE-3]]:25: note: 'B' 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;
+}
+
+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 'value' 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 'value' has type 'A *'
+ // CHECK-FIXES: A *a12 = value;
+ (void)a12;
+}
+
+template <typename T>
+void testCastTemplateIgnore(T* value) {
+ A *a13 = llvm::cast<A>(value);
+ // CHECK-MESSAGES-NOT: warning
+ // CHECK-FIXES-NOT: A *a13 = value;
+ (void)a13;
+}
+template void testCastTemplateIgnore<A>(A *value);
+
+template <typename T>
+struct testCastTemplateIgnoreStruct {
+ void method(T* value) {
+ A *a14 = llvm::cast<A>(value);
+ // CHECK-MESSAGES-NOT: warning
+ // CHECK-FIXES-NOT: A *a14 = 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 'value' has 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 'value' has type 'A *'
+ // CHECK-FIXES: A *a16 = value;
+ (void)a16; (void) other;
+}
+
>From 2f63c0e1dfd8a163858695c1f6cdec1002786a06 Mon Sep 17 00:00:00 2001
From: "Henrik G. Olsson" <h_olsson at apple.com>
Date: Sun, 29 Mar 2026 11:52:24 -0700
Subject: [PATCH 02/11] attempt to fix windows ci failure
---
.../test/clang-tidy/checkers/llvm/redundant-casting.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
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
index 4027933f95c4b..aad3d6a701fa4 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/llvm/redundant-casting.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/llvm/redundant-casting.cpp
@@ -1,4 +1,4 @@
-// RUN: %check_clang_tidy -std=c++17 %s llvm-redundant-casting %t
+// RUN: %check_clang_tidy -std=c++17 %s llvm-redundant-casting %t -- -- -fno-delayed-template-parsing
namespace llvm {
#define CAST_FUNCTION(name) \
>From 6102c8bea704ca8cdb0de0bcff08eaa371fc666f Mon Sep 17 00:00:00 2001
From: "Henrik G. Olsson" <hnrklssn at gmail.com>
Date: Sun, 29 Mar 2026 13:54:31 -0700
Subject: [PATCH 03/11] Apply suggestions from code review
Co-authored-by: Victor Chernyakin <chernyakin.victor.j at outlook.com>
---
.../clang-tidy/llvm/RedundantCastingCheck.cpp | 8 ++++----
.../test/clang-tidy/checkers/llvm/redundant-casting.cpp | 2 +-
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/llvm/RedundantCastingCheck.cpp b/clang-tools-extra/clang-tidy/llvm/RedundantCastingCheck.cpp
index a29701cc012b6..16c6914897e07 100644
--- a/clang-tools-extra/clang-tidy/llvm/RedundantCastingCheck.cpp
+++ b/clang-tools-extra/clang-tidy/llvm/RedundantCastingCheck.cpp
@@ -30,7 +30,7 @@ AST_MATCHER_P(OverloadExpr, hasAnyUnresolvedName, ArrayRef<StringRef>, Names) {
}
} // namespace
-static StringRef FunctionNames[] = {
+static constexpr StringRef FunctionNames[] = {
"cast", "cast_or_null", "cast_if_present",
"dyn_cast", "dyn_cast_or_null", "dyn_cast_if_present"};
@@ -98,7 +98,7 @@ void RedundantCastingCheck::check(const MatchFinder::MatchResult &Result) {
RetTy = TArg.getAsType()->getCanonicalTypeUnqualified();
FuncName = UnresolvedCallee->getName().getAsString();
} else {
- return;
+ llvm_unreachable("");
}
const auto *Arg = Call->getArg(0);
@@ -118,9 +118,9 @@ void RedundantCastingCheck::check(const MatchFinder::MatchResult &Result) {
diag(Call->getExprLoc(), "redundant use of '%0'")
<< FuncName
<< FixItHint::CreateReplacement(Call->getSourceRange(), ArgText);
- diag(Arg->getExprLoc(), "source expression %0 has type %1",
+ diag(Arg->getExprLoc(), "source expression has type %1",
DiagnosticIDs::Note)
- << Arg << Arg->IgnoreParenImpCasts()->getType();
+ << Arg->getSourceRange() << Arg->IgnoreParenImpCasts()->getType();
if (FromTy != RetTy) {
diag(Arg->getExprLoc(), "%0 is a subtype of %1", DiagnosticIDs::Note)
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
index aad3d6a701fa4..11445c982d8fc 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/llvm/redundant-casting.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/llvm/redundant-casting.cpp
@@ -1,4 +1,4 @@
-// RUN: %check_clang_tidy -std=c++17 %s llvm-redundant-casting %t -- -- -fno-delayed-template-parsing
+// RUN: %check_clang_tidy -std=c++17-or-later %s llvm-redundant-casting %t -- -- -fno-delayed-template-parsing
namespace llvm {
#define CAST_FUNCTION(name) \
>From 61e9ed12a0cc7b66934120b69564b295f921c3f8 Mon Sep 17 00:00:00 2001
From: "Henrik G. Olsson" <h_olsson at apple.com>
Date: Sun, 29 Mar 2026 15:09:50 -0700
Subject: [PATCH 04/11] update test
---
.../clang-tidy/llvm/RedundantCastingCheck.cpp | 2 +-
.../checkers/llvm/redundant-casting.cpp | 24 +++++++++----------
2 files changed, 13 insertions(+), 13 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/llvm/RedundantCastingCheck.cpp b/clang-tools-extra/clang-tidy/llvm/RedundantCastingCheck.cpp
index 16c6914897e07..2cd89639e28e7 100644
--- a/clang-tools-extra/clang-tidy/llvm/RedundantCastingCheck.cpp
+++ b/clang-tools-extra/clang-tidy/llvm/RedundantCastingCheck.cpp
@@ -118,7 +118,7 @@ void RedundantCastingCheck::check(const MatchFinder::MatchResult &Result) {
diag(Call->getExprLoc(), "redundant use of '%0'")
<< FuncName
<< FixItHint::CreateReplacement(Call->getSourceRange(), ArgText);
- diag(Arg->getExprLoc(), "source expression has type %1",
+ diag(Arg->getExprLoc(), "source expression has type %0",
DiagnosticIDs::Note)
<< Arg->getSourceRange() << Arg->IgnoreParenImpCasts()->getType();
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
index 11445c982d8fc..74f78243fc197 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/llvm/redundant-casting.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/llvm/redundant-casting.cpp
@@ -33,7 +33,7 @@ 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 'value' has type 'A'
+ // CHECK-MESSAGES: :[[@LINE-2]]:25: note: source expression has type 'A'
// CHECK-FIXES: A& a1 = value;
(void)a1;
}
@@ -41,7 +41,7 @@ void testCast(A& value) {
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 'value' has type 'A'
+ // CHECK-MESSAGES: :[[@LINE-2]]:29: note: source expression has type 'A'
// CHECK-FIXES: A& a2 = value;
(void)a2;
}
@@ -49,7 +49,7 @@ void testDynCast(A& value) {
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 'value' has type 'A'
+ // CHECK-MESSAGES: :[[@LINE-2]]:33: note: source expression has type 'A'
// CHECK-FIXES: A& a3 = value;
(void)a3;
}
@@ -57,7 +57,7 @@ void testCastOrNull(A& value) {
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 'value' has type 'A'
+ // CHECK-MESSAGES: :[[@LINE-2]]:36: note: source expression has type 'A'
// CHECK-FIXES: A& a4 = value;
(void)a4;
}
@@ -65,7 +65,7 @@ void testCastIfPresent(A& value) {
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 'value' has type 'A'
+ // CHECK-MESSAGES: :[[@LINE-2]]:37: note: source expression has type 'A'
// CHECK-FIXES: A& a5 = value;
(void)a5;
}
@@ -73,7 +73,7 @@ void testDynCastOrNull(A& value) {
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 'value' has type 'A'
+ // CHECK-MESSAGES: :[[@LINE-2]]:40: note: source expression has type 'A'
// CHECK-FIXES: A& a6 = value;
(void)a6;
}
@@ -81,7 +81,7 @@ void testDynCastIfPresent(A& value) {
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 '(getA())' has type 'A'
+ // CHECK-MESSAGES: :[[@LINE-2]]:25: note: source expression has type 'A'
// CHECK-FIXES: A& a7 = (getA());
(void)a7;
}
@@ -89,7 +89,7 @@ void testCastNonDeclRef() {
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 'value' has type 'B'
+ // CHECK-MESSAGES: :[[@LINE-2]]:25: note: source expression has type 'B'
// CHECK-MESSAGES: :[[@LINE-3]]:25: note: 'B' is a subtype of 'A'
// CHECK-FIXES: A& a8 = value;
(void)a8;
@@ -106,7 +106,7 @@ 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 'value' has type 'A'
+ // CHECK-MESSAGES: :[[@LINE-2]]:20: note: source expression has type 'A'
// CHECK-FIXES: A& a11 = value;
(void)a11;
}
@@ -115,7 +115,7 @@ void testCastInLLVM(A& value) {
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 'value' has type 'A *'
+ // CHECK-MESSAGES: :[[@LINE-2]]:26: note: source expression has type 'A *'
// CHECK-FIXES: A *a12 = value;
(void)a12;
}
@@ -147,7 +147,7 @@ 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 'value' has type 'T *'
+ // CHECK-MESSAGES: :[[@LINE-2]]:26: note: source expression has type 'T *'
// CHECK-FIXES: T *a15 = value;
(void)a15;
}
@@ -156,7 +156,7 @@ 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 'value' has type 'A *'
+ // CHECK-MESSAGES: :[[@LINE-2]]:26: note: source expression has type 'A *'
// CHECK-FIXES: A *a16 = value;
(void)a16; (void) other;
}
>From cde2f3fe8b28eb3d4fc13e4543af30267f6cceca Mon Sep 17 00:00:00 2001
From: "Henrik G. Olsson" <h_olsson at apple.com>
Date: Sun, 29 Mar 2026 15:13:07 -0700
Subject: [PATCH 05/11] simplify ignoring template instantiation
---
.../clang-tidy/llvm/RedundantCastingCheck.cpp | 15 ++-------------
.../clang-tidy/llvm/RedundantCastingCheck.h | 6 ++++++
2 files changed, 8 insertions(+), 13 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/llvm/RedundantCastingCheck.cpp b/clang-tools-extra/clang-tidy/llvm/RedundantCastingCheck.cpp
index 2cd89639e28e7..1a1c1cee823da 100644
--- a/clang-tools-extra/clang-tidy/llvm/RedundantCastingCheck.cpp
+++ b/clang-tools-extra/clang-tidy/llvm/RedundantCastingCheck.cpp
@@ -49,12 +49,7 @@ void RedundantCastingCheck::registerMatchers(MatchFinder *Finder) {
unresolvedLookupExpr(hasAnyUnresolvedName(CalleeName))
.bind("callee")))));
};
- Finder->addMatcher(
- callExpr(
- AnyCalleeName(FunctionNames),
- hasAncestor(functionDecl().bind("context")))
- .bind("call"),
- this);
+ Finder->addMatcher(callExpr(AnyCalleeName(FunctionNames)).bind("call"), this);
Finder->addMatcher(
callExpr(AnyCalleeNameInUninstantiatedTemplate(FunctionNames))
.bind("call"),
@@ -77,13 +72,7 @@ void RedundantCastingCheck::check(const MatchFinder::MatchResult &Result) {
CanQualType RetTy;
std::string FuncName;
if (const auto *ResolvedCallee = Nodes.getNodeAs<DeclRefExpr>("callee")) {
- const auto *F = dyn_cast<FunctionDecl>(ResolvedCallee->getDecl());
- const auto *SurroundingFunc = Nodes.getNodeAs<FunctionDecl>("context");
- // Casts can be redundant for some instantiations but not others.
- // Only emit warnings in templates in the uninstantated versions.
- if (SurroundingFunc->isTemplateInstantiation())
- return;
-
+ const auto *F = cast<FunctionDecl>(ResolvedCallee->getDecl());
RetTy = stripPointerOrReference(F->getReturnType())
->getCanonicalTypeUnqualified();
FuncName = F->getName();
diff --git a/clang-tools-extra/clang-tidy/llvm/RedundantCastingCheck.h b/clang-tools-extra/clang-tidy/llvm/RedundantCastingCheck.h
index 3243616976014..2ab7369c0358e 100644
--- a/clang-tools-extra/clang-tidy/llvm/RedundantCastingCheck.h
+++ b/clang-tools-extra/clang-tidy/llvm/RedundantCastingCheck.h
@@ -26,6 +26,12 @@ class RedundantCastingCheck : public ClangTidyCheck {
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
>From 5f38eed14aa30a1b3e3d88035e5168f7f671009c Mon Sep 17 00:00:00 2001
From: "Henrik G. Olsson" <h_olsson at apple.com>
Date: Sun, 29 Mar 2026 15:14:46 -0700
Subject: [PATCH 06/11] clang format
---
clang-tools-extra/clang-tidy/.clang-format | 4 ----
clang-tools-extra/clang-tidy/llvm/RedundantCastingCheck.cpp | 6 +++---
2 files changed, 3 insertions(+), 7 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/.clang-format b/clang-tools-extra/clang-tidy/.clang-format
index 1b342d3f71f71..99928eba2b7fe 100644
--- a/clang-tools-extra/clang-tidy/.clang-format
+++ b/clang-tools-extra/clang-tidy/.clang-format
@@ -1,9 +1,5 @@
BasedOnStyle: LLVM
InsertNewlineAtEOF: true
-KeepEmptyLines:
- AtEndOfFile: false
- AtStartOfBlock: false
- AtStartOfFile: false
LineEnding: LF
QualifierAlignment: Left
RemoveBracesLLVM: true
diff --git a/clang-tools-extra/clang-tidy/llvm/RedundantCastingCheck.cpp b/clang-tools-extra/clang-tidy/llvm/RedundantCastingCheck.cpp
index 1a1c1cee823da..75e414963c1c9 100644
--- a/clang-tools-extra/clang-tidy/llvm/RedundantCastingCheck.cpp
+++ b/clang-tools-extra/clang-tidy/llvm/RedundantCastingCheck.cpp
@@ -95,7 +95,8 @@ void RedundantCastingCheck::check(const MatchFinder::MatchResult &Result) {
stripPointerOrReference(Arg->getType())->getCanonicalTypeUnqualified();
const auto *FromDecl = FromTy->getAsCXXRecordDecl();
const auto *RetDecl = RetTy->getAsCXXRecordDecl();
- const bool IsDerived = FromDecl && RetDecl && FromDecl->isDerivedFrom(RetDecl);
+ const bool IsDerived =
+ FromDecl && RetDecl && FromDecl->isDerivedFrom(RetDecl);
if (FromTy != RetTy && !IsDerived)
return;
@@ -107,8 +108,7 @@ void RedundantCastingCheck::check(const MatchFinder::MatchResult &Result) {
diag(Call->getExprLoc(), "redundant use of '%0'")
<< FuncName
<< FixItHint::CreateReplacement(Call->getSourceRange(), ArgText);
- diag(Arg->getExprLoc(), "source expression has type %0",
- DiagnosticIDs::Note)
+ diag(Arg->getExprLoc(), "source expression has type %0", DiagnosticIDs::Note)
<< Arg->getSourceRange() << Arg->IgnoreParenImpCasts()->getType();
if (FromTy != RetTy) {
>From 702274001b1a78bc8efb6e5ef47d10b50b4dbefa Mon Sep 17 00:00:00 2001
From: "Henrik G. Olsson" <h_olsson at apple.com>
Date: Sun, 29 Mar 2026 15:32:10 -0700
Subject: [PATCH 07/11] add test cases with const
---
.../checkers/llvm/redundant-casting.cpp | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
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
index 74f78243fc197..cd07000eea666 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/llvm/redundant-casting.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/llvm/redundant-casting.cpp
@@ -161,3 +161,19 @@ void testCastTemplateTrigger2(A* value, T other) {
(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 'const 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 type 'const A *'
+ // CHECK-FIXES: const A* a18 = value;
+ (void)a18;
+}
+
>From 801ca54c8bad9c2b510ea510055934496c67b4e3 Mon Sep 17 00:00:00 2001
From: "Henrik G. Olsson" <h_olsson at apple.com>
Date: Sun, 29 Mar 2026 16:43:07 -0700
Subject: [PATCH 08/11] revert unintended change to clang-format config
---
clang-tools-extra/clang-tidy/.clang-format | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/clang-tools-extra/clang-tidy/.clang-format b/clang-tools-extra/clang-tidy/.clang-format
index 99928eba2b7fe..1b342d3f71f71 100644
--- a/clang-tools-extra/clang-tidy/.clang-format
+++ b/clang-tools-extra/clang-tidy/.clang-format
@@ -1,5 +1,9 @@
BasedOnStyle: LLVM
InsertNewlineAtEOF: true
+KeepEmptyLines:
+ AtEndOfFile: false
+ AtStartOfBlock: false
+ AtStartOfFile: false
LineEnding: LF
QualifierAlignment: Left
RemoveBracesLLVM: true
>From f5f8e973cbca599f1d5c3007999b3107cacce873 Mon Sep 17 00:00:00 2001
From: "Henrik G. Olsson" <h_olsson at apple.com>
Date: Sun, 29 Mar 2026 16:43:49 -0700
Subject: [PATCH 09/11] remove lambdas with a single caller
---
.../clang-tidy/llvm/RedundantCastingCheck.cpp | 29 ++++++++-----------
1 file changed, 12 insertions(+), 17 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/llvm/RedundantCastingCheck.cpp b/clang-tools-extra/clang-tidy/llvm/RedundantCastingCheck.cpp
index 75e414963c1c9..2a869e6b9b0f5 100644
--- a/clang-tools-extra/clang-tidy/llvm/RedundantCastingCheck.cpp
+++ b/clang-tools-extra/clang-tidy/llvm/RedundantCastingCheck.cpp
@@ -35,25 +35,20 @@ static constexpr StringRef FunctionNames[] = {
"dyn_cast", "dyn_cast_or_null", "dyn_cast_if_present"};
void RedundantCastingCheck::registerMatchers(MatchFinder *Finder) {
- auto AnyCalleeName = [](ArrayRef<StringRef> CalleeName) {
- return allOf(unless(isMacroID()), unless(cxxMemberCallExpr()),
- callee(expr(ignoringImpCasts(
- declRefExpr(to(namedDecl(hasAnyName(CalleeName))),
- hasAnyTemplateArgumentLoc(anything()))
- .bind("callee")))));
- };
+ auto AnyCalleeName =
+ allOf(unless(isMacroID()), unless(cxxMemberCallExpr()),
+ callee(expr(ignoringImpCasts(
+ declRefExpr(to(namedDecl(hasAnyName(FunctionNames))),
+ hasAnyTemplateArgumentLoc(anything()))
+ .bind("callee")))));
auto AnyCalleeNameInUninstantiatedTemplate =
- [](ArrayRef<StringRef> CalleeName) {
- return allOf(unless(isMacroID()), unless(cxxMemberCallExpr()),
- callee(expr(ignoringImpCasts(
- unresolvedLookupExpr(hasAnyUnresolvedName(CalleeName))
- .bind("callee")))));
- };
- Finder->addMatcher(callExpr(AnyCalleeName(FunctionNames)).bind("call"), this);
+ allOf(unless(isMacroID()), unless(cxxMemberCallExpr()),
+ callee(expr(ignoringImpCasts(
+ unresolvedLookupExpr(hasAnyUnresolvedName(FunctionNames))
+ .bind("callee")))));
+ Finder->addMatcher(callExpr(AnyCalleeName).bind("call"), this);
Finder->addMatcher(
- callExpr(AnyCalleeNameInUninstantiatedTemplate(FunctionNames))
- .bind("call"),
- this);
+ callExpr(AnyCalleeNameInUninstantiatedTemplate).bind("call"), this);
}
static QualType stripPointerOrReference(QualType Ty) {
>From d2489890b5a8d1d947744e63a5d7c396de677b41 Mon Sep 17 00:00:00 2001
From: "Henrik G. Olsson" <h_olsson at apple.com>
Date: Sun, 29 Mar 2026 17:19:30 -0700
Subject: [PATCH 10/11] simplify note diagnostic
---
.../clang-tidy/llvm/RedundantCastingCheck.cpp | 22 +++++++++++--------
.../checkers/llvm/redundant-casting.cpp | 13 +++++------
2 files changed, 19 insertions(+), 16 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/llvm/RedundantCastingCheck.cpp b/clang-tools-extra/clang-tidy/llvm/RedundantCastingCheck.cpp
index 2a869e6b9b0f5..5b01960fe181e 100644
--- a/clang-tools-extra/clang-tidy/llvm/RedundantCastingCheck.cpp
+++ b/clang-tools-extra/clang-tidy/llvm/RedundantCastingCheck.cpp
@@ -10,6 +10,7 @@
#include "clang/AST/Expr.h"
#include "clang/AST/ExprCXX.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"
@@ -86,8 +87,9 @@ void RedundantCastingCheck::check(const MatchFinder::MatchResult &Result) {
}
const auto *Arg = Call->getArg(0);
- const CanQualType FromTy =
- stripPointerOrReference(Arg->getType())->getCanonicalTypeUnqualified();
+ QualType ArgTy = Arg->getType();
+ QualType ArgPointeeTy = stripPointerOrReference(ArgTy);
+ const CanQualType FromTy = ArgPointeeTy->getCanonicalTypeUnqualified();
const auto *FromDecl = FromTy->getAsCXXRecordDecl();
const auto *RetDecl = RetTy->getAsCXXRecordDecl();
const bool IsDerived =
@@ -103,13 +105,15 @@ void RedundantCastingCheck::check(const MatchFinder::MatchResult &Result) {
diag(Call->getExprLoc(), "redundant use of '%0'")
<< FuncName
<< FixItHint::CreateReplacement(Call->getSourceRange(), ArgText);
- diag(Arg->getExprLoc(), "source expression has type %0", DiagnosticIDs::Note)
- << Arg->getSourceRange() << Arg->IgnoreParenImpCasts()->getType();
-
- if (FromTy != RetTy) {
- diag(Arg->getExprLoc(), "%0 is a subtype of %1", DiagnosticIDs::Note)
- << FromTy << RetTy;
- }
+ // printing the canonical type for a template parameter prints as e.g.
+ // 'type-parameter-0-0'
+ 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/test/clang-tidy/checkers/llvm/redundant-casting.cpp b/clang-tools-extra/test/clang-tidy/checkers/llvm/redundant-casting.cpp
index cd07000eea666..7ae036b7398ac 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/llvm/redundant-casting.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/llvm/redundant-casting.cpp
@@ -89,8 +89,7 @@ void testCastNonDeclRef() {
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'
- // CHECK-MESSAGES: :[[@LINE-3]]:25: note: 'B' is a subtype of 'A'
+ // CHECK-MESSAGES: :[[@LINE-2]]:25: note: source expression has type 'B', which is a subtype of 'A'
// CHECK-FIXES: A& a8 = value;
(void)a8;
}
@@ -115,7 +114,7 @@ void testCastInLLVM(A& value) {
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 type 'A *'
+ // CHECK-MESSAGES: :[[@LINE-2]]:26: note: source expression has pointee type 'A'
// CHECK-FIXES: A *a12 = value;
(void)a12;
}
@@ -147,7 +146,7 @@ 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 type 'T *'
+ // CHECK-MESSAGES: :[[@LINE-2]]:26: note: source expression has pointee type 'T'
// CHECK-FIXES: T *a15 = value;
(void)a15;
}
@@ -156,7 +155,7 @@ 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 type 'A *'
+ // CHECK-MESSAGES: :[[@LINE-2]]:26: note: source expression has pointee type 'A'
// CHECK-FIXES: A *a16 = value;
(void)a16; (void) other;
}
@@ -164,7 +163,7 @@ void testCastTemplateTrigger2(A* value, T 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 'const A'
+ // CHECK-MESSAGES: :[[@LINE-2]]:32: note: source expression has type 'A'
// CHECK-FIXES: const A& a17 = value;
(void)a17;
}
@@ -172,7 +171,7 @@ void testCastConst(const A& value) {
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 type 'const A *'
+ // CHECK-MESSAGES: :[[@LINE-2]]:32: note: source expression has pointee type 'A'
// CHECK-FIXES: const A* a18 = value;
(void)a18;
}
>From 6e6828a4e769fc2956282f5e66078bce07074e3b Mon Sep 17 00:00:00 2001
From: "Henrik G. Olsson" <h_olsson at apple.com>
Date: Sun, 29 Mar 2026 17:25:34 -0700
Subject: [PATCH 11/11] const correctness
---
clang-tools-extra/clang-tidy/llvm/RedundantCastingCheck.cpp | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/llvm/RedundantCastingCheck.cpp b/clang-tools-extra/clang-tidy/llvm/RedundantCastingCheck.cpp
index 5b01960fe181e..1659be63b4629 100644
--- a/clang-tools-extra/clang-tidy/llvm/RedundantCastingCheck.cpp
+++ b/clang-tools-extra/clang-tidy/llvm/RedundantCastingCheck.cpp
@@ -87,8 +87,8 @@ void RedundantCastingCheck::check(const MatchFinder::MatchResult &Result) {
}
const auto *Arg = Call->getArg(0);
- QualType ArgTy = Arg->getType();
- QualType ArgPointeeTy = stripPointerOrReference(ArgTy);
+ 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();
@@ -107,7 +107,7 @@ void RedundantCastingCheck::check(const MatchFinder::MatchResult &Result) {
<< FixItHint::CreateReplacement(Call->getSourceRange(), ArgText);
// printing the canonical type for a template parameter prints as e.g.
// 'type-parameter-0-0'
- QualType DiagFromTy(ArgPointeeTy->getUnqualifiedDesugaredType(), 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",
More information about the cfe-commits
mailing list