//===----------------------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "clang-tidy/ClangTidyCheck.h" #include "clang-tidy/ClangTidyModuleRegistry.h" #include "uglify_attributes.hpp" #include "utilities.hpp" #include namespace { // Starting with Clang 17 ToT C++23 support is provided by CPlusPlus23 instead // of C++23 support is provided by CPlusPlus2b. To allow a smooth transition for // libc++ use "reflection" to select the proper member. Since the change // happens in the development cycle it's not possible to use #ifdefs. template bool CPlusPlus23(const T& lang_opts) requires requires { T::CPlusPlus2b; } { return lang_opts.CPlusPlus2b; } template bool CPlusPlus23(const T& lang_opts) requires requires { T::CPlusPlus23; } { return lang_opts.CPlusPlus23; } std::vector get_standard_attributes(const clang::LangOptions& lang_opts) { std::vector attributes; if (lang_opts.CPlusPlus11) { attributes.emplace_back("noreturn"); attributes.emplace_back("carries_dependency"); } if (lang_opts.CPlusPlus14) attributes.emplace_back("deprecated"); if (lang_opts.CPlusPlus17) { attributes.emplace_back("fallthrough"); attributes.emplace_back("nodiscard"); attributes.emplace_back("maybe_unused"); } if (lang_opts.CPlusPlus20) { attributes.emplace_back("likely"); attributes.emplace_back("unlikely"); attributes.emplace_back("no_unique_address"); } if (CPlusPlus23(lang_opts)) { attributes.emplace_back("assume"); } return attributes; } AST_MATCHER(clang::Attr, isPretty) { if (Node.isKeywordAttribute() || !Node.getAttrName()) return false; if (Node.isCXX11Attribute() && !Node.hasScope()) { if (is_ugly_name(Node.getAttrName()->getName())) return false; return !llvm::is_contained( get_standard_attributes(Finder->getASTContext().getLangOpts()), Node.getAttrName()->getName()); } if (Node.hasScope()) if (!is_ugly_name(Node.getScopeName()->getName())) return true; return !is_ugly_name(Node.getAttrName()->getName()); } std::optional getUglyfiedCXX11Attr(const clang::Attr& attr) { // TODO: Don't emit FixItHints for attributes with `using` in them or emit correct fixes. std::string attr_string; if (attr.isClangScope()) attr_string += "_Clang::"; else if (attr.isGNUScope()) attr_string += "__gnu__::"; if (!attr.getAttrName()->getName().starts_with("__")) { attr_string += "__"; attr_string += attr.getAttrName()->getName(); attr_string += "__"; } else { attr_string += attr.getAttrName()->getName(); } return std::move(attr_string); } std::optional getUglyfiedGNUAttr(const clang::Attr& attr) { return "__" + attr.getAttrName()->getName().str() + "__"; } std::optional getUglified(const clang::Attr& attr) { if (attr.isCXX11Attribute()) { return getUglyfiedCXX11Attr(attr); } else if (attr.isGNUAttribute()) { return getUglyfiedGNUAttr(attr); } return std::nullopt; } } // namespace namespace libcpp { uglify_attributes::uglify_attributes(llvm::StringRef name, clang::tidy::ClangTidyContext* context) : clang::tidy::ClangTidyCheck(name, context) {} void uglify_attributes::registerMatchers(clang::ast_matchers::MatchFinder* finder) { using namespace clang::ast_matchers; finder->addMatcher(attr(isPretty()).bind("normal_attribute"), this); } void uglify_attributes::check(const clang::ast_matchers::MatchFinder::MatchResult& result) { if (const auto* attr = result.Nodes.getNodeAs("normal_attribute"); attr != nullptr) { auto diagnostic = diag(attr->getLoc(), "Non-standard attributes should use the _Ugly spelling"); auto uglified = getUglified(*attr); if (uglified.has_value()) { diagnostic << clang::FixItHint::CreateReplacement(attr->getRange(), *uglified); } } } } // namespace libcpp