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
17namespace {
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.
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} // namespace
115
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 libcpp
135

Provided by KDAB

Privacy Policy
Update your C++ knowledge – Modern C++11/14/17 Training
Find out more

source code of libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp