https://github.com/isuckatcs updated https://github.com/llvm/llvm-project/pull/110099
>From 9cdba3e947705053b14f8eeca39c281fd18e21ce Mon Sep 17 00:00:00 2001 From: isuckatcs <65320245+isucka...@users.noreply.github.com> Date: Thu, 26 Sep 2024 11:43:10 +0200 Subject: [PATCH 1/3] [clang-tidy] Portability Template Virtual Member Function Check --- .../clang-tidy/portability/CMakeLists.txt | 1 + .../portability/PortabilityTidyModule.cpp | 3 + .../TemplateVirtualMemberFunctionCheck.cpp | 49 +++++++ .../TemplateVirtualMemberFunctionCheck.h | 36 +++++ clang-tools-extra/docs/ReleaseNotes.rst | 7 + .../docs/clang-tidy/checks/list.rst | 1 + .../template-virtual-member-function.rst | 33 +++++ .../template-virtual-member-function.cpp | 134 ++++++++++++++++++ 8 files changed, 264 insertions(+) create mode 100644 clang-tools-extra/clang-tidy/portability/TemplateVirtualMemberFunctionCheck.cpp create mode 100644 clang-tools-extra/clang-tidy/portability/TemplateVirtualMemberFunctionCheck.h create mode 100644 clang-tools-extra/docs/clang-tidy/checks/portability/template-virtual-member-function.rst create mode 100644 clang-tools-extra/test/clang-tidy/checkers/portability/template-virtual-member-function.cpp diff --git a/clang-tools-extra/clang-tidy/portability/CMakeLists.txt b/clang-tools-extra/clang-tidy/portability/CMakeLists.txt index 01a86d686daa76..df08cf2c8e292c 100644 --- a/clang-tools-extra/clang-tidy/portability/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/portability/CMakeLists.txt @@ -9,6 +9,7 @@ add_clang_library(clangTidyPortabilityModule RestrictSystemIncludesCheck.cpp SIMDIntrinsicsCheck.cpp StdAllocatorConstCheck.cpp + TemplateVirtualMemberFunctionCheck.cpp LINK_LIBS clangTidy diff --git a/clang-tools-extra/clang-tidy/portability/PortabilityTidyModule.cpp b/clang-tools-extra/clang-tidy/portability/PortabilityTidyModule.cpp index b3759a754587d7..316b98b46cf3f2 100644 --- a/clang-tools-extra/clang-tidy/portability/PortabilityTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/portability/PortabilityTidyModule.cpp @@ -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 @@ class PortabilityModule : public ClangTidyModule { "portability-simd-intrinsics"); CheckFactories.registerCheck<StdAllocatorConstCheck>( "portability-std-allocator-const"); + CheckFactories.registerCheck<TemplateVirtualMemberFunctionCheck>( + "portability-template-virtual-member-function"); } }; diff --git a/clang-tools-extra/clang-tidy/portability/TemplateVirtualMemberFunctionCheck.cpp b/clang-tools-extra/clang-tidy/portability/TemplateVirtualMemberFunctionCheck.cpp new file mode 100644 index 00000000000000..6b7b50798798fa --- /dev/null +++ b/clang-tools-extra/clang-tidy/portability/TemplateVirtualMemberFunctionCheck.cpp @@ -0,0 +1,49 @@ +//===--- 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 { + +void TemplateVirtualMemberFunctionCheck::registerMatchers(MatchFinder *Finder) { + Finder->addMatcher(classTemplateSpecializationDecl().bind("specialization"), + this); +} + +void TemplateVirtualMemberFunctionCheck::check( + const MatchFinder::MatchResult &Result) { + const auto *MatchedDecl = + Result.Nodes.getNodeAs<ClassTemplateSpecializationDecl>("specialization"); + + if (MatchedDecl->isExplicitSpecialization()) + return; + + for (auto &&Method : MatchedDecl->methods()) { + if (!Method->isVirtual()) + continue; + + if (const auto *Dtor = llvm::dyn_cast<CXXDestructorDecl>(Method); + Dtor && Dtor->isDefaulted()) + continue; + + if (!Method->isUsed()) { + diag(Method->getLocation(), + "unspecified virtual member function instantiation; the virtual " + "member function is not instantiated but it might be with a " + "different compiler"); + diag(MatchedDecl->getPointOfInstantiation(), "template instantiated here", + DiagnosticIDs::Note); + } + } +} + +} // namespace clang::tidy::portability diff --git a/clang-tools-extra/clang-tidy/portability/TemplateVirtualMemberFunctionCheck.h b/clang-tools-extra/clang-tidy/portability/TemplateVirtualMemberFunctionCheck.h new file mode 100644 index 00000000000000..9d7918dbdba612 --- /dev/null +++ b/clang-tools-extra/clang-tidy/portability/TemplateVirtualMemberFunctionCheck.h @@ -0,0 +1,36 @@ +//===--- 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 { + +/// Detects C++ [temp.inst#11]: "It is unspecified whether or not an +/// implementation implicitly instantiates a virtual member function of a class +/// template if the virtual member function would not otherwise be +/// instantiated." +/// +/// 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 diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst index 8f7b0b5333f3a1..81e83c66273ed0 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -103,6 +103,13 @@ Improvements to clang-tidy New checks ^^^^^^^^^^ +- New :doc:`portability-template-virtual-member-function + <clang-tidy/checks/portability/template-virtual-member-function>` check. + + Detects C++ [temp.inst#11]: "It is unspecified whether or not an implementation + implicitly instantiates a virtual member function of a class template if the + virtual member function would not otherwise be instantiated." + New check aliases ^^^^^^^^^^^^^^^^^ diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst index 1909d7b8d8e246..b7fc78de79eadd 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/list.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst @@ -346,6 +346,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" diff --git a/clang-tools-extra/docs/clang-tidy/checks/portability/template-virtual-member-function.rst b/clang-tools-extra/docs/clang-tidy/checks/portability/template-virtual-member-function.rst new file mode 100644 index 00000000000000..3e22d967734fa1 --- /dev/null +++ b/clang-tools-extra/docs/clang-tidy/checks/portability/template-virtual-member-function.rst @@ -0,0 +1,33 @@ +.. title:: clang-tidy - portability-template-virtual-member-function + +portability-template-virtual-member-function +============================================ +Per C++ ``[temp.inst#11]``: "It is unspecified whether or not an implementation +implicitly instantiates a virtual member function of a class template if the virtual +member function would not otherwise be instantiated." + +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. diff --git a/clang-tools-extra/test/clang-tidy/checkers/portability/template-virtual-member-function.cpp b/clang-tools-extra/test/clang-tidy/checkers/portability/template-virtual-member-function.cpp new file mode 100644 index 00000000000000..59503e46b9bf3f --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/portability/template-virtual-member-function.cpp @@ -0,0 +1,134 @@ +// 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 >From 19fee22058d3e73618e11f41073d889db1700a36 Mon Sep 17 00:00:00 2001 From: isuckatcs <65320245+isucka...@users.noreply.github.com> Date: Thu, 26 Sep 2024 20:09:16 +0200 Subject: [PATCH 2/3] address comments --- .../TemplateVirtualMemberFunctionCheck.cpp | 3 +-- clang-tools-extra/docs/ReleaseNotes.rst | 8 +++++--- .../portability/template-virtual-member-function.rst | 11 +++++++---- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/clang-tools-extra/clang-tidy/portability/TemplateVirtualMemberFunctionCheck.cpp b/clang-tools-extra/clang-tidy/portability/TemplateVirtualMemberFunctionCheck.cpp index 6b7b50798798fa..e97c32ba46ed27 100644 --- a/clang-tools-extra/clang-tidy/portability/TemplateVirtualMemberFunctionCheck.cpp +++ b/clang-tools-extra/clang-tidy/portability/TemplateVirtualMemberFunctionCheck.cpp @@ -1,5 +1,4 @@ -//===--- TemplateVirtualMemberFunctionCheck.cpp - clang-tidy -//-------------------===// +//===--- 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. diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst index 81e83c66273ed0..11947cd1631bf1 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -106,9 +106,11 @@ New checks - New :doc:`portability-template-virtual-member-function <clang-tidy/checks/portability/template-virtual-member-function>` check. - Detects C++ [temp.inst#11]: "It is unspecified whether or not an implementation - implicitly instantiates a virtual member function of a class template if the - virtual member function would not otherwise be instantiated." + 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. New check aliases ^^^^^^^^^^^^^^^^^ diff --git a/clang-tools-extra/docs/clang-tidy/checks/portability/template-virtual-member-function.rst b/clang-tools-extra/docs/clang-tidy/checks/portability/template-virtual-member-function.rst index 3e22d967734fa1..084488a68ea82e 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/portability/template-virtual-member-function.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/portability/template-virtual-member-function.rst @@ -2,11 +2,14 @@ portability-template-virtual-member-function ============================================ -Per C++ ``[temp.inst#11]``: "It is unspecified whether or not an implementation -implicitly instantiates a virtual member function of a class template if the virtual -member function would not otherwise be instantiated." -In the following snippets the virtual member function is not instantiated by gcc and clang, +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. + +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. >From 2e0f9ab80032938fbdac8aaa3ab87a1be0a89ad7 Mon Sep 17 00:00:00 2001 From: isuckatcs <65320245+isucka...@users.noreply.github.com> Date: Fri, 27 Sep 2024 01:57:00 +0200 Subject: [PATCH 3/3] address comments --- .../TemplateVirtualMemberFunctionCheck.cpp | 9 ++--- .../TemplateVirtualMemberFunctionCheck.h | 10 +++-- .../template-virtual-member-function.cpp | 39 +++++++++++++++++++ 3 files changed, 49 insertions(+), 9 deletions(-) diff --git a/clang-tools-extra/clang-tidy/portability/TemplateVirtualMemberFunctionCheck.cpp b/clang-tools-extra/clang-tidy/portability/TemplateVirtualMemberFunctionCheck.cpp index e97c32ba46ed27..80e59a2f0e596a 100644 --- a/clang-tools-extra/clang-tidy/portability/TemplateVirtualMemberFunctionCheck.cpp +++ b/clang-tools-extra/clang-tidy/portability/TemplateVirtualMemberFunctionCheck.cpp @@ -14,7 +14,9 @@ using namespace clang::ast_matchers; namespace clang::tidy::portability { void TemplateVirtualMemberFunctionCheck::registerMatchers(MatchFinder *Finder) { - Finder->addMatcher(classTemplateSpecializationDecl().bind("specialization"), + Finder->addMatcher(classTemplateSpecializationDecl( + unless(isExplicitTemplateSpecialization())) + .bind("specialization"), this); } @@ -23,10 +25,7 @@ void TemplateVirtualMemberFunctionCheck::check( const auto *MatchedDecl = Result.Nodes.getNodeAs<ClassTemplateSpecializationDecl>("specialization"); - if (MatchedDecl->isExplicitSpecialization()) - return; - - for (auto &&Method : MatchedDecl->methods()) { + for (const CXXMethodDecl *Method : MatchedDecl->methods()) { if (!Method->isVirtual()) continue; diff --git a/clang-tools-extra/clang-tidy/portability/TemplateVirtualMemberFunctionCheck.h b/clang-tools-extra/clang-tidy/portability/TemplateVirtualMemberFunctionCheck.h index 9d7918dbdba612..41f92adadd6e8a 100644 --- a/clang-tools-extra/clang-tidy/portability/TemplateVirtualMemberFunctionCheck.h +++ b/clang-tools-extra/clang-tidy/portability/TemplateVirtualMemberFunctionCheck.h @@ -13,10 +13,12 @@ namespace clang::tidy::portability { -/// Detects C++ [temp.inst#11]: "It is unspecified whether or not an -/// implementation implicitly instantiates a virtual member function of a class -/// template if the virtual member function would not otherwise be -/// instantiated." +/// 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 diff --git a/clang-tools-extra/test/clang-tidy/checkers/portability/template-virtual-member-function.cpp b/clang-tools-extra/test/clang-tidy/checkers/portability/template-virtual-member-function.cpp index 59503e46b9bf3f..94786ae93dd3f3 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/portability/template-virtual-member-function.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/portability/template-virtual-member-function.cpp @@ -132,3 +132,42 @@ int main() { 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 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits