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( "llvm-qualified-auto"); + CheckFactories.registerCheck( + "llvm-redundant-casting"); CheckFactories.registerCheck("llvm-twine-local"); CheckFactories.registerCheck( "llvm-type-switch-case-types"); diff --git a/clang-tools-extra/clang-tidy/llvm/RedundantCastingCheck.cpp b/clang-tools-extra/clang-tidy/llvm/RedundantCastingCheck.cpp new file mode 100644 index 0000000000000..8f8c152b5de86 --- /dev/null +++ b/clang-tools-extra/clang-tidy/llvm/RedundantCastingCheck.cpp @@ -0,0 +1,183 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "RedundantCastingCheck.h" +#include "clang/AST/ASTTypeTraits.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/Expr.h" +#include "clang/AST/ExprCXX.h" +#include "clang/AST/NestedNameSpecifierBase.h" +#include "clang/AST/ParentMapContext.h" +#include "clang/AST/TemplateBase.h" +#include "clang/AST/TypeBase.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/ASTMatchers/ASTMatchers.h" +#include "clang/Lex/Lexer.h" +#include "llvm/ADT/STLExtras.h" + +using namespace clang::ast_matchers; + +namespace clang::tidy::llvm_check { + +namespace { +AST_MATCHER(Expr, isMacroID) { return Node.getExprLoc().isMacroID(); } +AST_MATCHER_P(OverloadExpr, hasAnyUnresolvedName, ArrayRef, 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("call"); + + CanQualType RetTy; + std::string FuncName; + if (const auto *ResolvedCallee = Nodes.getNodeAs("callee")) { + const auto *F = cast(ResolvedCallee->getDecl()); + RetTy = stripPointerOrReference(F->getReturnType()) + ->getCanonicalTypeUnqualified(); + FuncName = F->getName(); + } else if (const auto *UnresolvedCallee = + Nodes.getNodeAs("callee")) { + const bool IsExplicitlyLLVM = + isLLVMNamespace(UnresolvedCallee->getQualifier()); + const auto *CallerNS = Nodes.getNodeAs("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("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()) { + 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(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(d)`. + if (IsDerivedFromParent && + Paths.isAmbiguous(ParentTy->getCanonicalTypeUnqualified())) + return; + } + } + + auto GetText = [&](SourceRange R) { + return Lexer::getSourceText(CharSourceRange::getTokenRange(R), + *Result.SourceManager, getLangOpts()); + }; + StringRef ArgText = GetText(Arg->getSourceRange()); + diag(Call->getExprLoc(), "redundant use of '%0'") + << FuncName + << FixItHint::CreateReplacement(Call->getSourceRange(), ArgText); + // printing the canonical type for a template parameter prints as e.g. + // 'type-parameter-0-0' + const QualType DiagFromTy(ArgPointeeTy->getUnqualifiedDesugaredType(), 0); + diag(Arg->getExprLoc(), + "source expression has%select{| pointee}0 type %1%select{|, which is a " + "subtype of %3}2", + DiagnosticIDs::Note) + << Arg->getSourceRange() << ArgTy->isPointerType() << DiagFromTy + << (FromTy != RetTy) << RetTy; +} + +} // namespace clang::tidy::llvm_check diff --git a/clang-tools-extra/clang-tidy/llvm/RedundantCastingCheck.h b/clang-tools-extra/clang-tidy/llvm/RedundantCastingCheck.h new file mode 100644 index 0000000000000..2ab7369c0358e --- /dev/null +++ b/clang-tools-extra/clang-tidy/llvm/RedundantCastingCheck.h @@ -0,0 +1,39 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_LLVM_REDUNDANTCASTINGCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_LLVM_REDUNDANTCASTINGCHECK_H + +#include "../ClangTidyCheck.h" + +namespace clang::tidy::llvm_check { + +/// Detect redundant uses of LLVM's cast and dyn_cast functions. +/// +/// For the user-facing documentation see: +/// https://clang.llvm.org/extra/clang-tidy/checks/llvm/redundant-casting.html +class RedundantCastingCheck : public ClangTidyCheck { +public: + RedundantCastingCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + bool isLanguageVersionSupported(const LangOptions &LangOpts) const override { + return LangOpts.CPlusPlus; + } + + std::optional getCheckTraversalKind() const override { + // Casts can be redundant for some instantiations but not others. + // Only emit warnings in templates in the uninstantated versions. + return TK_IgnoreUnlessSpelledInSource; + } +}; + +} // namespace clang::tidy::llvm_check + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_LLVM_REDUNDANTCASTINGCHECK_H diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst index eb735e6e62ee4..32db8e40e31ca 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -114,6 +114,13 @@ New checks Finds functions where throwing exceptions is unsafe but the function is still marked as potentially throwing. +- New :doc:`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 ` 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 `, "Yes" :doc:`llvm-prefer-register-over-unsigned `, "Yes" :doc:`llvm-prefer-static-over-anonymous-namespace `, + :doc:`llvm-redundant-casting `, "Yes" :doc:`llvm-twine-local `, "Yes" :doc:`llvm-type-switch-case-types `, "Yes" :doc:`llvm-use-new-mlir-op-builder `, "Yes" diff --git a/clang-tools-extra/docs/clang-tidy/checks/llvm/redundant-casting.rst b/clang-tools-extra/docs/clang-tidy/checks/llvm/redundant-casting.rst new file mode 100644 index 0000000000000..84596b26ee382 --- /dev/null +++ b/clang-tools-extra/docs/clang-tidy/checks/llvm/redundant-casting.rst @@ -0,0 +1,32 @@ +.. title:: clang-tidy - llvm-redundant-casting + +llvm-redundant-casting +====================== + +Points out uses of ``cast<>``, ``dyn_cast<>`` and their ``or_null`` variants +that are unnecessary because the argument already is of the target type, or a +derived type thereof. + +.. code-block:: c++ + + struct A {}; + A a; + // Finds: + A x = cast(a); + // replaced by: + A x = a; + + struct B : public A {}; + B b; + // Finds: + A y = cast(b); + // replaced by: + A y = b; + +Supported functions: + - ``llvm::cast`` + - ``llvm::cast_or_null`` + - ``llvm::cast_if_present`` + - ``llvm::dyn_cast`` + - ``llvm::dyn_cast_or_null`` + - ``llvm::dyn_cast_if_present`` diff --git a/clang-tools-extra/test/clang-tidy/checkers/llvm/redundant-casting.cpp b/clang-tools-extra/test/clang-tidy/checkers/llvm/redundant-casting.cpp new file mode 100644 index 0000000000000..933cbadc51701 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/llvm/redundant-casting.cpp @@ -0,0 +1,286 @@ +// RUN: %check_clang_tidy -std=c++17-or-later %s llvm-redundant-casting %t -- -- -fno-delayed-template-parsing + +namespace llvm { +#define CAST_FUNCTION(name) \ +template \ +[[nodiscard]] inline decltype(auto) name(const From &Val) { \ + return static_cast(Val); \ +} \ +template \ +[[nodiscard]] inline decltype(auto) name(From &Val) { \ + return static_cast(Val); \ +} \ +template \ +[[nodiscard]] inline decltype(auto) name(const From *Val) { \ + return static_cast(Val); \ +} \ +template \ +[[nodiscard]] inline decltype(auto) name(From *Val) { \ + return static_cast(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(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(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(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(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(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(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((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(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(value); + // CHECK-MESSAGES-NOT: warning + // CHECK-FIXES-NOT: A& a9 = value; + (void)a9; +} + +struct C : B {}; + +void testUpcastTransitive(C& value) { + A& a10 = llvm::cast(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(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(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 +void testCastTemplateIgnore(T* value) { + A *a13 = llvm::cast(value); + (void)a13; +} +template void testCastTemplateIgnore(A *value); + +template +struct testCastTemplateIgnoreStruct { + void method(T* value) { + A *a14 = llvm::cast(value); + (void)a14; + } +}; + +void call(testCastTemplateIgnoreStruct s, A *a) { + s.method(a); +} + +template +void testCastTemplateTrigger1(T* value) { + T *a15 = llvm::cast(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 +void testCastTemplateTrigger2(A* value, T other) { + A *a16 = llvm::cast(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(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(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(value); + (void)a19; +} + +struct D : A {}; +struct E {}; +struct F : D, E {}; + +void testCastUpcastMultipleInheritance(F& value) { + E& a20 = llvm::cast(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(llvm::cast(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(value); + (void)a21; +} + +void testCastUpcastDiamondImplicit(G& value) { + A& a22 = llvm::cast(value); + (void)a22; +} + +void testCastUpcastDiamondSingleSide(G& value) { + C& a23 = llvm::cast(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(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 +struct K {}; + +struct L : K {}; + +void testCastCRTPUpcast(L& value) { + K& a24 = llvm::cast>(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' + // CHECK-FIXES: K& a24 = value; + (void)a24; +} + +CAST_FUNCTION(cast) +CAST_FUNCTION(dyn_cast) + +void testCastNonLLVM(A& value) { + A& a25 = cast(value); + (void)a25; +} + +void testDynCastNonLLVM(A& value) { + A& a26 = dyn_cast(value); + (void)a26; +} + +template +void testCastNonLLVMUnresolved(T& value) { + T& a27 = cast(value); + (void)a27; +} + +namespace llvm { +namespace magic { +template +void testCastImplicitlyLLVMUnresolved(T& value) { + T& a28 = cast(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(value); + (void)a29; +} diff --git a/clang/include/clang/ASTMatchers/ASTMatchers.h b/clang/include/clang/ASTMatchers/ASTMatchers.h index e8e7643e0dddd..09232ee463b51 100644 --- a/clang/include/clang/ASTMatchers/ASTMatchers.h +++ b/clang/include/clang/ASTMatchers/ASTMatchers.h @@ -7043,9 +7043,9 @@ extern const internal::VariadicDynCastAllOfMatcher< templateSpecializationTypeLoc; /// Matches template specialization `TypeLoc`s, class template specializations, -/// variable template specializations, and function template specializations -/// that have at least one `TemplateArgumentLoc` matching the given -/// `InnerMatcher`. +/// variable template specializations, unresolved overloads, and function +/// template specializations that have at least one `TemplateArgumentLoc` +/// matching the given `InnerMatcher`. /// /// Given /// \code @@ -7059,7 +7059,8 @@ AST_POLYMORPHIC_MATCHER_P( hasAnyTemplateArgumentLoc, AST_POLYMORPHIC_SUPPORTED_TYPES(ClassTemplateSpecializationDecl, VarTemplateSpecializationDecl, FunctionDecl, - DeclRefExpr, TemplateSpecializationTypeLoc), + DeclRefExpr, TemplateSpecializationTypeLoc, + OverloadExpr), internal::Matcher, 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, 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 class A {}; +/// A a; +/// \endcode +/// varDecl(hasTypeLoc(templateSpecializationTypeLoc(templateArgumentLocCountIs(1)))) +/// matches `A a`. +AST_POLYMORPHIC_MATCHER_P( + templateArgumentLocCountIs, + AST_POLYMORPHIC_SUPPORTED_TYPES(ClassTemplateSpecializationDecl, + VarTemplateSpecializationDecl, FunctionDecl, + DeclRefExpr, TemplateSpecializationTypeLoc, + OverloadExpr), + unsigned, MatchCount) { + unsigned Count = internal::getNumTemplateArgsWritten(Node); + return Count == MatchCount; +} + /// Matches type \c bool. /// /// Given diff --git a/clang/include/clang/ASTMatchers/ASTMatchersInternal.h b/clang/include/clang/ASTMatchers/ASTMatchersInternal.h index c050fb7d797e3..cb12d10d628b6 100644 --- a/clang/include/clang/ASTMatchers/ASTMatchersInternal.h +++ b/clang/include/clang/ASTMatchers/ASTMatchersInternal.h @@ -1986,6 +1986,46 @@ getTemplateArgsWritten(const TemplateSpecializationTypeLoc &T) { return Args; } +inline ArrayRef +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;