1 //===-- ChangeNamespace.h -- Change namespace  ------------------*- C++ -*-===//
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 #ifndef LLVM_CLANG_TOOLS_EXTRA_CHANGE_NAMESPACE_CHANGENAMESPACE_H
10 #define LLVM_CLANG_TOOLS_EXTRA_CHANGE_NAMESPACE_CHANGENAMESPACE_H
11 
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 #include "clang/Format/Format.h"
14 #include "clang/Tooling/Core/Replacement.h"
15 #include "llvm/Support/Regex.h"
16 #include <string>
17 
18 namespace clang {
19 namespace change_namespace {
20 
21 // This tool can be used to change the surrounding namespaces of class/function
22 // definitions. Classes/functions in the moved namespace will have new
23 // namespaces while references to symbols (e.g. types, functions) which are not
24 // defined in the changed namespace will be correctly qualified by prepending
25 // namespace specifiers before them.
26 // This will try to add shortest namespace specifiers possible. When a symbol
27 // reference needs to be fully-qualified, this adds a "::" prefix to the
28 // namespace specifiers unless the new namespace is the global namespace.
29 // For classes, only classes that are declared/defined in the given namespace in
30 // specified files will be moved: forward declarations will remain in the old
31 // namespace.
32 // For example, changing "a" to "x":
33 // Old code:
34 //   namespace a {
35 //   class FWD;
36 //   class A { FWD *fwd; }
37 //   }  // a
38 // New code:
39 //   namespace a {
40 //   class FWD;
41 //   }  // a
42 //   namespace x {
43 //   class A { ::a::FWD *fwd; }
44 //   }  // x
45 // FIXME: support moving typedef, enums across namespaces.
46 class ChangeNamespaceTool : public ast_matchers::MatchFinder::MatchCallback {
47 public:
48   // Moves code in the old namespace `OldNs` to the new namespace `NewNs` in
49   // files matching `FilePattern`.
50   ChangeNamespaceTool(
51       llvm::StringRef OldNs, llvm::StringRef NewNs, llvm::StringRef FilePattern,
52       llvm::ArrayRef<std::string> AllowedSymbolPatterns,
53       std::map<std::string, tooling::Replacements> *FileToReplacements,
54       llvm::StringRef FallbackStyle = "LLVM");
55 
56   void registerMatchers(ast_matchers::MatchFinder *Finder);
57 
58   void run(const ast_matchers::MatchFinder::MatchResult &Result) override;
59 
60   // Moves the changed code in old namespaces but leaves class forward
61   // declarations behind.
62   void onEndOfTranslationUnit() override;
63 
64 private:
65   void moveOldNamespace(const ast_matchers::MatchFinder::MatchResult &Result,
66                         const NamespaceDecl *NsDecl);
67 
68   void moveClassForwardDeclaration(
69       const ast_matchers::MatchFinder::MatchResult &Result,
70       const NamedDecl *FwdDecl);
71 
72   void replaceQualifiedSymbolInDeclContext(
73       const ast_matchers::MatchFinder::MatchResult &Result,
74       const DeclContext *DeclContext, SourceLocation Start, SourceLocation End,
75       const NamedDecl *FromDecl);
76 
77   void fixTypeLoc(const ast_matchers::MatchFinder::MatchResult &Result,
78                   SourceLocation Start, SourceLocation End, TypeLoc Type);
79 
80   void fixUsingShadowDecl(const ast_matchers::MatchFinder::MatchResult &Result,
81                           const UsingDecl *UsingDeclaration);
82 
83   void fixDeclRefExpr(const ast_matchers::MatchFinder::MatchResult &Result,
84                       const DeclContext *UseContext, const NamedDecl *From,
85                       const DeclRefExpr *Ref);
86 
87   // Information about moving an old namespace.
88   struct MoveNamespace {
89     // The start offset of the namespace block being moved in the original
90     // code.
91     unsigned Offset;
92     // The length of the namespace block in the original code.
93     unsigned Length;
94     // The offset at which the new namespace block will be inserted in the
95     // original code.
96     unsigned InsertionOffset;
97     // The file in which the namespace is declared.
98     FileID FID;
99     SourceManager *SourceMgr;
100   };
101 
102   // Information about inserting a class forward declaration.
103   struct InsertForwardDeclaration {
104     // The offset at while the forward declaration will be inserted in the
105     // original code.
106     unsigned InsertionOffset;
107     // The code to be inserted.
108     std::string ForwardDeclText;
109   };
110 
111   std::string FallbackStyle;
112   // In match callbacks, this contains replacements for replacing `typeLoc`s in
113   // and deleting forward declarations in the moved namespace blocks.
114   // In `onEndOfTranslationUnit` callback, the previous added replacements are
115   // applied (on the moved namespace blocks), and then changed code in old
116   // namespaces re moved to new namespaces, and previously deleted forward
117   // declarations are inserted back to old namespaces, from which they are
118   // deleted.
119   std::map<std::string, tooling::Replacements> &FileToReplacements;
120   // A fully qualified name of the old namespace without "::" prefix, e.g.
121   // "a::b::c".
122   std::string OldNamespace;
123   // A fully qualified name of the new namespace without "::" prefix, e.g.
124   // "x::y::z".
125   std::string NewNamespace;
126   // The longest suffix in the old namespace that does not overlap the new
127   // namespace.
128   // For example, if `OldNamespace` is "a::b::c" and `NewNamespace` is
129   // "a::x::y", then `DiffOldNamespace` will be "b::c".
130   std::string DiffOldNamespace;
131   // The longest suffix in the new namespace that does not overlap the old
132   // namespace.
133   // For example, if `OldNamespace` is "a::b::c" and `NewNamespace` is
134   // "a::x::y", then `DiffNewNamespace` will be "x::y".
135   std::string DiffNewNamespace;
136   // A regex pattern that matches files to be processed.
137   std::string FilePattern;
138   llvm::Regex FilePatternRE;
139   // Information about moved namespaces grouped by file.
140   // Since we are modifying code in old namespaces (e.g. add namespace
141   // specifiers) as well as moving them, we store information about namespaces
142   // to be moved and only move them after all modifications are finished (i.e.
143   // in `onEndOfTranslationUnit`).
144   std::map<std::string, std::vector<MoveNamespace>> MoveNamespaces;
145   // Information about forward declaration insertions grouped by files.
146   // A class forward declaration is not moved, so it will be deleted from the
147   // moved code block and inserted back into the old namespace. The insertion
148   // will be done after removing the code from the old namespace and before
149   // inserting it to the new namespace.
150   std::map<std::string, std::vector<InsertForwardDeclaration>> InsertFwdDecls;
151   // Records all using declarations, which can be used to shorten namespace
152   // specifiers.
153   llvm::SmallPtrSet<const UsingDecl *, 8> UsingDecls;
154   // Records all using namespace declarations, which can be used to shorten
155   // namespace specifiers.
156   llvm::SmallPtrSet<const UsingDirectiveDecl *, 8> UsingNamespaceDecls;
157   // Records all namespace alias declarations, which can be used to shorten
158   // namespace specifiers.
159   llvm::SmallPtrSet<const NamespaceAliasDecl *, 8> NamespaceAliasDecls;
160   // TypeLocs of CXXCtorInitializer. Types of CXXCtorInitializers do not need to
161   // be fixed.
162   llvm::SmallVector<TypeLoc, 8> BaseCtorInitializerTypeLocs;
163   // Since a DeclRefExpr for a function call can be matched twice (one as
164   // CallExpr and one as DeclRefExpr), we record all DeclRefExpr's that have
165   // been processed so that we don't handle them twice.
166   llvm::SmallPtrSet<const clang::DeclRefExpr*, 16> ProcessedFuncRefs;
167   // Patterns of symbol names whose references are not expected to be updated
168   // when changing namespaces around them.
169   std::vector<llvm::Regex> AllowedSymbolRegexes;
170 };
171 
172 } // namespace change_namespace
173 } // namespace clang
174 
175 #endif // LLVM_CLANG_TOOLS_EXTRA_CHANGE_NAMESPACE_CHANGENAMESPACE_H
176