1 //===----------------------------------------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "clang-tidy/ClangTidyCheck.h"
10 #include "clang-tidy/ClangTidyModuleRegistry.h"
12 #include "uglify_attributes.hpp"
17 #include <string_view>
20 bool isUgly(std::string_view str
) {
23 if (str
[0] == '_' && str
[1] >= 'A' && str
[1] <= 'Z')
25 return str
.find("__") != std::string_view::npos
;
28 // Starting with Clang 17 ToT C++23 support is provided by CPlusPlus23 instead
29 // of C++23 support is provided by CPlusPlus2b. To allow a smooth transition for
30 // libc++ use "reflection" to select the proper member. Since the change
31 // happens in the development cycle it's not possible to use #ifdefs.
33 bool CPlusPlus23(const T
& lang_opts
)
34 requires requires
{ T::CPlusPlus2b
; }
36 return lang_opts
.CPlusPlus2b
;
40 bool CPlusPlus23(const T
& lang_opts
)
41 requires requires
{ T::CPlusPlus23
; }
43 return lang_opts
.CPlusPlus23
;
46 std::vector
<const char*> get_standard_attributes(const clang::LangOptions
& lang_opts
) {
47 std::vector
<const char*> attributes
;
49 if (lang_opts
.CPlusPlus11
) {
50 attributes
.emplace_back("noreturn");
51 attributes
.emplace_back("carries_dependency");
54 if (lang_opts
.CPlusPlus14
)
55 attributes
.emplace_back("deprecated");
57 if (lang_opts
.CPlusPlus17
) {
58 attributes
.emplace_back("fallthrough");
59 attributes
.emplace_back("nodiscard");
60 attributes
.emplace_back("maybe_unused");
63 if (lang_opts
.CPlusPlus20
) {
64 attributes
.emplace_back("likely");
65 attributes
.emplace_back("unlikely");
66 attributes
.emplace_back("no_unique_address");
69 if (CPlusPlus23(lang_opts
)) {
70 attributes
.emplace_back("assume");
76 AST_MATCHER(clang::Attr
, isPretty
) {
77 if (Node
.isKeywordAttribute() || !Node
.getAttrName())
79 if (Node
.isCXX11Attribute() && !Node
.hasScope()) {
80 if (isUgly(Node
.getAttrName()->getName()))
82 return !llvm::is_contained(
83 get_standard_attributes(Finder
->getASTContext().getLangOpts()), Node
.getAttrName()->getName());
86 if (!isUgly(Node
.getScopeName()->getName()))
88 return !isUgly(Node
.getAttrName()->getName());
93 std::optional
<std::string
> getUglyfiedCXX11Attr(const clang::Attr
& attr
) {
94 // TODO: Don't emit FixItHints for attributes with `using` in them or emit correct fixes.
96 std::string attr_string
;
97 if (attr
.isClangScope())
98 attr_string
+= "_Clang::";
99 else if (attr
.isGNUScope())
100 attr_string
+= "__gnu__::";
102 if (!attr
.getAttrName()->getName().starts_with("__")) {
104 attr_string
+= attr
.getAttrName()->getName();
107 attr_string
+= attr
.getAttrName()->getName();
109 return std::move(attr_string
);
112 std::optional
<std::string
> getUglyfiedGNUAttr(const clang::Attr
& attr
) {
113 return "__" + attr
.getAttrName()->getName().str() + "__";
116 std::optional
<std::string
> getUglified(const clang::Attr
& attr
) {
117 if (attr
.isCXX11Attribute()) {
118 return getUglyfiedCXX11Attr(attr
);
119 } else if (attr
.isGNUAttribute()) {
120 return getUglyfiedGNUAttr(attr
);
128 uglify_attributes::uglify_attributes(llvm::StringRef name
, clang::tidy::ClangTidyContext
* context
)
129 : clang::tidy::ClangTidyCheck(name
, context
) {}
131 void uglify_attributes::registerMatchers(clang::ast_matchers::MatchFinder
* finder
) {
132 using namespace clang::ast_matchers
;
133 finder
->addMatcher(attr(isPretty()).bind("normal_attribute"), this);
136 void uglify_attributes::check(const clang::ast_matchers::MatchFinder::MatchResult
& result
) {
137 if (const auto* attr
= result
.Nodes
.getNodeAs
<clang::Attr
>("normal_attribute"); attr
!= nullptr) {
138 auto diagnostic
= diag(attr
->getLoc(), "Non-standard attributes should use the _Ugly spelling");
139 auto uglified
= getUglified(*attr
);
140 if (uglified
.has_value()) {
141 diagnostic
<< clang::FixItHint::CreateReplacement(attr
->getRange(), *uglified
);
145 } // namespace libcpp