1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
|
//===-- 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 <string>
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<std::string> AllowedSymbolPatterns,
std::map<std::string, tooling::Replacements> *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<std::string, tooling::Replacements> &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<std::string, std::vector<MoveNamespace>> 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<std::string, std::vector<InsertForwardDeclaration>> InsertFwdDecls;
// Records all using declarations, which can be used to shorten namespace
// specifiers.
llvm::SmallPtrSet<const UsingDecl *, 8> UsingDecls;
// Records all using namespace declarations, which can be used to shorten
// namespace specifiers.
llvm::SmallPtrSet<const UsingDirectiveDecl *, 8> UsingNamespaceDecls;
// Records all namespace alias declarations, which can be used to shorten
// namespace specifiers.
llvm::SmallPtrSet<const NamespaceAliasDecl *, 8> NamespaceAliasDecls;
// TypeLocs of CXXCtorInitializer. Types of CXXCtorInitializers do not need to
// be fixed.
llvm::SmallVector<TypeLoc, 8> 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<const clang::DeclRefExpr*, 16> ProcessedFuncRefs;
// Patterns of symbol names whose references are not expected to be updated
// when changing namespaces around them.
std::vector<llvm::Regex> AllowedSymbolRegexes;
};
} // namespace change_namespace
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CHANGE_NAMESPACE_CHANGENAMESPACE_H
|