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