176 lines · c
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-exception6//7//===----------------------------------------------------------------------===//8 9#ifndef LLVM_CLANG_TOOLS_EXTRA_CHANGE_NAMESPACE_CHANGENAMESPACE_H10#define LLVM_CLANG_TOOLS_EXTRA_CHANGE_NAMESPACE_CHANGENAMESPACE_H11 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 18namespace clang {19namespace change_namespace {20 21// This tool can be used to change the surrounding namespaces of class/function22// definitions. Classes/functions in the moved namespace will have new23// namespaces while references to symbols (e.g. types, functions) which are not24// defined in the changed namespace will be correctly qualified by prepending25// namespace specifiers before them.26// This will try to add shortest namespace specifiers possible. When a symbol27// reference needs to be fully-qualified, this adds a "::" prefix to the28// namespace specifiers unless the new namespace is the global namespace.29// For classes, only classes that are declared/defined in the given namespace in30// specified files will be moved: forward declarations will remain in the old31// namespace.32// For example, changing "a" to "x":33// Old code:34// namespace a {35// class FWD;36// class A { FWD *fwd; }37// } // a38// New code:39// namespace a {40// class FWD;41// } // a42// namespace x {43// class A { ::a::FWD *fwd; }44// } // x45// FIXME: support moving typedef, enums across namespaces.46class ChangeNamespaceTool : public ast_matchers::MatchFinder::MatchCallback {47public:48 // Moves code in the old namespace `OldNs` to the new namespace `NewNs` in49 // 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 forward61 // declarations behind.62 void onEndOfTranslationUnit() override;63 64private: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 original90 // 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 the95 // 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 the105 // 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 in113 // and deleting forward declarations in the moved namespace blocks.114 // In `onEndOfTranslationUnit` callback, the previous added replacements are115 // applied (on the moved namespace blocks), and then changed code in old116 // namespaces re moved to new namespaces, and previously deleted forward117 // declarations are inserted back to old namespaces, from which they are118 // 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 new127 // namespace.128 // For example, if `OldNamespace` is "a::b::c" and `NewNamespace` is129 // "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 old132 // namespace.133 // For example, if `OldNamespace` is "a::b::c" and `NewNamespace` is134 // "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 namespace141 // specifiers) as well as moving them, we store information about namespaces142 // 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 the147 // moved code block and inserted back into the old namespace. The insertion148 // will be done after removing the code from the old namespace and before149 // 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 namespace152 // specifiers.153 llvm::SmallPtrSet<const UsingDecl *, 8> UsingDecls;154 // Records all using namespace declarations, which can be used to shorten155 // namespace specifiers.156 llvm::SmallPtrSet<const UsingDirectiveDecl *, 8> UsingNamespaceDecls;157 // Records all namespace alias declarations, which can be used to shorten158 // namespace specifiers.159 llvm::SmallPtrSet<const NamespaceAliasDecl *, 8> NamespaceAliasDecls;160 // TypeLocs of CXXCtorInitializer. Types of CXXCtorInitializers do not need to161 // be fixed.162 llvm::SmallVector<TypeLoc, 8> BaseCtorInitializerTypeLocs;163 // Since a DeclRefExpr for a function call can be matched twice (one as164 // CallExpr and one as DeclRefExpr), we record all DeclRefExpr's that have165 // 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 updated168 // when changing namespaces around them.169 std::vector<llvm::Regex> AllowedSymbolRegexes;170};171 172} // namespace change_namespace173} // namespace clang174 175#endif // LLVM_CLANG_TOOLS_EXTRA_CHANGE_NAMESPACE_CHANGENAMESPACE_H176