xref: /llvm-project/libcxx/test/tools/clang_tidy_checks/hide_from_abi.cpp (revision 83ce139721260d176773d811f576289370f3313c)
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 {
AST_MATCHER(clang::ClassTemplateDecl,hasFullSpecializations)15 AST_MATCHER(clang::ClassTemplateDecl, hasFullSpecializations) { return !Node.specializations().empty(); }
AST_MATCHER(clang::CXXRecordDecl,isTrivial)16 AST_MATCHER(clang::CXXRecordDecl, isTrivial) { return Node.isTrivial(); }
17 } // namespace
18 
19 namespace libcpp {
hide_from_abi(llvm::StringRef name,clang::tidy::ClangTidyContext * context)20 hide_from_abi::hide_from_abi(llvm::StringRef name, clang::tidy::ClangTidyContext* context)
21     : clang::tidy::ClangTidyCheck(name, context) {}
22 
registerMatchers(clang::ast_matchers::MatchFinder * finder)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 
check(const clang::ast_matchers::MatchFinder::MatchResult & result)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