File: hide_from_abi.cpp

package info (click to toggle)
llvm-toolchain-19 1%3A19.1.7-3
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 1,998,520 kB
  • sloc: cpp: 6,951,680; ansic: 1,486,157; asm: 913,598; python: 232,024; f90: 80,126; objc: 75,281; lisp: 37,276; pascal: 16,990; sh: 10,009; ml: 5,058; perl: 4,724; awk: 3,523; makefile: 3,167; javascript: 2,504; xml: 892; fortran: 664; cs: 573
file content (110 lines) | stat: -rw-r--r-- 5,013 bytes parent folder | download | duplicates (24)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
//===----------------------------------------------------------------------===//
//
// 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