xref: /llvm-project/libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp (revision f69585235ec85d54e0f3fc41b2d5700430907f99)
1c8eff956SNikolas Klauser //===----------------------------------------------------------------------===//
2c8eff956SNikolas Klauser //
3c8eff956SNikolas Klauser // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4c8eff956SNikolas Klauser // See https://llvm.org/LICENSE.txt for license information.
5c8eff956SNikolas Klauser // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6c8eff956SNikolas Klauser //
7c8eff956SNikolas Klauser //===----------------------------------------------------------------------===//
8c8eff956SNikolas Klauser 
9c8eff956SNikolas Klauser #include "clang-tidy/ClangTidyCheck.h"
10c8eff956SNikolas Klauser #include "clang-tidy/ClangTidyModuleRegistry.h"
11c8eff956SNikolas Klauser 
12c8eff956SNikolas Klauser #include "uglify_attributes.hpp"
13*f6958523SNikolas Klauser #include "utilities.hpp"
14c8eff956SNikolas Klauser 
15*f6958523SNikolas Klauser #include <optional>
16c8eff956SNikolas Klauser 
17c8eff956SNikolas Klauser namespace {
18c8eff956SNikolas Klauser 
19cc3be76bSMark de Wever // Starting with Clang 17 ToT C++23 support is provided by CPlusPlus23 instead
20cc3be76bSMark de Wever // of C++23 support is provided by CPlusPlus2b. To allow a smooth transition for
21cc3be76bSMark de Wever // libc++ use "reflection" to select the proper member. Since the change
22cc3be76bSMark de Wever // happens in the development cycle it's not possible to use #ifdefs.
23cc3be76bSMark de Wever template <class T>
24cc3be76bSMark de Wever bool CPlusPlus23(const T& lang_opts)
25cc3be76bSMark de Wever   requires requires { T::CPlusPlus2b; }
26cc3be76bSMark de Wever {
27cc3be76bSMark de Wever   return lang_opts.CPlusPlus2b;
28cc3be76bSMark de Wever }
29cc3be76bSMark de Wever 
30cc3be76bSMark de Wever template <class T>
31cc3be76bSMark de Wever bool CPlusPlus23(const T& lang_opts)
32cc3be76bSMark de Wever   requires requires { T::CPlusPlus23; }
33cc3be76bSMark de Wever {
34cc3be76bSMark de Wever   return lang_opts.CPlusPlus23;
35cc3be76bSMark de Wever }
36cc3be76bSMark de Wever 
37c6afeda8SNikolas Klauser std::vector<const char*> get_standard_attributes(const clang::LangOptions& lang_opts) {
380e823b02SNikolas Klauser   std::vector<const char*> attributes;
390e823b02SNikolas Klauser 
400e823b02SNikolas Klauser   if (lang_opts.CPlusPlus11) {
410e823b02SNikolas Klauser     attributes.emplace_back("noreturn");
420e823b02SNikolas Klauser     attributes.emplace_back("carries_dependency");
430e823b02SNikolas Klauser   }
44c6afeda8SNikolas Klauser 
45c6afeda8SNikolas Klauser   if (lang_opts.CPlusPlus14)
46c6afeda8SNikolas Klauser     attributes.emplace_back("deprecated");
47c6afeda8SNikolas Klauser 
48c6afeda8SNikolas Klauser   if (lang_opts.CPlusPlus17) {
49c6afeda8SNikolas Klauser     attributes.emplace_back("fallthrough");
50c6afeda8SNikolas Klauser     attributes.emplace_back("nodiscard");
51c6afeda8SNikolas Klauser     attributes.emplace_back("maybe_unused");
52c6afeda8SNikolas Klauser   }
53c6afeda8SNikolas Klauser 
54c6afeda8SNikolas Klauser   if (lang_opts.CPlusPlus20) {
55c6afeda8SNikolas Klauser     attributes.emplace_back("likely");
56c6afeda8SNikolas Klauser     attributes.emplace_back("unlikely");
57c6afeda8SNikolas Klauser     attributes.emplace_back("no_unique_address");
58c6afeda8SNikolas Klauser   }
59c6afeda8SNikolas Klauser 
60cc3be76bSMark de Wever   if (CPlusPlus23(lang_opts)) {
61c6afeda8SNikolas Klauser     attributes.emplace_back("assume");
62c6afeda8SNikolas Klauser   }
63c6afeda8SNikolas Klauser 
64c6afeda8SNikolas Klauser   return attributes;
65c6afeda8SNikolas Klauser }
66c6afeda8SNikolas Klauser 
67c8eff956SNikolas Klauser AST_MATCHER(clang::Attr, isPretty) {
6881fb5a0eSMark de Wever   if (Node.isKeywordAttribute() || !Node.getAttrName())
69c8eff956SNikolas Klauser     return false;
70c6afeda8SNikolas Klauser   if (Node.isCXX11Attribute() && !Node.hasScope()) {
71*f6958523SNikolas Klauser     if (is_ugly_name(Node.getAttrName()->getName()))
72c8eff956SNikolas Klauser       return false;
73c6afeda8SNikolas Klauser     return !llvm::is_contained(
74c6afeda8SNikolas Klauser         get_standard_attributes(Finder->getASTContext().getLangOpts()), Node.getAttrName()->getName());
75c6afeda8SNikolas Klauser   }
76c8eff956SNikolas Klauser   if (Node.hasScope())
77*f6958523SNikolas Klauser     if (!is_ugly_name(Node.getScopeName()->getName()))
78c8eff956SNikolas Klauser       return true;
79*f6958523SNikolas Klauser   return !is_ugly_name(Node.getAttrName()->getName());
80c8eff956SNikolas Klauser }
81c8eff956SNikolas Klauser 
82c8eff956SNikolas Klauser std::optional<std::string> getUglyfiedCXX11Attr(const clang::Attr& attr) {
83c6afeda8SNikolas Klauser   // TODO: Don't emit FixItHints for attributes with `using` in them or emit correct fixes.
84c8eff956SNikolas Klauser 
85c8eff956SNikolas Klauser   std::string attr_string;
86c8eff956SNikolas Klauser   if (attr.isClangScope())
87c8eff956SNikolas Klauser     attr_string += "_Clang::";
88c8eff956SNikolas Klauser   else if (attr.isGNUScope())
89c8eff956SNikolas Klauser     attr_string += "__gnu__::";
90c8eff956SNikolas Klauser 
91c8eff956SNikolas Klauser   if (!attr.getAttrName()->getName().starts_with("__")) {
92c8eff956SNikolas Klauser     attr_string += "__";
93c8eff956SNikolas Klauser     attr_string += attr.getAttrName()->getName();
94c8eff956SNikolas Klauser     attr_string += "__";
95c8eff956SNikolas Klauser   } else {
96c8eff956SNikolas Klauser     attr_string += attr.getAttrName()->getName();
97c8eff956SNikolas Klauser   }
98c8eff956SNikolas Klauser   return std::move(attr_string);
99c8eff956SNikolas Klauser }
100c8eff956SNikolas Klauser 
101c8eff956SNikolas Klauser std::optional<std::string> getUglyfiedGNUAttr(const clang::Attr& attr) {
102c8eff956SNikolas Klauser   return "__" + attr.getAttrName()->getName().str() + "__";
103c8eff956SNikolas Klauser }
104c8eff956SNikolas Klauser 
105c8eff956SNikolas Klauser std::optional<std::string> getUglified(const clang::Attr& attr) {
106c8eff956SNikolas Klauser   if (attr.isCXX11Attribute()) {
107c8eff956SNikolas Klauser     return getUglyfiedCXX11Attr(attr);
108c8eff956SNikolas Klauser   } else if (attr.isGNUAttribute()) {
109c8eff956SNikolas Klauser     return getUglyfiedGNUAttr(attr);
110c8eff956SNikolas Klauser   }
111c8eff956SNikolas Klauser 
112c8eff956SNikolas Klauser   return std::nullopt;
113c8eff956SNikolas Klauser }
114c8eff956SNikolas Klauser } // namespace
115c8eff956SNikolas Klauser 
116c8eff956SNikolas Klauser namespace libcpp {
117c8eff956SNikolas Klauser uglify_attributes::uglify_attributes(llvm::StringRef name, clang::tidy::ClangTidyContext* context)
118c8eff956SNikolas Klauser     : clang::tidy::ClangTidyCheck(name, context) {}
119c8eff956SNikolas Klauser 
120c8eff956SNikolas Klauser void uglify_attributes::registerMatchers(clang::ast_matchers::MatchFinder* finder) {
121c8eff956SNikolas Klauser   using namespace clang::ast_matchers;
122c8eff956SNikolas Klauser   finder->addMatcher(attr(isPretty()).bind("normal_attribute"), this);
123c8eff956SNikolas Klauser }
124c8eff956SNikolas Klauser 
125c8eff956SNikolas Klauser void uglify_attributes::check(const clang::ast_matchers::MatchFinder::MatchResult& result) {
126c6afeda8SNikolas Klauser   if (const auto* attr = result.Nodes.getNodeAs<clang::Attr>("normal_attribute"); attr != nullptr) {
127c6afeda8SNikolas Klauser     auto diagnostic = diag(attr->getLoc(), "Non-standard attributes should use the _Ugly spelling");
128c6afeda8SNikolas Klauser     auto uglified   = getUglified(*attr);
129c8eff956SNikolas Klauser     if (uglified.has_value()) {
130c6afeda8SNikolas Klauser       diagnostic << clang::FixItHint::CreateReplacement(attr->getRange(), *uglified);
131c8eff956SNikolas Klauser     }
132c8eff956SNikolas Klauser   }
133c8eff956SNikolas Klauser }
134c8eff956SNikolas Klauser } // namespace libcpp
135