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