summaryrefslogtreecommitdiff
path: root/clang-tools-extra/clang-move/Move.h
blob: ea241bbbc4f8a0b5d9d8fc01295ff9e0c9d870c6 (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
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
//===-- Move.h - Clang move  ----------------------------------------------===//
//
// 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_CLANG_MOVE_CLANGMOVE_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_MOVE_CLANGMOVE_H

#include "HelperDeclRefGraph.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Frontend/FrontendAction.h"
#include "clang/Tooling/Core/Replacement.h"
#include "clang/Tooling/Tooling.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringRef.h"
#include <map>
#include <memory>
#include <string>
#include <vector>

namespace clang {
namespace move {

// A reporter which collects and reports declarations in old header.
class DeclarationReporter {
public:
  DeclarationReporter() = default;
  ~DeclarationReporter() = default;

  void reportDeclaration(llvm::StringRef DeclarationName, llvm::StringRef Type,
                         bool Templated) {
    DeclarationList.emplace_back(DeclarationName, Type, Templated);
  };

  struct Declaration {
    Declaration(llvm::StringRef QName, llvm::StringRef Kind, bool Templated)
        : QualifiedName(QName), Kind(Kind), Templated(Templated) {}

    friend bool operator==(const Declaration &LHS, const Declaration &RHS) {
      return std::tie(LHS.QualifiedName, LHS.Kind, LHS.Templated) ==
             std::tie(RHS.QualifiedName, RHS.Kind, RHS.Templated);
    }
    std::string QualifiedName; // E.g. A::B::Foo.
    std::string Kind;          // E.g. Function, Class
    bool Templated = false;    // Whether the declaration is templated.
  };

  const std::vector<Declaration> getDeclarationList() const {
    return DeclarationList;
  }

private:
  std::vector<Declaration> DeclarationList;
};

// Specify declarations being moved. It contains all information of the moved
// declarations.
struct MoveDefinitionSpec {
  // The list of fully qualified names, e.g. Foo, a::Foo, b::Foo.
  SmallVector<std::string, 4> Names;
  // The file path of old header, can be relative path and absolute path.
  std::string OldHeader;
  // The file path of old cc, can be relative path and absolute path.
  std::string OldCC;
  // The file path of new header, can be relative path and absolute path.
  std::string NewHeader;
  // The file path of new cc, can be relative path and absolute path.
  std::string NewCC;
  // Whether old.h depends on new.h. If true, #include "new.h" will be added
  // in old.h.
  bool OldDependOnNew = false;
  // Whether new.h depends on old.h. If true, #include "old.h" will be added
  // in new.h.
  bool NewDependOnOld = false;
};

// A Context which contains extra options which are used in ClangMoveTool.
struct ClangMoveContext {
  MoveDefinitionSpec Spec;
  // The Key is file path, value is the replacements being applied to the file.
  std::map<std::string, tooling::Replacements> &FileToReplacements;
  // The original working directory where the local clang-move binary runs.
  //
  // clang-move will change its current working directory to the build
  // directory when analyzing the source file. We save the original working
  // directory in order to get the absolute file path for the fields in Spec.
  std::string OriginalRunningDirectory;
  // The name of a predefined code style.
  std::string FallbackStyle;
  // Whether dump all declarations in old header.
  bool DumpDeclarations;
};

// This tool is used to move class/function definitions from the given source
// files (old.h/cc) to new files (new.h/cc).
// The goal of this tool is to make the new/old files as compilable as possible.
//
// When moving a symbol,all used helper declarations (e.g. static
// functions/variables definitions in global/named namespace,
// functions/variables/classes definitions in anonymous namespace) used by the
// moved symbol in old.cc are moved to the new.cc. In addition, all
// using-declarations in old.cc are also moved to new.cc; forward class
// declarations in old.h are also moved to new.h.
//
// The remaining helper declarations which are unused by non-moved symbols in
// old.cc will be removed.
//
// Note: When all declarations in old header are being moved, all code in
// old.h/cc will be moved, which means old.h/cc are empty. This ignores symbols
// that are not supported (e.g. typedef and enum) so that we always move old
// files to new files when all symbols produced from dump_decls are moved.
class ClangMoveTool : public ast_matchers::MatchFinder::MatchCallback {
public:
  ClangMoveTool(ClangMoveContext *const Context,
                DeclarationReporter *const Reporter);

  void registerMatchers(ast_matchers::MatchFinder *Finder);

  void run(const ast_matchers::MatchFinder::MatchResult &Result) override;

  void onEndOfTranslationUnit() override;

  /// Add #includes from old.h/cc files.
  ///
  /// \param IncludeHeader The name of the file being included, as written in
  /// the source code.
  /// \param IsAngled Whether the file name was enclosed in angle brackets.
  /// \param SearchPath The search path which was used to find the IncludeHeader
  /// in the file system. It can be a relative path or an absolute path.
  /// \param FileName The name of file where the IncludeHeader comes from.
  /// \param IncludeFilenameRange The source range for the written file name in
  /// #include (i.e. "old.h" for #include "old.h") in old.cc.
  /// \param SM The SourceManager.
  void addIncludes(llvm::StringRef IncludeHeader, bool IsAngled,
                   llvm::StringRef SearchPath, llvm::StringRef FileName,
                   clang::CharSourceRange IncludeFilenameRange,
                   const SourceManager &SM);

  std::vector<const NamedDecl *> &getMovedDecls() { return MovedDecls; }

  /// Add declarations being removed from old.h/cc. For each declarations, the
  /// method also records the mapping relationship between the corresponding
  /// FilePath and its FileID.
  void addRemovedDecl(const NamedDecl *Decl);

  llvm::SmallPtrSet<const NamedDecl *, 8> &getUnremovedDeclsInOldHeader() {
    return UnremovedDeclsInOldHeader;
  }

private:
  // Make the Path absolute using the OrignalRunningDirectory if the Path is not
  // an absolute path. An empty Path will result in an empty string.
  std::string makeAbsolutePath(StringRef Path);

  void removeDeclsInOldFiles();
  void moveDeclsToNewFiles();
  void moveAll(SourceManager& SM, StringRef OldFile, StringRef NewFile);

  // Stores all MatchCallbacks created by this tool.
  std::vector<std::unique_ptr<ast_matchers::MatchFinder::MatchCallback>>
      MatchCallbacks;
  // Store all potential declarations (decls being moved, forward decls) that
  // might need to move to new.h/cc. It includes all helper declarations
  // (include unused ones) by default. The unused ones will be filtered out in
  // the last stage. Saving in an AST-visited order.
  std::vector<const NamedDecl *> MovedDecls;
  // The declarations that needs to be removed in old.cc/h.
  std::vector<const NamedDecl *> RemovedDecls;
  // The #includes in old_header.h.
  std::vector<std::string> HeaderIncludes;
  // The #includes in old_cc.cc.
  std::vector<std::string> CCIncludes;
  // Records all helper declarations (function/variable/class definitions in
  // anonymous namespaces, static function/variable definitions in global/named
  // namespaces) in old.cc. saving in an AST-visited order.
  std::vector<const NamedDecl *> HelperDeclarations;
  // The unmoved named declarations in old header.
  llvm::SmallPtrSet<const NamedDecl*, 8> UnremovedDeclsInOldHeader;
  /// The source range for the written file name in #include (i.e. "old.h" for
  /// #include "old.h") in old.cc,  including the enclosing quotes or angle
  /// brackets.
  clang::CharSourceRange OldHeaderIncludeRangeInCC;
  /// The source range for the written file name in #include (i.e. "old.h" for
  /// #include "old.h") in old.h,  including the enclosing quotes or angle
  /// brackets.
  clang::CharSourceRange OldHeaderIncludeRangeInHeader;
  /// Mapping from FilePath to FileID, which can be used in post processes like
  /// cleanup around replacements.
  llvm::StringMap<FileID> FilePathToFileID;
  /// A context contains all running options. It is not owned.
  ClangMoveContext *const Context;
  /// A reporter to report all declarations from old header. It is not owned.
  DeclarationReporter *const Reporter;
  /// Builder for helper declarations reference graph.
  HelperDeclRGBuilder RGBuilder;
};

class ClangMoveAction : public clang::ASTFrontendAction {
public:
  ClangMoveAction(ClangMoveContext *const Context,
                  DeclarationReporter *const Reporter)
      : MoveTool(Context, Reporter) {
    MoveTool.registerMatchers(&MatchFinder);
  }

  ~ClangMoveAction() override = default;

  std::unique_ptr<clang::ASTConsumer>
  CreateASTConsumer(clang::CompilerInstance &Compiler,
                    llvm::StringRef InFile) override;

private:
  ast_matchers::MatchFinder MatchFinder;
  ClangMoveTool MoveTool;
};

class ClangMoveActionFactory : public tooling::FrontendActionFactory {
public:
  ClangMoveActionFactory(ClangMoveContext *const Context,
                         DeclarationReporter *const Reporter = nullptr)
      : Context(Context), Reporter(Reporter) {}

  std::unique_ptr<clang::FrontendAction> create() override {
    return std::make_unique<ClangMoveAction>(Context, Reporter);
  }

private:
  // Not owned.
  ClangMoveContext *const Context;
  DeclarationReporter *const Reporter;
};

} // namespace move
} // namespace clang

#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_MOVE_CLANGMOVE_H