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 14 #include <algorithm> 15 #include <string_view> 16 17 namespace { 18 bool isUgly(std::string_view str) { 19 if (str.size() < 2) 20 return false; 21 if (str[0] == '_' && str[1] >= 'A' && str[1] <= 'Z') 22 return true; 23 return str.find("__") != std::string_view::npos; 24 } 25 26 AST_MATCHER(clang::Attr, isPretty) { 27 if (Node.isKeywordAttribute()) 28 return false; 29 if (Node.isCXX11Attribute() && !Node.hasScope()) // TODO: reject standard attributes that are version extensions 30 return false; 31 if (Node.hasScope()) 32 if (!isUgly(Node.getScopeName()->getName())) 33 return true; 34 35 if (Node.getAttrName()) 36 return !isUgly(Node.getAttrName()->getName()); 37 38 return false; 39 } 40 41 std::optional<std::string> getUglyfiedCXX11Attr(const clang::Attr& attr) { 42 // Don't try to fix attributes with `using` in them. 43 if (std::ranges::search(std::string_view(attr.getSpelling()), std::string_view("::")).empty()) 44 return std::nullopt; 45 46 std::string attr_string; 47 if (attr.isClangScope()) 48 attr_string += "_Clang::"; 49 else if (attr.isGNUScope()) 50 attr_string += "__gnu__::"; 51 52 if (!attr.getAttrName()->getName().starts_with("__")) { 53 attr_string += "__"; 54 attr_string += attr.getAttrName()->getName(); 55 attr_string += "__"; 56 } else { 57 attr_string += attr.getAttrName()->getName(); 58 } 59 return std::move(attr_string); 60 } 61 62 std::optional<std::string> getUglyfiedGNUAttr(const clang::Attr& attr) { 63 return "__" + attr.getAttrName()->getName().str() + "__"; 64 } 65 66 std::optional<std::string> getUglified(const clang::Attr& attr) { 67 if (attr.isCXX11Attribute()) { 68 return getUglyfiedCXX11Attr(attr); 69 } else if (attr.isGNUAttribute()) { 70 return getUglyfiedGNUAttr(attr); 71 } 72 73 return std::nullopt; 74 } 75 } // namespace 76 77 namespace libcpp { 78 uglify_attributes::uglify_attributes(llvm::StringRef name, clang::tidy::ClangTidyContext* context) 79 : clang::tidy::ClangTidyCheck(name, context) {} 80 81 void uglify_attributes::registerMatchers(clang::ast_matchers::MatchFinder* finder) { 82 using namespace clang::ast_matchers; 83 finder->addMatcher(attr(isPretty()).bind("normal_attribute"), this); 84 } 85 86 void uglify_attributes::check(const clang::ast_matchers::MatchFinder::MatchResult& result) { 87 if (const auto* call = result.Nodes.getNodeAs<clang::Attr>("normal_attribute"); call != nullptr) { 88 auto diagnostic = diag(call->getLoc(), "Non-standard attributes should use the _Ugly spelling"); 89 auto uglified = getUglified(*call); 90 if (uglified.has_value()) { 91 diagnostic << clang::FixItHint::CreateReplacement(call->getRange(), *uglified); 92 } 93 } 94 } 95 } // namespace libcpp 96