reference, declarationdefinition
definition → references, declarations, derived classes, virtual overrides
reference to multiple definitions → definitions
unreferenced
    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