summaryrefslogtreecommitdiffstats
path: root/clang-change-namespace/ChangeNamespace.h
blob: 293d5ce83433898834327e418aab69c4864e36e2 (plain)
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
// speficifed 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> WhiteListedSymbolPatterns,
      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
  // spedifiers) 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> WhiteListedSymbolRegexes;
};

} // namespace change_namespace
} // namespace clang

#endif // LLVM_CLANG_TOOLS_EXTRA_CHANGE_NAMESPACE_CHANGENAMESPACE_H