[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