[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 <chernyakin.victor.j@outlook.com>
This commit is contained in:
Henrik G. Olsson
2026-04-08 09:54:04 -07:00
committed by GitHub
parent 815edc3ff6
commit 430e1be254
10 changed files with 625 additions and 7 deletions

View File

@@ -10,6 +10,7 @@ add_clang_library(clangTidyLLVMModule STATIC
PreferIsaOrDynCastInConditionalsCheck.cpp
PreferRegisterOverUnsignedCheck.cpp
PreferStaticOverAnonymousNamespaceCheck.cpp
RedundantCastingCheck.cpp
TwineLocalCheck.cpp
TypeSwitchCaseTypesCheck.cpp
UseNewMLIROpBuilderCheck.cpp

View File

@@ -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 @@ public:
"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");

View File

@@ -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

View File

@@ -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

View File

@@ -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.

View File

@@ -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"

View File

@@ -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``

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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;