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