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" <[email protected]> Date: Sun, 29 Mar 2026 10:39:21 -0700 Subject: [PATCH 1/9] [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" <[email protected]> Date: Sun, 29 Mar 2026 11:52:24 -0700 Subject: [PATCH 2/9] 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" <[email protected]> Date: Sun, 29 Mar 2026 13:54:31 -0700 Subject: [PATCH 3/9] Apply suggestions from code review Co-authored-by: Victor Chernyakin <[email protected]> --- .../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" <[email protected]> Date: Sun, 29 Mar 2026 15:09:50 -0700 Subject: [PATCH 4/9] 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" <[email protected]> Date: Sun, 29 Mar 2026 15:13:07 -0700 Subject: [PATCH 5/9] 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" <[email protected]> Date: Sun, 29 Mar 2026 15:14:46 -0700 Subject: [PATCH 6/9] 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" <[email protected]> Date: Sun, 29 Mar 2026 15:32:10 -0700 Subject: [PATCH 7/9] 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" <[email protected]> Date: Sun, 29 Mar 2026 16:43:07 -0700 Subject: [PATCH 8/9] 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" <[email protected]> Date: Sun, 29 Mar 2026 16:43:49 -0700 Subject: [PATCH 9/9] 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) { _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
