1 //===-- ChangeNamespace.h -- Change namespace ------------------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
9 #ifndef LLVM_CLANG_TOOLS_EXTRA_CHANGE_NAMESPACE_CHANGENAMESPACE_H
10 #define LLVM_CLANG_TOOLS_EXTRA_CHANGE_NAMESPACE_CHANGENAMESPACE_H
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"
19 namespace change_namespace
{
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
32 // For example, changing "a" to "x":
36 // class A { FWD *fwd; }
43 // class A { ::a::FWD *fwd; }
45 // FIXME: support moving typedef, enums across namespaces.
46 class ChangeNamespaceTool
: public ast_matchers::MatchFinder::MatchCallback
{
48 // Moves code in the old namespace `OldNs` to the new namespace `NewNs` in
49 // files matching `FilePattern`.
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");
56 void registerMatchers(ast_matchers::MatchFinder
*Finder
);
58 void run(const ast_matchers::MatchFinder::MatchResult
&Result
) override
;
60 // Moves the changed code in old namespaces but leaves class forward
61 // declarations behind.
62 void onEndOfTranslationUnit() override
;
65 void moveOldNamespace(const ast_matchers::MatchFinder::MatchResult
&Result
,
66 const NamespaceDecl
*NsDecl
);
68 void moveClassForwardDeclaration(
69 const ast_matchers::MatchFinder::MatchResult
&Result
,
70 const NamedDecl
*FwdDecl
);
72 void replaceQualifiedSymbolInDeclContext(
73 const ast_matchers::MatchFinder::MatchResult
&Result
,
74 const DeclContext
*DeclContext
, SourceLocation Start
, SourceLocation End
,
75 const NamedDecl
*FromDecl
);
77 void fixTypeLoc(const ast_matchers::MatchFinder::MatchResult
&Result
,
78 SourceLocation Start
, SourceLocation End
, TypeLoc Type
);
80 void fixUsingShadowDecl(const ast_matchers::MatchFinder::MatchResult
&Result
,
81 const UsingDecl
*UsingDeclaration
);
83 void fixDeclRefExpr(const ast_matchers::MatchFinder::MatchResult
&Result
,
84 const DeclContext
*UseContext
, const NamedDecl
*From
,
85 const DeclRefExpr
*Ref
);
87 // Information about moving an old namespace.
88 struct MoveNamespace
{
89 // The start offset of the namespace block being moved in the original
92 // The length of the namespace block in the original code.
94 // The offset at which the new namespace block will be inserted in the
96 unsigned InsertionOffset
;
97 // The file in which the namespace is declared.
99 SourceManager
*SourceMgr
;
102 // Information about inserting a class forward declaration.
103 struct InsertForwardDeclaration
{
104 // The offset at while the forward declaration will be inserted in the
106 unsigned InsertionOffset
;
107 // The code to be inserted.
108 std::string ForwardDeclText
;
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
119 std::map
<std::string
, tooling::Replacements
> &FileToReplacements
;
120 // A fully qualified name of the old namespace without "::" prefix, e.g.
122 std::string OldNamespace
;
123 // A fully qualified name of the new namespace without "::" prefix, e.g.
125 std::string NewNamespace
;
126 // The longest suffix in the old namespace that does not overlap the new
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
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
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
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
;
172 } // namespace change_namespace
175 #endif // LLVM_CLANG_TOOLS_EXTRA_CHANGE_NAMESPACE_CHANGENAMESPACE_H