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 |
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 | |