1 //===----------------------------------------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "clang-tidy/ClangTidyCheck.h"
10 #include "clang-tidy/ClangTidyModuleRegistry.h"
12 #include "hide_from_abi.hpp"
15 AST_MATCHER(clang::ClassTemplateDecl
, hasFullSpecializations
) { return !Node
.specializations().empty(); }
16 AST_MATCHER(clang::CXXRecordDecl
, isTrivial
) { return Node
.isTrivial(); }
20 hide_from_abi::hide_from_abi(llvm::StringRef name
, clang::tidy::ClangTidyContext
* context
)
21 : clang::tidy::ClangTidyCheck(name
, context
) {}
23 void hide_from_abi::registerMatchers(clang::ast_matchers::MatchFinder
* finder
) {
24 using namespace clang::ast_matchers
;
26 auto has_hide_from_abi_attr
= anyOf(hasAttr(clang::attr::Visibility
), hasAttr(clang::attr::AbiTag
));
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",
39 "__libcpp_snprintf_l",
40 "__libcpp_asprintf_l",
42 "__tree_sub_invariant",
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
53 .bind("hide_from_abi_on_free_function"),
56 auto on_trivial
= allOf(
57 unless(isImplicit()), isDefaulted(), unless(ofClass(hasAncestor(classTemplateDecl()))), ofClass(isTrivial()));
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.
65 has_hide_from_abi_attr
,
68 hasAncestor(cxxRecordDecl(isLambda())),
69 ofClass(anyOf(hasAncestor(classTemplateDecl(hasFullSpecializations())),
70 hasAnyName("basic_filebuf",
72 "basic_ofstream", // These are in the dylib in ABIv2
73 // TODO: fix the matcher to catch `sentry` instantiation.
76 hasParent(classTemplateSpecializationDecl()),
79 .bind("hide_from_abi_on_member_function"),
83 cxxMethodDecl(has_hide_from_abi_attr
, on_trivial
).bind("hide_from_abi_on_defaulted_smf_in_trivial_class"), this);
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");
89 diag(call
->getBeginLoc(), "_LIBCPP_HIDE_FROM_ABI is missing");
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
)
98 if (const auto* call
= result
.Nodes
.getNodeAs
<clang::CXXMethodDecl
>("hide_from_abi_on_member_function");
100 diag(call
->getLocation(), "_LIBCPP_HIDE_FROM_ABI or _LIBCPP_HIDE_FROM_ABI_VIRTUAL is missing");
103 if (const auto* call
=
104 result
.Nodes
.getNodeAs
<clang::CXXMethodDecl
>("hide_from_abi_on_defaulted_smf_in_trivial_class");
106 diag(call
->getLocation(),
107 "_LIBCPP_HIDE_FROM_ABI should not be used for special member functions in trivial classes");
110 } // namespace libcpp