1 | //===----------------------------------------------------------------------===// |
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 "clang-tidy/ClangTidyCheck.h" |
10 | #include "clang-tidy/ClangTidyModuleRegistry.h" |
11 | |
12 | #include "hide_from_abi.hpp" |
13 | |
14 | namespace { |
15 | AST_MATCHER(clang::ClassTemplateDecl, hasFullSpecializations) { return !Node.specializations().empty(); } |
16 | AST_MATCHER(clang::CXXRecordDecl, isTrivial) { return Node.isTrivial(); } |
17 | } // namespace |
18 | |
19 | namespace libcpp { |
20 | hide_from_abi::hide_from_abi(llvm::StringRef name, clang::tidy::ClangTidyContext* context) |
21 | : clang::tidy::ClangTidyCheck(name, context) {} |
22 | |
23 | void hide_from_abi::registerMatchers(clang::ast_matchers::MatchFinder* finder) { |
24 | using namespace clang::ast_matchers; |
25 | |
26 | auto has_hide_from_abi_attr = anyOf(hasAttr(clang::attr::Visibility), hasAttr(clang::attr::AbiTag)); |
27 | |
28 | finder->addMatcher( |
29 | functionDecl( |
30 | unless(anyOf( |
31 | // These functions can't be marked `[[gnu::always_inline]]` for various reasons, |
32 | // so we can't mark them `_LIBCPP_HIDE_FROM_ABI`. These functions are ignored in |
33 | // all namespaces. Checking the qualified name is a lot harder and these names |
34 | // should result in very few (if any) false-negatives. This is also just a |
35 | // temporary work-around until we can mark functions as HIDE_FROM_ABI without |
36 | // having to add `[[gnu::always_inline]]` with GCC. |
37 | hasAnyName("__introsort" , |
38 | "__inplace_merge" , |
39 | "__libcpp_snprintf_l" , |
40 | "__libcpp_asprintf_l" , |
41 | "__libcpp_sscanf_l" , |
42 | "__tree_sub_invariant" , |
43 | "__stable_sort_move" , |
44 | "__stable_sort" , |
45 | "__stable_partition" , |
46 | "__lock_first" , |
47 | "__stable_partition_impl" ), |
48 | has_hide_from_abi_attr, |
49 | cxxMethodDecl(), // We have explicitly instantiated classes and some of their methods don't have these attributes |
50 | isDeleted(), |
51 | isConsteval())), |
52 | isDefinition()) |
53 | .bind("hide_from_abi_on_free_function" ), |
54 | this); |
55 | |
56 | auto on_trivial = allOf( |
57 | unless(isImplicit()), isDefaulted(), unless(ofClass(hasAncestor(classTemplateDecl()))), ofClass(isTrivial())); |
58 | |
59 | // TODO: find a better way to check for explicit instantiations. Currently, every template that has a full |
60 | // specialization is ignored. For example, vector is ignored because we instantiate vector<double> |
61 | // in discrete_distribution. |
62 | finder->addMatcher( |
63 | cxxMethodDecl( |
64 | unless(anyOf( |
65 | has_hide_from_abi_attr, |
66 | isDeleted(), |
67 | isImplicit(), |
68 | hasAncestor(cxxRecordDecl(isLambda())), |
69 | ofClass(anyOf(hasAncestor(classTemplateDecl(hasFullSpecializations())), |
70 | hasAnyName("basic_filebuf" , |
71 | "basic_ifstream" , |
72 | "basic_ofstream" , // These are in the dylib in ABIv2 |
73 | // TODO: fix the matcher to catch `sentry` instantiation. |
74 | "sentry" ))), |
75 | isConsteval(), |
76 | hasParent(classTemplateSpecializationDecl()), |
77 | on_trivial)), |
78 | isDefinition()) |
79 | .bind("hide_from_abi_on_member_function" ), |
80 | this); |
81 | |
82 | finder->addMatcher( |
83 | cxxMethodDecl(has_hide_from_abi_attr, on_trivial).bind("hide_from_abi_on_defaulted_smf_in_trivial_class" ), this); |
84 | } |
85 | |
86 | void hide_from_abi::check(const clang::ast_matchers::MatchFinder::MatchResult& result) { |
87 | if (const auto* call = result.Nodes.getNodeAs<clang::FunctionDecl>("hide_from_abi_on_free_function" ); |
88 | call != nullptr) { |
89 | diag(call->getBeginLoc(), "_LIBCPP_HIDE_FROM_ABI is missing" ); |
90 | } |
91 | |
92 | // The rest gets ignored in C++03 because it is subtly different in some cases. |
93 | // e.g. we change the definition of default constructors in some cases |
94 | // TODO: check whether we can remove thse differences |
95 | if (!result.Context->getLangOpts().CPlusPlus11) |
96 | return; |
97 | |
98 | if (const auto* call = result.Nodes.getNodeAs<clang::CXXMethodDecl>("hide_from_abi_on_member_function" ); |
99 | call != nullptr) { |
100 | diag(call->getLocation(), "_LIBCPP_HIDE_FROM_ABI or _LIBCPP_HIDE_FROM_ABI_VIRTUAL is missing" ); |
101 | } |
102 | |
103 | if (const auto* call = |
104 | result.Nodes.getNodeAs<clang::CXXMethodDecl>("hide_from_abi_on_defaulted_smf_in_trivial_class" ); |
105 | call != nullptr) { |
106 | diag(call->getLocation(), |
107 | "_LIBCPP_HIDE_FROM_ABI should not be used for special member functions in trivial classes" ); |
108 | } |
109 | } |
110 | } // namespace libcpp |
111 | |