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
= {"noreturn", "carries_dependency"};
49 if (lang_opts
.CPlusPlus14
)
50 attributes
.emplace_back("deprecated");
52 if (lang_opts
.CPlusPlus17
) {
53 attributes
.emplace_back("fallthrough");
54 attributes
.emplace_back("nodiscard");
55 attributes
.emplace_back("maybe_unused");
58 if (lang_opts
.CPlusPlus20
) {
59 attributes
.emplace_back("likely");
60 attributes
.emplace_back("unlikely");
61 attributes
.emplace_back("no_unique_address");
64 if (CPlusPlus23(lang_opts
)) {
65 attributes
.emplace_back("assume");
71 AST_MATCHER(clang::Attr
, isPretty
) {
72 if (Node
.isKeywordAttribute() || !Node
.getAttrName())
74 if (Node
.isCXX11Attribute() && !Node
.hasScope()) {
75 if (isUgly(Node
.getAttrName()->getName()))
77 return !llvm::is_contained(
78 get_standard_attributes(Finder
->getASTContext().getLangOpts()), Node
.getAttrName()->getName());
81 if (!isUgly(Node
.getScopeName()->getName()))
83 return !isUgly(Node
.getAttrName()->getName());
88 std::optional
<std::string
> getUglyfiedCXX11Attr(const clang::Attr
& attr
) {
89 // TODO: Don't emit FixItHints for attributes with `using` in them or emit correct fixes.
91 std::string attr_string
;
92 if (attr
.isClangScope())
93 attr_string
+= "_Clang::";
94 else if (attr
.isGNUScope())
95 attr_string
+= "__gnu__::";
97 if (!attr
.getAttrName()->getName().starts_with("__")) {
99 attr_string
+= attr
.getAttrName()->getName();
102 attr_string
+= attr
.getAttrName()->getName();
104 return std::move(attr_string
);
107 std::optional
<std::string
> getUglyfiedGNUAttr(const clang::Attr
& attr
) {
108 return "__" + attr
.getAttrName()->getName().str() + "__";
111 std::optional
<std::string
> getUglified(const clang::Attr
& attr
) {
112 if (attr
.isCXX11Attribute()) {
113 return getUglyfiedCXX11Attr(attr
);
114 } else if (attr
.isGNUAttribute()) {
115 return getUglyfiedGNUAttr(attr
);
123 uglify_attributes::uglify_attributes(llvm::StringRef name
, clang::tidy::ClangTidyContext
* context
)
124 : clang::tidy::ClangTidyCheck(name
, context
) {}
126 void uglify_attributes::registerMatchers(clang::ast_matchers::MatchFinder
* finder
) {
127 using namespace clang::ast_matchers
;
128 finder
->addMatcher(attr(isPretty()).bind("normal_attribute"), this);
131 void uglify_attributes::check(const clang::ast_matchers::MatchFinder::MatchResult
& result
) {
132 if (const auto* attr
= result
.Nodes
.getNodeAs
<clang::Attr
>("normal_attribute"); attr
!= nullptr) {
133 auto diagnostic
= diag(attr
->getLoc(), "Non-standard attributes should use the _Ugly spelling");
134 auto uglified
= getUglified(*attr
);
135 if (uglified
.has_value()) {
136 diagnostic
<< clang::FixItHint::CreateReplacement(attr
->getRange(), *uglified
);
140 } // namespace libcpp