[clang-tools-extra] 6d8e966 - [clang-tidy] Portability Template Virtual Member Function Check (#110099)
via cfe-commits
cfe-commits at lists.llvm.org
Thu Oct 10 03:32:42 PDT 2024
Author: isuckatcs
Date: 2024-10-10T12:32:39+02:00
New Revision: 6d8e966512f0b050e84b65c1deed479d5c92fe4c
URL: https://github.com/llvm/llvm-project/commit/6d8e966512f0b050e84b65c1deed479d5c92fe4c
DIFF: https://github.com/llvm/llvm-project/commit/6d8e966512f0b050e84b65c1deed479d5c92fe4c.diff
LOG: [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.
Added:
clang-tools-extra/clang-tidy/portability/TemplateVirtualMemberFunctionCheck.cpp
clang-tools-extra/clang-tidy/portability/TemplateVirtualMemberFunctionCheck.h
clang-tools-extra/docs/clang-tidy/checks/portability/template-virtual-member-function.rst
clang-tools-extra/test/clang-tidy/checkers/portability/template-virtual-member-function.cpp
Modified:
clang-tools-extra/clang-tidy/portability/CMakeLists.txt
clang-tools-extra/clang-tidy/portability/PortabilityTidyModule.cpp
clang-tools-extra/docs/ReleaseNotes.rst
clang-tools-extra/docs/clang-tidy/checks/list.rst
Removed:
################################################################################
diff --git a/clang-tools-extra/clang-tidy/portability/CMakeLists.txt b/clang-tools-extra/clang-tidy/portability/CMakeLists.txt
index 3f0b7d47207938..5a38722a61481b 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 STATIC
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..9c2f27f01f1849
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/portability/TemplateVirtualMemberFunctionCheck.cpp
@@ -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 "
+ "
diff erent compiler");
+ diag(ImplicitSpecialization->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..41f92adadd6e8a
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/portability/TemplateVirtualMemberFunctionCheck.h
@@ -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
diff erent
+/// 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
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 3c4652e3c23774..3f7bcde1eb3014 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -121,6 +121,12 @@ New checks
Gives warnings for tagged unions, where the number of tags is
diff erent 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
^^^^^^^^^^^^^^^^^
diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst
index d76466d480d39c..0082234f5ed31b 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -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"
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..aa3ed6653b475b
--- /dev/null
+++ b/clang-tools-extra/docs/clang-tidy/checks/portability/template-virtual-member-function.rst
@@ -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.
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..94786ae93dd3f3
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/portability/template-virtual-member-function.cpp
@@ -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
More information about the cfe-commits
mailing list