//===-- ChangeNamespace.h -- Change namespace ------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #ifndef LLVM_CLANG_TOOLS_EXTRA_CHANGE_NAMESPACE_CHANGENAMESPACE_H #define LLVM_CLANG_TOOLS_EXTRA_CHANGE_NAMESPACE_CHANGENAMESPACE_H #include "clang/ASTMatchers/ASTMatchFinder.h" #include "clang/Format/Format.h" #include "clang/Tooling/Core/Replacement.h" #include "llvm/Support/Regex.h" #include namespace clang { namespace change_namespace { // This tool can be used to change the surrounding namespaces of class/function // definitions. Classes/functions in the moved namespace will have new // namespaces while references to symbols (e.g. types, functions) which are not // defined in the changed namespace will be correctly qualified by prepending // namespace specifiers before them. // This will try to add shortest namespace specifiers possible. When a symbol // reference needs to be fully-qualified, this adds a "::" prefix to the // namespace specifiers unless the new namespace is the global namespace. // For classes, only classes that are declared/defined in the given namespace in // specified files will be moved: forward declarations will remain in the old // namespace. // For example, changing "a" to "x": // Old code: // namespace a { // class FWD; // class A { FWD *fwd; } // } // a // New code: // namespace a { // class FWD; // } // a // namespace x { // class A { ::a::FWD *fwd; } // } // x // FIXME: support moving typedef, enums across namespaces. class ChangeNamespaceTool : public ast_matchers::MatchFinder::MatchCallback { public: // Moves code in the old namespace `OldNs` to the new namespace `NewNs` in // files matching `FilePattern`. ChangeNamespaceTool( llvm::StringRef OldNs, llvm::StringRef NewNs, llvm::StringRef FilePattern, llvm::ArrayRef AllowedSymbolPatterns, std::map *FileToReplacements, llvm::StringRef FallbackStyle = "LLVM"); void registerMatchers(ast_matchers::MatchFinder *Finder); void run(const ast_matchers::MatchFinder::MatchResult &Result) override; // Moves the changed code in old namespaces but leaves class forward // declarations behind. void onEndOfTranslationUnit() override; private: void moveOldNamespace(const ast_matchers::MatchFinder::MatchResult &Result, const NamespaceDecl *NsDecl); void moveClassForwardDeclaration( const ast_matchers::MatchFinder::MatchResult &Result, const NamedDecl *FwdDecl); void replaceQualifiedSymbolInDeclContext( const ast_matchers::MatchFinder::MatchResult &Result, const DeclContext *DeclContext, SourceLocation Start, SourceLocation End, const NamedDecl *FromDecl); void fixTypeLoc(const ast_matchers::MatchFinder::MatchResult &Result, SourceLocation Start, SourceLocation End, TypeLoc Type); void fixUsingShadowDecl(const ast_matchers::MatchFinder::MatchResult &Result, const UsingDecl *UsingDeclaration); void fixDeclRefExpr(const ast_matchers::MatchFinder::MatchResult &Result, const DeclContext *UseContext, const NamedDecl *From, const DeclRefExpr *Ref); // Information about moving an old namespace. struct MoveNamespace { // The start offset of the namespace block being moved in the original // code. unsigned Offset; // The length of the namespace block in the original code. unsigned Length; // The offset at which the new namespace block will be inserted in the // original code. unsigned InsertionOffset; // The file in which the namespace is declared. FileID FID; SourceManager *SourceMgr; }; // Information about inserting a class forward declaration. struct InsertForwardDeclaration { // The offset at while the forward declaration will be inserted in the // original code. unsigned InsertionOffset; // The code to be inserted. std::string ForwardDeclText; }; std::string FallbackStyle; // In match callbacks, this contains replacements for replacing `typeLoc`s in // and deleting forward declarations in the moved namespace blocks. // In `onEndOfTranslationUnit` callback, the previous added replacements are // applied (on the moved namespace blocks), and then changed code in old // namespaces re moved to new namespaces, and previously deleted forward // declarations are inserted back to old namespaces, from which they are // deleted. std::map &FileToReplacements; // A fully qualified name of the old namespace without "::" prefix, e.g. // "a::b::c". std::string OldNamespace; // A fully qualified name of the new namespace without "::" prefix, e.g. // "x::y::z". std::string NewNamespace; // The longest suffix in the old namespace that does not overlap the new // namespace. // For example, if `OldNamespace` is "a::b::c" and `NewNamespace` is // "a::x::y", then `DiffOldNamespace` will be "b::c". std::string DiffOldNamespace; // The longest suffix in the new namespace that does not overlap the old // namespace. // For example, if `OldNamespace` is "a::b::c" and `NewNamespace` is // "a::x::y", then `DiffNewNamespace` will be "x::y". std::string DiffNewNamespace; // A regex pattern that matches files to be processed. std::string FilePattern; llvm::Regex FilePatternRE; // Information about moved namespaces grouped by file. // Since we are modifying code in old namespaces (e.g. add namespace // specifiers) as well as moving them, we store information about namespaces // to be moved and only move them after all modifications are finished (i.e. // in `onEndOfTranslationUnit`). std::map> MoveNamespaces; // Information about forward declaration insertions grouped by files. // A class forward declaration is not moved, so it will be deleted from the // moved code block and inserted back into the old namespace. The insertion // will be done after removing the code from the old namespace and before // inserting it to the new namespace. std::map> InsertFwdDecls; // Records all using declarations, which can be used to shorten namespace // specifiers. llvm::SmallPtrSet UsingDecls; // Records all using namespace declarations, which can be used to shorten // namespace specifiers. llvm::SmallPtrSet UsingNamespaceDecls; // Records all namespace alias declarations, which can be used to shorten // namespace specifiers. llvm::SmallPtrSet NamespaceAliasDecls; // TypeLocs of CXXCtorInitializer. Types of CXXCtorInitializers do not need to // be fixed. llvm::SmallVector BaseCtorInitializerTypeLocs; // Since a DeclRefExpr for a function call can be matched twice (one as // CallExpr and one as DeclRefExpr), we record all DeclRefExpr's that have // been processed so that we don't handle them twice. llvm::SmallPtrSet ProcessedFuncRefs; // Patterns of symbol names whose references are not expected to be updated // when changing namespaces around them. std::vector AllowedSymbolRegexes; }; } // namespace change_namespace } // namespace clang #endif // LLVM_CLANG_TOOLS_EXTRA_CHANGE_NAMESPACE_CHANGENAMESPACE_H