135 lines · cpp
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-exception6//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 17namespace {18 19// Starting with Clang 17 ToT C++23 support is provided by CPlusPlus23 instead20// of C++23 support is provided by CPlusPlus2b. To allow a smooth transition for21// libc++ use "reflection" to select the proper member. Since the change22// happens in the development cycle it's not possible to use #ifdefs.23template <class T>24bool CPlusPlus23(const T& lang_opts)25 requires requires { T::CPlusPlus2b; }26{27 return lang_opts.CPlusPlus2b;28}29 30template <class T>31bool CPlusPlus23(const T& lang_opts)32 requires requires { T::CPlusPlus23; }33{34 return lang_opts.CPlusPlus23;35}36 37std::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 67AST_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 82std::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 101std::optional<std::string> getUglyfiedGNUAttr(const clang::Attr& attr) {102 return "__" + attr.getAttrName()->getName().str() + "__";103}104 105std::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} // namespace115 116namespace libcpp {117uglify_attributes::uglify_attributes(llvm::StringRef name, clang::tidy::ClangTidyContext* context)118 : clang::tidy::ClangTidyCheck(name, context) {}119 120void 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 125void 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 libcpp135