File: uglify_attributes.cpp

package info (click to toggle)
llvm-toolchain-18 1%3A18.1.8-20
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 1,909,296 kB
  • sloc: cpp: 6,667,975; ansic: 1,440,452; asm: 883,619; python: 230,549; objc: 76,880; f90: 74,238; lisp: 35,989; pascal: 16,571; sh: 10,229; perl: 7,459; ml: 5,047; awk: 3,523; makefile: 2,992; javascript: 2,149; xml: 892; fortran: 649; cs: 573
file content (145 lines) | stat: -rw-r--r-- 4,474 bytes parent folder | download | duplicates (6)
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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
//===----------------------------------------------------------------------===//
//
// 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 "uglify_attributes.hpp"

#include <algorithm>
#include <array>
#include <span>
#include <string_view>

namespace {
bool isUgly(std::string_view str) {
  if (str.size() < 2)
    return false;
  if (str[0] == '_' && str[1] >= 'A' && str[1] <= 'Z')
    return true;
  return str.find("__") != std::string_view::npos;
}

// Starting with Clang 17 ToT C++23 support is provided by CPlusPlus23 instead
// of C++23 support is provided by CPlusPlus2b. To allow a smooth transition for
// libc++ use "reflection" to select the proper member. Since the change
// happens in the development cycle it's not possible to use #ifdefs.
template <class T>
bool CPlusPlus23(const T& lang_opts)
  requires requires { T::CPlusPlus2b; }
{
  return lang_opts.CPlusPlus2b;
}

template <class T>
bool CPlusPlus23(const T& lang_opts)
  requires requires { T::CPlusPlus23; }
{
  return lang_opts.CPlusPlus23;
}

std::vector<const char*> get_standard_attributes(const clang::LangOptions& lang_opts) {
  std::vector<const char*> attributes;

  if (lang_opts.CPlusPlus11) {
    attributes.emplace_back("noreturn");
    attributes.emplace_back("carries_dependency");
  }

  if (lang_opts.CPlusPlus14)
    attributes.emplace_back("deprecated");

  if (lang_opts.CPlusPlus17) {
    attributes.emplace_back("fallthrough");
    attributes.emplace_back("nodiscard");
    attributes.emplace_back("maybe_unused");
  }

  if (lang_opts.CPlusPlus20) {
    attributes.emplace_back("likely");
    attributes.emplace_back("unlikely");
    attributes.emplace_back("no_unique_address");
  }

  if (CPlusPlus23(lang_opts)) {
    attributes.emplace_back("assume");
  }

  return attributes;
}

AST_MATCHER(clang::Attr, isPretty) {
  if (Node.isKeywordAttribute() || !Node.getAttrName())
    return false;
  if (Node.isCXX11Attribute() && !Node.hasScope()) {
    if (isUgly(Node.getAttrName()->getName()))
      return false;
    return !llvm::is_contained(
        get_standard_attributes(Finder->getASTContext().getLangOpts()), Node.getAttrName()->getName());
  }
  if (Node.hasScope())
    if (!isUgly(Node.getScopeName()->getName()))
      return true;
  return !isUgly(Node.getAttrName()->getName());

  return false;
}

std::optional<std::string> getUglyfiedCXX11Attr(const clang::Attr& attr) {
  // TODO: Don't emit FixItHints for attributes with `using` in them or emit correct fixes.

  std::string attr_string;
  if (attr.isClangScope())
    attr_string += "_Clang::";
  else if (attr.isGNUScope())
    attr_string += "__gnu__::";

  if (!attr.getAttrName()->getName().starts_with("__")) {
    attr_string += "__";
    attr_string += attr.getAttrName()->getName();
    attr_string += "__";
  } else {
    attr_string += attr.getAttrName()->getName();
  }
  return std::move(attr_string);
}

std::optional<std::string> getUglyfiedGNUAttr(const clang::Attr& attr) {
  return "__" + attr.getAttrName()->getName().str() + "__";
}

std::optional<std::string> getUglified(const clang::Attr& attr) {
  if (attr.isCXX11Attribute()) {
    return getUglyfiedCXX11Attr(attr);
  } else if (attr.isGNUAttribute()) {
    return getUglyfiedGNUAttr(attr);
  }

  return std::nullopt;
}
} // namespace

namespace libcpp {
uglify_attributes::uglify_attributes(llvm::StringRef name, clang::tidy::ClangTidyContext* context)
    : clang::tidy::ClangTidyCheck(name, context) {}

void uglify_attributes::registerMatchers(clang::ast_matchers::MatchFinder* finder) {
  using namespace clang::ast_matchers;
  finder->addMatcher(attr(isPretty()).bind("normal_attribute"), this);
}

void uglify_attributes::check(const clang::ast_matchers::MatchFinder::MatchResult& result) {
  if (const auto* attr = result.Nodes.getNodeAs<clang::Attr>("normal_attribute"); attr != nullptr) {
    auto diagnostic = diag(attr->getLoc(), "Non-standard attributes should use the _Ugly spelling");
    auto uglified   = getUglified(*attr);
    if (uglified.has_value()) {
      diagnostic << clang::FixItHint::CreateReplacement(attr->getRange(), *uglified);
    }
  }
}
} // namespace libcpp