Skip to content

Commit 6d8e966

Browse files
authored
[clang-tidy] Portability Template Virtual Member Function Check (llvm#110099)
Introduced a new check that finds cases when an uninstantiated virtual member function in a template class causes cross-compiler incompatibility.
1 parent 003375f commit 6d8e966

File tree

8 files changed

+303
-0
lines changed

8 files changed

+303
-0
lines changed

clang-tools-extra/clang-tidy/portability/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ add_clang_library(clangTidyPortabilityModule STATIC
99
RestrictSystemIncludesCheck.cpp
1010
SIMDIntrinsicsCheck.cpp
1111
StdAllocatorConstCheck.cpp
12+
TemplateVirtualMemberFunctionCheck.cpp
1213

1314
LINK_LIBS
1415
clangTidy

clang-tools-extra/clang-tidy/portability/PortabilityTidyModule.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include "RestrictSystemIncludesCheck.h"
1313
#include "SIMDIntrinsicsCheck.h"
1414
#include "StdAllocatorConstCheck.h"
15+
#include "TemplateVirtualMemberFunctionCheck.h"
1516

1617
namespace clang::tidy {
1718
namespace portability {
@@ -25,6 +26,8 @@ class PortabilityModule : public ClangTidyModule {
2526
"portability-simd-intrinsics");
2627
CheckFactories.registerCheck<StdAllocatorConstCheck>(
2728
"portability-std-allocator-const");
29+
CheckFactories.registerCheck<TemplateVirtualMemberFunctionCheck>(
30+
"portability-template-virtual-member-function");
2831
}
2932
};
3033

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
//===--- TemplateVirtualMemberFunctionCheck.cpp - clang-tidy --------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "TemplateVirtualMemberFunctionCheck.h"
10+
#include "clang/ASTMatchers/ASTMatchFinder.h"
11+
12+
using namespace clang::ast_matchers;
13+
14+
namespace clang::tidy::portability {
15+
namespace {
16+
AST_MATCHER(CXXMethodDecl, isUsed) { return Node.isUsed(); }
17+
} // namespace
18+
19+
void TemplateVirtualMemberFunctionCheck::registerMatchers(MatchFinder *Finder) {
20+
Finder->addMatcher(
21+
cxxMethodDecl(ofClass(classTemplateSpecializationDecl(
22+
unless(isExplicitTemplateSpecialization()))
23+
.bind("specialization")),
24+
isVirtual(), unless(isUsed()),
25+
unless(cxxDestructorDecl(isDefaulted())))
26+
.bind("method"),
27+
this);
28+
}
29+
30+
void TemplateVirtualMemberFunctionCheck::check(
31+
const MatchFinder::MatchResult &Result) {
32+
const auto *ImplicitSpecialization =
33+
Result.Nodes.getNodeAs<ClassTemplateSpecializationDecl>("specialization");
34+
const auto *MethodDecl = Result.Nodes.getNodeAs<CXXMethodDecl>("method");
35+
36+
diag(MethodDecl->getLocation(),
37+
"unspecified virtual member function instantiation; the virtual "
38+
"member function is not instantiated but it might be with a "
39+
"different compiler");
40+
diag(ImplicitSpecialization->getPointOfInstantiation(),
41+
"template instantiated here", DiagnosticIDs::Note);
42+
}
43+
44+
} // namespace clang::tidy::portability
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
//===--- TemplateVirtualMemberFunctionCheck.h - clang-tidy ------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PORTABILITY_TEMPLATEVIRTUALMEMBERFUNCTIONCHECK_H
10+
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PORTABILITY_TEMPLATEVIRTUALMEMBERFUNCTIONCHECK_H
11+
12+
#include "../ClangTidyCheck.h"
13+
14+
namespace clang::tidy::portability {
15+
16+
/// Upon instantiating a template class, non-virtual member functions don't have
17+
/// to be instantiated unless they are used. Virtual member function
18+
/// instantiation on the other hand is unspecified and depends on the
19+
/// implementation of the compiler. This check intends to find cases when a
20+
/// virtual member function is not instantiated but it might be with a different
21+
/// compiler.
22+
///
23+
/// For the user-facing documentation see:
24+
/// http://clang.llvm.org/extra/clang-tidy/checks/portability/template-virtual-member-function.html
25+
class TemplateVirtualMemberFunctionCheck : public ClangTidyCheck {
26+
public:
27+
TemplateVirtualMemberFunctionCheck(StringRef Name, ClangTidyContext *Context)
28+
: ClangTidyCheck(Name, Context) {}
29+
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
30+
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
31+
bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
32+
return LangOpts.CPlusPlus;
33+
}
34+
};
35+
36+
} // namespace clang::tidy::portability
37+
38+
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PORTABILITY_TEMPLATEVIRTUALMEMBERFUNCTIONCHECK_H

clang-tools-extra/docs/ReleaseNotes.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,12 @@ New checks
121121
Gives warnings for tagged unions, where the number of tags is
122122
different from the number of data members inside the union.
123123

124+
- New :doc:`portability-template-virtual-member-function
125+
<clang-tidy/checks/portability/template-virtual-member-function>` check.
126+
127+
Finds cases when an uninstantiated virtual member function in a template class
128+
causes cross-compiler incompatibility.
129+
124130
New check aliases
125131
^^^^^^^^^^^^^^^^^
126132

clang-tools-extra/docs/clang-tidy/checks/list.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,7 @@ Clang-Tidy Checks
348348
:doc:`portability-restrict-system-includes <portability/restrict-system-includes>`, "Yes"
349349
:doc:`portability-simd-intrinsics <portability/simd-intrinsics>`,
350350
:doc:`portability-std-allocator-const <portability/std-allocator-const>`,
351+
:doc:`portability-template-virtual-member-function <portability/template-virtual-member-function>`,
351352
:doc:`readability-avoid-const-params-in-decls <readability/avoid-const-params-in-decls>`, "Yes"
352353
:doc:`readability-avoid-nested-conditional-operator <readability/avoid-nested-conditional-operator>`,
353354
:doc:`readability-avoid-return-with-void-value <readability/avoid-return-with-void-value>`, "Yes"
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
.. title:: clang-tidy - portability-template-virtual-member-function
2+
3+
portability-template-virtual-member-function
4+
============================================
5+
6+
Finds cases when an uninstantiated virtual member function in a template class causes
7+
cross-compiler incompatibility.
8+
9+
Upon instantiating a template class, non-virtual member functions don't have to be
10+
instantiated unless they are used. Virtual member function instantiation on the other hand
11+
is unspecified and depends on the implementation of the compiler.
12+
13+
In the following snippets the virtual member function is not instantiated by GCC and Clang,
14+
but it is instantiated by MSVC, so while the snippet is accepted by the former compilers,
15+
it is rejected by the latter.
16+
17+
.. code:: c++
18+
19+
template<typename T>
20+
struct CrossPlatformError {
21+
virtual ~CrossPlatformError() = default;
22+
23+
static void used() {}
24+
25+
virtual void unused() {
26+
T MSVCError = this;
27+
};
28+
};
29+
30+
int main() {
31+
CrossPlatformError<int>::used();
32+
return 0;
33+
}
34+
35+
Cross-platform projects that need to support MSVC on Windows might see compiler errors
36+
because certain virtual member functions are instantiated, which are not instantiated
37+
by other compilers on other platforms. This check highlights such virtual member functions.
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
// RUN: %check_clang_tidy %s portability-template-virtual-member-function %t
2+
namespace UninstantiatedVirtualMember {
3+
template<typename T>
4+
struct CrossPlatformError {
5+
virtual ~CrossPlatformError() = default;
6+
7+
static void used() {}
8+
9+
// CHECK-MESSAGES: [[#@LINE+1]]:18: warning: unspecified virtual member function instantiation
10+
virtual void unused() {
11+
T MSVCError = this;
12+
};
13+
};
14+
15+
int main() {
16+
// CHECK-MESSAGES: [[#@LINE+1]]:5: note: template instantiated here
17+
CrossPlatformError<int>::used();
18+
return 0;
19+
}
20+
} // namespace UninstantiatedVirtualMember
21+
22+
namespace UninstantiatedVirtualMembers {
23+
template<typename T>
24+
struct CrossPlatformError {
25+
virtual ~CrossPlatformError() = default;
26+
27+
static void used() {}
28+
29+
// CHECK-MESSAGES: [[#@LINE+2]]:18: warning: unspecified virtual member function instantiation
30+
// CHECK-MESSAGES: [[#@LINE+13]]:5: note: template instantiated here
31+
virtual void unused() {
32+
T MSVCError = this;
33+
};
34+
35+
// CHECK-MESSAGES: [[#@LINE+2]]:18: warning: unspecified virtual member function instantiation
36+
// CHECK-MESSAGES: [[#@LINE+7]]:5: note: template instantiated here
37+
virtual void unused2() {
38+
T MSVCError = this;
39+
};
40+
};
41+
42+
int main() {
43+
CrossPlatformError<int>::used();
44+
return 0;
45+
}
46+
} // namespace UninstantiatedVirtualMembers
47+
48+
namespace UninstantiatedVirtualDestructor {
49+
template<typename T>
50+
struct CrossPlatformError {
51+
// CHECK-MESSAGES: [[#@LINE+2]]:13: warning: unspecified virtual member function instantiation
52+
// CHECK-MESSAGES: [[#@LINE+9]]:5: note: template instantiated here
53+
virtual ~CrossPlatformError() {
54+
T MSVCError = this;
55+
};
56+
57+
static void used() {}
58+
};
59+
60+
int main() {
61+
CrossPlatformError<int>::used();
62+
return 0;
63+
}
64+
} // namespace UninstantiatedVirtualDestructor
65+
66+
namespace MultipleImplicitInstantiations {
67+
template<typename T>
68+
struct CrossPlatformError {
69+
virtual ~CrossPlatformError() = default;
70+
71+
static void used() {}
72+
73+
// CHECK-MESSAGES: [[#@LINE+2]]:18: warning: unspecified virtual member function instantiation
74+
// CHECK-MESSAGES: [[#@LINE+7]]:5: note: template instantiated here
75+
virtual void unused() {
76+
T MSVCError = this;
77+
};
78+
};
79+
80+
int main() {
81+
CrossPlatformError<int>::used();
82+
CrossPlatformError<float>::used();
83+
CrossPlatformError<long>::used();
84+
return 0;
85+
}
86+
} // namespace MultipleImplicitInstantiations
87+
88+
namespace SomeImplicitInstantiationError {
89+
template <typename T> struct CrossPlatformError {
90+
virtual ~CrossPlatformError() = default;
91+
92+
static void used() {}
93+
94+
// CHECK-MESSAGES: [[#@LINE+2]]:18: warning: unspecified virtual member function instantiation
95+
// CHECK-MESSAGES: [[#@LINE+5]]:5: note: template instantiated here
96+
virtual void unused(){};
97+
};
98+
99+
int main() {
100+
CrossPlatformError<int>::used();
101+
CrossPlatformError<float> NoError;
102+
return 0;
103+
}
104+
} // namespace SomeImplicitInstantiationError
105+
106+
namespace InstantiatedVirtualMemberFunctions {
107+
template<typename T>
108+
struct NoError {
109+
virtual ~NoError() {};
110+
virtual void unused() {};
111+
virtual void unused2() {};
112+
virtual void unused3() {};
113+
};
114+
115+
int main() {
116+
NoError<int> Ne;
117+
return 0;
118+
}
119+
} // namespace InstantiatedVirtualMemberFunctions
120+
121+
namespace UninstantiatedNonVirtualMemberFunctions {
122+
template<typename T>
123+
struct NoError {
124+
static void used() {};
125+
void unused() {};
126+
void unused2() {};
127+
void unused3() {};
128+
};
129+
130+
int main() {
131+
NoError<int>::used();
132+
return 0;
133+
}
134+
} // namespace UninstantiatedNonVirtualMemberFunctions
135+
136+
namespace PartialSpecializationError {
137+
template<typename T, typename U>
138+
struct CrossPlatformError {};
139+
140+
template<typename U>
141+
struct CrossPlatformError<int, U>{
142+
virtual ~CrossPlatformError() = default;
143+
144+
static void used() {}
145+
146+
// CHECK-MESSAGES: [[#@LINE+2]]:18: warning: unspecified virtual member function instantiation
147+
// CHECK-MESSAGES: [[#@LINE+7]]:5: note: template instantiated here
148+
virtual void unused() {
149+
U MSVCError = this;
150+
};
151+
};
152+
153+
int main() {
154+
CrossPlatformError<int, float>::used();
155+
return 0;
156+
}
157+
} // namespace PartialSpecializationError
158+
159+
namespace PartialSpecializationNoInstantiation {
160+
template<typename T, typename U>
161+
struct NoInstantiation {};
162+
163+
template<typename U>
164+
struct NoInstantiation<int, U>{
165+
virtual ~NoInstantiation() = default;
166+
167+
static void used() {}
168+
169+
virtual void unused() {
170+
U MSVCError = this;
171+
};
172+
};
173+
} // namespace PartialSpecializationNoInstantiation

0 commit comments

Comments
 (0)