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