Run DCE after a LoopFlatten test to reduce spurious output [nfc]
[llvm-project.git] / libcxx / test / tools / clang_tidy_checks / uglify_attributes.cpp
blob5252087d55ee9b3bb9b14a4106a56be1cd64d43f
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 //===----------------------------------------------------------------------===//
9 #include "clang-tidy/ClangTidyCheck.h"
10 #include "clang-tidy/ClangTidyModuleRegistry.h"
12 #include "uglify_attributes.hpp"
14 #include <algorithm>
15 #include <array>
16 #include <span>
17 #include <string_view>
19 namespace {
20 bool isUgly(std::string_view str) {
21 if (str.size() < 2)
22 return false;
23 if (str[0] == '_' && str[1] >= 'A' && str[1] <= 'Z')
24 return true;
25 return str.find("__") != std::string_view::npos;
28 // Starting with Clang 17 ToT C++23 support is provided by CPlusPlus23 instead
29 // of C++23 support is provided by CPlusPlus2b. To allow a smooth transition for
30 // libc++ use "reflection" to select the proper member. Since the change
31 // happens in the development cycle it's not possible to use #ifdefs.
32 template <class T>
33 bool CPlusPlus23(const T& lang_opts)
34 requires requires { T::CPlusPlus2b; }
36 return lang_opts.CPlusPlus2b;
39 template <class T>
40 bool CPlusPlus23(const T& lang_opts)
41 requires requires { T::CPlusPlus23; }
43 return lang_opts.CPlusPlus23;
46 std::vector<const char*> get_standard_attributes(const clang::LangOptions& lang_opts) {
47 std::vector<const char*> attributes = {"noreturn", "carries_dependency"};
49 if (lang_opts.CPlusPlus14)
50 attributes.emplace_back("deprecated");
52 if (lang_opts.CPlusPlus17) {
53 attributes.emplace_back("fallthrough");
54 attributes.emplace_back("nodiscard");
55 attributes.emplace_back("maybe_unused");
58 if (lang_opts.CPlusPlus20) {
59 attributes.emplace_back("likely");
60 attributes.emplace_back("unlikely");
61 attributes.emplace_back("no_unique_address");
64 if (CPlusPlus23(lang_opts)) {
65 attributes.emplace_back("assume");
68 return attributes;
71 AST_MATCHER(clang::Attr, isPretty) {
72 if (Node.isKeywordAttribute() || !Node.getAttrName())
73 return false;
74 if (Node.isCXX11Attribute() && !Node.hasScope()) {
75 if (isUgly(Node.getAttrName()->getName()))
76 return false;
77 return !llvm::is_contained(
78 get_standard_attributes(Finder->getASTContext().getLangOpts()), Node.getAttrName()->getName());
80 if (Node.hasScope())
81 if (!isUgly(Node.getScopeName()->getName()))
82 return true;
83 return !isUgly(Node.getAttrName()->getName());
85 return false;
88 std::optional<std::string> getUglyfiedCXX11Attr(const clang::Attr& attr) {
89 // TODO: Don't emit FixItHints for attributes with `using` in them or emit correct fixes.
91 std::string attr_string;
92 if (attr.isClangScope())
93 attr_string += "_Clang::";
94 else if (attr.isGNUScope())
95 attr_string += "__gnu__::";
97 if (!attr.getAttrName()->getName().starts_with("__")) {
98 attr_string += "__";
99 attr_string += attr.getAttrName()->getName();
100 attr_string += "__";
101 } else {
102 attr_string += attr.getAttrName()->getName();
104 return std::move(attr_string);
107 std::optional<std::string> getUglyfiedGNUAttr(const clang::Attr& attr) {
108 return "__" + attr.getAttrName()->getName().str() + "__";
111 std::optional<std::string> getUglified(const clang::Attr& attr) {
112 if (attr.isCXX11Attribute()) {
113 return getUglyfiedCXX11Attr(attr);
114 } else if (attr.isGNUAttribute()) {
115 return getUglyfiedGNUAttr(attr);
118 return std::nullopt;
120 } // namespace
122 namespace libcpp {
123 uglify_attributes::uglify_attributes(llvm::StringRef name, clang::tidy::ClangTidyContext* context)
124 : clang::tidy::ClangTidyCheck(name, context) {}
126 void uglify_attributes::registerMatchers(clang::ast_matchers::MatchFinder* finder) {
127 using namespace clang::ast_matchers;
128 finder->addMatcher(attr(isPretty()).bind("normal_attribute"), this);
131 void uglify_attributes::check(const clang::ast_matchers::MatchFinder::MatchResult& result) {
132 if (const auto* attr = result.Nodes.getNodeAs<clang::Attr>("normal_attribute"); attr != nullptr) {
133 auto diagnostic = diag(attr->getLoc(), "Non-standard attributes should use the _Ugly spelling");
134 auto uglified = getUglified(*attr);
135 if (uglified.has_value()) {
136 diagnostic << clang::FixItHint::CreateReplacement(attr->getRange(), *uglified);
140 } // namespace libcpp