xref: /llvm-project/clang-tools-extra/clang-tidy/utils/NamespaceAliaser.cpp (revision 11a411a49b62c129bba551df4587dd446fcdc660)
1 //===---------- NamespaceAliaser.cpp - clang-tidy -------------------------===//
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 "NamespaceAliaser.h"
10 
11 #include "ASTUtils.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 #include "clang/ASTMatchers/ASTMatchers.h"
14 #include "clang/Lex/Lexer.h"
15 #include <optional>
16 namespace clang::tidy::utils {
17 
18 using namespace ast_matchers;
19 
NamespaceAliaser(const SourceManager & SourceMgr)20 NamespaceAliaser::NamespaceAliaser(const SourceManager &SourceMgr)
21     : SourceMgr(SourceMgr) {}
22 
AST_MATCHER_P(NamespaceAliasDecl,hasTargetNamespace,ast_matchers::internal::Matcher<NamespaceDecl>,innerMatcher)23 AST_MATCHER_P(NamespaceAliasDecl, hasTargetNamespace,
24               ast_matchers::internal::Matcher<NamespaceDecl>, innerMatcher) {
25   return innerMatcher.matches(*Node.getNamespace(), Finder, Builder);
26 }
27 
28 std::optional<FixItHint>
createAlias(ASTContext & Context,const Stmt & Statement,StringRef Namespace,const std::vector<std::string> & Abbreviations)29 NamespaceAliaser::createAlias(ASTContext &Context, const Stmt &Statement,
30                               StringRef Namespace,
31                               const std::vector<std::string> &Abbreviations) {
32   const FunctionDecl *Function = getSurroundingFunction(Context, Statement);
33   if (!Function || !Function->hasBody())
34     return std::nullopt;
35 
36   if (AddedAliases[Function].count(Namespace.str()) != 0)
37     return std::nullopt;
38 
39   // FIXME: Doesn't consider the order of declarations.
40   // If we accidentally pick an alias defined later in the function,
41   // the output won't compile.
42   // FIXME: Also doesn't consider file or class-scope aliases.
43 
44   const auto *ExistingAlias = selectFirst<NamedDecl>(
45       "alias", match(functionDecl(hasBody(compoundStmt(has(declStmt(
46                          has(namespaceAliasDecl(hasTargetNamespace(hasName(
47                                                     std::string(Namespace))))
48                                  .bind("alias"))))))),
49                      *Function, Context));
50 
51   if (ExistingAlias != nullptr) {
52     AddedAliases[Function][Namespace.str()] = ExistingAlias->getName().str();
53     return std::nullopt;
54   }
55 
56   for (const auto &Abbreviation : Abbreviations) {
57     DeclarationMatcher ConflictMatcher = namedDecl(hasName(Abbreviation));
58     const auto HasConflictingChildren =
59         !match(findAll(ConflictMatcher), *Function, Context).empty();
60     const auto HasConflictingAncestors =
61         !match(functionDecl(hasAncestor(decl(has(ConflictMatcher)))), *Function,
62                Context)
63              .empty();
64     if (HasConflictingAncestors || HasConflictingChildren)
65       continue;
66 
67     std::string Declaration =
68         (llvm::Twine("\nnamespace ") + Abbreviation + " = " + Namespace + ";")
69             .str();
70     SourceLocation Loc =
71         Lexer::getLocForEndOfToken(Function->getBody()->getBeginLoc(), 0,
72                                    SourceMgr, Context.getLangOpts());
73     AddedAliases[Function][Namespace.str()] = Abbreviation;
74     return FixItHint::CreateInsertion(Loc, Declaration);
75   }
76 
77   return std::nullopt;
78 }
79 
getNamespaceName(ASTContext & Context,const Stmt & Statement,StringRef Namespace) const80 std::string NamespaceAliaser::getNamespaceName(ASTContext &Context,
81                                                const Stmt &Statement,
82                                                StringRef Namespace) const {
83   const auto *Function = getSurroundingFunction(Context, Statement);
84   auto FunctionAliases = AddedAliases.find(Function);
85   if (FunctionAliases != AddedAliases.end()) {
86     if (FunctionAliases->second.count(Namespace) != 0) {
87       return FunctionAliases->second.find(Namespace)->getValue();
88     }
89   }
90   return Namespace.str();
91 }
92 
93 } // namespace clang::tidy::utils
94