[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:
@@ -10,6 +10,7 @@ add_clang_library(clangTidyLLVMModule STATIC
|
||||
PreferIsaOrDynCastInConditionalsCheck.cpp
|
||||
PreferRegisterOverUnsignedCheck.cpp
|
||||
PreferStaticOverAnonymousNamespaceCheck.cpp
|
||||
RedundantCastingCheck.cpp
|
||||
TwineLocalCheck.cpp
|
||||
TypeSwitchCaseTypesCheck.cpp
|
||||
UseNewMLIROpBuilderCheck.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 @@ 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");
|
||||
|
||||
183
clang-tools-extra/clang-tidy/llvm/RedundantCastingCheck.cpp
Normal file
183
clang-tools-extra/clang-tidy/llvm/RedundantCastingCheck.cpp
Normal 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
|
||||
39
clang-tools-extra/clang-tidy/llvm/RedundantCastingCheck.h
Normal file
39
clang-tools-extra/clang-tidy/llvm/RedundantCastingCheck.h
Normal 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
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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``
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user