mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-18 17:56:50 +00:00
[clang-tidy] Portability Template Virtual Member Function Check (#110099)
Introduced a new check that finds cases when an uninstantiated virtual member function in a template class causes cross-compiler incompatibility.
This commit is contained in:
parent
003375fb2b
commit
6d8e966512
@ -9,6 +9,7 @@ add_clang_library(clangTidyPortabilityModule STATIC
|
||||
RestrictSystemIncludesCheck.cpp
|
||||
SIMDIntrinsicsCheck.cpp
|
||||
StdAllocatorConstCheck.cpp
|
||||
TemplateVirtualMemberFunctionCheck.cpp
|
||||
|
||||
LINK_LIBS
|
||||
clangTidy
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "RestrictSystemIncludesCheck.h"
|
||||
#include "SIMDIntrinsicsCheck.h"
|
||||
#include "StdAllocatorConstCheck.h"
|
||||
#include "TemplateVirtualMemberFunctionCheck.h"
|
||||
|
||||
namespace clang::tidy {
|
||||
namespace portability {
|
||||
@ -25,6 +26,8 @@ public:
|
||||
"portability-simd-intrinsics");
|
||||
CheckFactories.registerCheck<StdAllocatorConstCheck>(
|
||||
"portability-std-allocator-const");
|
||||
CheckFactories.registerCheck<TemplateVirtualMemberFunctionCheck>(
|
||||
"portability-template-virtual-member-function");
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -0,0 +1,44 @@
|
||||
//===--- TemplateVirtualMemberFunctionCheck.cpp - clang-tidy --------------===//
|
||||
//
|
||||
// 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 "TemplateVirtualMemberFunctionCheck.h"
|
||||
#include "clang/ASTMatchers/ASTMatchFinder.h"
|
||||
|
||||
using namespace clang::ast_matchers;
|
||||
|
||||
namespace clang::tidy::portability {
|
||||
namespace {
|
||||
AST_MATCHER(CXXMethodDecl, isUsed) { return Node.isUsed(); }
|
||||
} // namespace
|
||||
|
||||
void TemplateVirtualMemberFunctionCheck::registerMatchers(MatchFinder *Finder) {
|
||||
Finder->addMatcher(
|
||||
cxxMethodDecl(ofClass(classTemplateSpecializationDecl(
|
||||
unless(isExplicitTemplateSpecialization()))
|
||||
.bind("specialization")),
|
||||
isVirtual(), unless(isUsed()),
|
||||
unless(cxxDestructorDecl(isDefaulted())))
|
||||
.bind("method"),
|
||||
this);
|
||||
}
|
||||
|
||||
void TemplateVirtualMemberFunctionCheck::check(
|
||||
const MatchFinder::MatchResult &Result) {
|
||||
const auto *ImplicitSpecialization =
|
||||
Result.Nodes.getNodeAs<ClassTemplateSpecializationDecl>("specialization");
|
||||
const auto *MethodDecl = Result.Nodes.getNodeAs<CXXMethodDecl>("method");
|
||||
|
||||
diag(MethodDecl->getLocation(),
|
||||
"unspecified virtual member function instantiation; the virtual "
|
||||
"member function is not instantiated but it might be with a "
|
||||
"different compiler");
|
||||
diag(ImplicitSpecialization->getPointOfInstantiation(),
|
||||
"template instantiated here", DiagnosticIDs::Note);
|
||||
}
|
||||
|
||||
} // namespace clang::tidy::portability
|
@ -0,0 +1,38 @@
|
||||
//===--- TemplateVirtualMemberFunctionCheck.h - clang-tidy ------*- C++ -*-===//
|
||||
//
|
||||
// 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_PORTABILITY_TEMPLATEVIRTUALMEMBERFUNCTIONCHECK_H
|
||||
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PORTABILITY_TEMPLATEVIRTUALMEMBERFUNCTIONCHECK_H
|
||||
|
||||
#include "../ClangTidyCheck.h"
|
||||
|
||||
namespace clang::tidy::portability {
|
||||
|
||||
/// Upon instantiating a template class, non-virtual member functions don't have
|
||||
/// to be instantiated unless they are used. Virtual member function
|
||||
/// instantiation on the other hand is unspecified and depends on the
|
||||
/// implementation of the compiler. This check intends to find cases when a
|
||||
/// virtual member function is not instantiated but it might be with a different
|
||||
/// compiler.
|
||||
///
|
||||
/// For the user-facing documentation see:
|
||||
/// http://clang.llvm.org/extra/clang-tidy/checks/portability/template-virtual-member-function.html
|
||||
class TemplateVirtualMemberFunctionCheck : public ClangTidyCheck {
|
||||
public:
|
||||
TemplateVirtualMemberFunctionCheck(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::portability
|
||||
|
||||
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PORTABILITY_TEMPLATEVIRTUALMEMBERFUNCTIONCHECK_H
|
@ -121,6 +121,12 @@ New checks
|
||||
Gives warnings for tagged unions, where the number of tags is
|
||||
different from the number of data members inside the union.
|
||||
|
||||
- New :doc:`portability-template-virtual-member-function
|
||||
<clang-tidy/checks/portability/template-virtual-member-function>` check.
|
||||
|
||||
Finds cases when an uninstantiated virtual member function in a template class
|
||||
causes cross-compiler incompatibility.
|
||||
|
||||
New check aliases
|
||||
^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
@ -348,6 +348,7 @@ Clang-Tidy Checks
|
||||
:doc:`portability-restrict-system-includes <portability/restrict-system-includes>`, "Yes"
|
||||
:doc:`portability-simd-intrinsics <portability/simd-intrinsics>`,
|
||||
:doc:`portability-std-allocator-const <portability/std-allocator-const>`,
|
||||
:doc:`portability-template-virtual-member-function <portability/template-virtual-member-function>`,
|
||||
:doc:`readability-avoid-const-params-in-decls <readability/avoid-const-params-in-decls>`, "Yes"
|
||||
:doc:`readability-avoid-nested-conditional-operator <readability/avoid-nested-conditional-operator>`,
|
||||
:doc:`readability-avoid-return-with-void-value <readability/avoid-return-with-void-value>`, "Yes"
|
||||
|
@ -0,0 +1,37 @@
|
||||
.. title:: clang-tidy - portability-template-virtual-member-function
|
||||
|
||||
portability-template-virtual-member-function
|
||||
============================================
|
||||
|
||||
Finds cases when an uninstantiated virtual member function in a template class causes
|
||||
cross-compiler incompatibility.
|
||||
|
||||
Upon instantiating a template class, non-virtual member functions don't have to be
|
||||
instantiated unless they are used. Virtual member function instantiation on the other hand
|
||||
is unspecified and depends on the implementation of the compiler.
|
||||
|
||||
In the following snippets the virtual member function is not instantiated by GCC and Clang,
|
||||
but it is instantiated by MSVC, so while the snippet is accepted by the former compilers,
|
||||
it is rejected by the latter.
|
||||
|
||||
.. code:: c++
|
||||
|
||||
template<typename T>
|
||||
struct CrossPlatformError {
|
||||
virtual ~CrossPlatformError() = default;
|
||||
|
||||
static void used() {}
|
||||
|
||||
virtual void unused() {
|
||||
T MSVCError = this;
|
||||
};
|
||||
};
|
||||
|
||||
int main() {
|
||||
CrossPlatformError<int>::used();
|
||||
return 0;
|
||||
}
|
||||
|
||||
Cross-platform projects that need to support MSVC on Windows might see compiler errors
|
||||
because certain virtual member functions are instantiated, which are not instantiated
|
||||
by other compilers on other platforms. This check highlights such virtual member functions.
|
@ -0,0 +1,173 @@
|
||||
// RUN: %check_clang_tidy %s portability-template-virtual-member-function %t
|
||||
namespace UninstantiatedVirtualMember {
|
||||
template<typename T>
|
||||
struct CrossPlatformError {
|
||||
virtual ~CrossPlatformError() = default;
|
||||
|
||||
static void used() {}
|
||||
|
||||
// CHECK-MESSAGES: [[#@LINE+1]]:18: warning: unspecified virtual member function instantiation
|
||||
virtual void unused() {
|
||||
T MSVCError = this;
|
||||
};
|
||||
};
|
||||
|
||||
int main() {
|
||||
// CHECK-MESSAGES: [[#@LINE+1]]:5: note: template instantiated here
|
||||
CrossPlatformError<int>::used();
|
||||
return 0;
|
||||
}
|
||||
} // namespace UninstantiatedVirtualMember
|
||||
|
||||
namespace UninstantiatedVirtualMembers {
|
||||
template<typename T>
|
||||
struct CrossPlatformError {
|
||||
virtual ~CrossPlatformError() = default;
|
||||
|
||||
static void used() {}
|
||||
|
||||
// CHECK-MESSAGES: [[#@LINE+2]]:18: warning: unspecified virtual member function instantiation
|
||||
// CHECK-MESSAGES: [[#@LINE+13]]:5: note: template instantiated here
|
||||
virtual void unused() {
|
||||
T MSVCError = this;
|
||||
};
|
||||
|
||||
// CHECK-MESSAGES: [[#@LINE+2]]:18: warning: unspecified virtual member function instantiation
|
||||
// CHECK-MESSAGES: [[#@LINE+7]]:5: note: template instantiated here
|
||||
virtual void unused2() {
|
||||
T MSVCError = this;
|
||||
};
|
||||
};
|
||||
|
||||
int main() {
|
||||
CrossPlatformError<int>::used();
|
||||
return 0;
|
||||
}
|
||||
} // namespace UninstantiatedVirtualMembers
|
||||
|
||||
namespace UninstantiatedVirtualDestructor {
|
||||
template<typename T>
|
||||
struct CrossPlatformError {
|
||||
// CHECK-MESSAGES: [[#@LINE+2]]:13: warning: unspecified virtual member function instantiation
|
||||
// CHECK-MESSAGES: [[#@LINE+9]]:5: note: template instantiated here
|
||||
virtual ~CrossPlatformError() {
|
||||
T MSVCError = this;
|
||||
};
|
||||
|
||||
static void used() {}
|
||||
};
|
||||
|
||||
int main() {
|
||||
CrossPlatformError<int>::used();
|
||||
return 0;
|
||||
}
|
||||
} // namespace UninstantiatedVirtualDestructor
|
||||
|
||||
namespace MultipleImplicitInstantiations {
|
||||
template<typename T>
|
||||
struct CrossPlatformError {
|
||||
virtual ~CrossPlatformError() = default;
|
||||
|
||||
static void used() {}
|
||||
|
||||
// CHECK-MESSAGES: [[#@LINE+2]]:18: warning: unspecified virtual member function instantiation
|
||||
// CHECK-MESSAGES: [[#@LINE+7]]:5: note: template instantiated here
|
||||
virtual void unused() {
|
||||
T MSVCError = this;
|
||||
};
|
||||
};
|
||||
|
||||
int main() {
|
||||
CrossPlatformError<int>::used();
|
||||
CrossPlatformError<float>::used();
|
||||
CrossPlatformError<long>::used();
|
||||
return 0;
|
||||
}
|
||||
} // namespace MultipleImplicitInstantiations
|
||||
|
||||
namespace SomeImplicitInstantiationError {
|
||||
template <typename T> struct CrossPlatformError {
|
||||
virtual ~CrossPlatformError() = default;
|
||||
|
||||
static void used() {}
|
||||
|
||||
// CHECK-MESSAGES: [[#@LINE+2]]:18: warning: unspecified virtual member function instantiation
|
||||
// CHECK-MESSAGES: [[#@LINE+5]]:5: note: template instantiated here
|
||||
virtual void unused(){};
|
||||
};
|
||||
|
||||
int main() {
|
||||
CrossPlatformError<int>::used();
|
||||
CrossPlatformError<float> NoError;
|
||||
return 0;
|
||||
}
|
||||
} // namespace SomeImplicitInstantiationError
|
||||
|
||||
namespace InstantiatedVirtualMemberFunctions {
|
||||
template<typename T>
|
||||
struct NoError {
|
||||
virtual ~NoError() {};
|
||||
virtual void unused() {};
|
||||
virtual void unused2() {};
|
||||
virtual void unused3() {};
|
||||
};
|
||||
|
||||
int main() {
|
||||
NoError<int> Ne;
|
||||
return 0;
|
||||
}
|
||||
} // namespace InstantiatedVirtualMemberFunctions
|
||||
|
||||
namespace UninstantiatedNonVirtualMemberFunctions {
|
||||
template<typename T>
|
||||
struct NoError {
|
||||
static void used() {};
|
||||
void unused() {};
|
||||
void unused2() {};
|
||||
void unused3() {};
|
||||
};
|
||||
|
||||
int main() {
|
||||
NoError<int>::used();
|
||||
return 0;
|
||||
}
|
||||
} // namespace UninstantiatedNonVirtualMemberFunctions
|
||||
|
||||
namespace PartialSpecializationError {
|
||||
template<typename T, typename U>
|
||||
struct CrossPlatformError {};
|
||||
|
||||
template<typename U>
|
||||
struct CrossPlatformError<int, U>{
|
||||
virtual ~CrossPlatformError() = default;
|
||||
|
||||
static void used() {}
|
||||
|
||||
// CHECK-MESSAGES: [[#@LINE+2]]:18: warning: unspecified virtual member function instantiation
|
||||
// CHECK-MESSAGES: [[#@LINE+7]]:5: note: template instantiated here
|
||||
virtual void unused() {
|
||||
U MSVCError = this;
|
||||
};
|
||||
};
|
||||
|
||||
int main() {
|
||||
CrossPlatformError<int, float>::used();
|
||||
return 0;
|
||||
}
|
||||
} // namespace PartialSpecializationError
|
||||
|
||||
namespace PartialSpecializationNoInstantiation {
|
||||
template<typename T, typename U>
|
||||
struct NoInstantiation {};
|
||||
|
||||
template<typename U>
|
||||
struct NoInstantiation<int, U>{
|
||||
virtual ~NoInstantiation() = default;
|
||||
|
||||
static void used() {}
|
||||
|
||||
virtual void unused() {
|
||||
U MSVCError = this;
|
||||
};
|
||||
};
|
||||
} // namespace PartialSpecializationNoInstantiation
|
Loading…
x
Reference in New Issue
Block a user