| //===----------------------------------------------------------------------===// |
| // |
| // 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 "clang-tidy/ClangTidyCheck.h" |
| #include "clang-tidy/ClangTidyModuleRegistry.h" |
| |
| #include "hide_from_abi.hpp" |
| |
| namespace { |
| AST_MATCHER(clang::ClassTemplateDecl, hasFullSpecializations) { return !Node.specializations().empty(); } |
| AST_MATCHER(clang::CXXRecordDecl, isTrivial) { return Node.isTrivial(); } |
| } // namespace |
| |
| namespace libcpp { |
| hide_from_abi::hide_from_abi(llvm::StringRef name, clang::tidy::ClangTidyContext* context) |
| : clang::tidy::ClangTidyCheck(name, context) {} |
| |
| void hide_from_abi::registerMatchers(clang::ast_matchers::MatchFinder* finder) { |
| using namespace clang::ast_matchers; |
| |
| auto has_hide_from_abi_attr = anyOf(hasAttr(clang::attr::Visibility), hasAttr(clang::attr::AbiTag)); |
| |
| finder->addMatcher( |
| functionDecl( |
| unless(anyOf( |
| // These functions can't be marked `[[gnu::always_inline]]` for various reasons, |
| // so we can't mark them `_LIBCPP_HIDE_FROM_ABI`. These functions are ignored in |
| // all namespaces. Checking the qualified name is a lot harder and these names |
| // should result in very few (if any) false-negatives. This is also just a |
| // temporary work-around until we can mark functions as HIDE_FROM_ABI without |
| // having to add `[[gnu::always_inline]]` with GCC. |
| hasAnyName("__introsort", |
| "__inplace_merge", |
| "__libcpp_snprintf_l", |
| "__libcpp_asprintf_l", |
| "__libcpp_sscanf_l", |
| "__tree_sub_invariant", |
| "__stable_sort_move", |
| "__stable_sort", |
| "__stable_partition", |
| "__lock_first", |
| "__stable_partition_impl"), |
| has_hide_from_abi_attr, |
| cxxMethodDecl(), // We have explicitly instantiated classes and some of their methods don't have these attributes |
| isDeleted(), |
| isConsteval())), |
| isDefinition()) |
| .bind("hide_from_abi_on_free_function"), |
| this); |
| |
| auto on_trivial = allOf( |
| unless(isImplicit()), isDefaulted(), unless(ofClass(hasAncestor(classTemplateDecl()))), ofClass(isTrivial())); |
| |
| // TODO: find a better way to check for explicit instantiations. Currently, every template that has a full |
| // specialization is ignored. For example, vector is ignored because we instantiate vector<double> |
| // in discrete_distribution. |
| finder->addMatcher( |
| cxxMethodDecl( |
| unless(anyOf( |
| has_hide_from_abi_attr, |
| isDeleted(), |
| isImplicit(), |
| hasAncestor(cxxRecordDecl(isLambda())), |
| ofClass(anyOf(hasAncestor(classTemplateDecl(hasFullSpecializations())), |
| hasAnyName("basic_filebuf", |
| "basic_ifstream", |
| "basic_ofstream", // These are in the dylib in ABIv2 |
| // TODO: fix the matcher to catch `sentry` instantiation. |
| "sentry"))), |
| isConsteval(), |
| hasParent(classTemplateSpecializationDecl()), |
| on_trivial)), |
| isDefinition()) |
| .bind("hide_from_abi_on_member_function"), |
| this); |
| |
| finder->addMatcher( |
| cxxMethodDecl(has_hide_from_abi_attr, on_trivial).bind("hide_from_abi_on_defaulted_smf_in_trivial_class"), this); |
| } |
| |
| void hide_from_abi::check(const clang::ast_matchers::MatchFinder::MatchResult& result) { |
| if (const auto* call = result.Nodes.getNodeAs<clang::FunctionDecl>("hide_from_abi_on_free_function"); |
| call != nullptr) { |
| diag(call->getBeginLoc(), "_LIBCPP_HIDE_FROM_ABI is missing"); |
| } |
| |
| // The rest gets ignored in C++03 because it is subtly different in some cases. |
| // e.g. we change the definition of default constructors in some cases |
| // TODO: check whether we can remove thse differences |
| if (!result.Context->getLangOpts().CPlusPlus11) |
| return; |
| |
| if (const auto* call = result.Nodes.getNodeAs<clang::CXXMethodDecl>("hide_from_abi_on_member_function"); |
| call != nullptr) { |
| diag(call->getLocation(), "_LIBCPP_HIDE_FROM_ABI or _LIBCPP_HIDE_FROM_ABI_VIRTUAL is missing"); |
| } |
| |
| if (const auto* call = |
| result.Nodes.getNodeAs<clang::CXXMethodDecl>("hide_from_abi_on_defaulted_smf_in_trivial_class"); |
| call != nullptr) { |
| diag(call->getLocation(), |
| "_LIBCPP_HIDE_FROM_ABI should not be used for special member functions in trivial classes"); |
| } |
| } |
| } // namespace libcpp |