//===-- 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 #include #include #include 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 getDeclarationList() const { return DeclarationList; } private: std::vector 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 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 &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 &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 &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> 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 MovedDecls; // The declarations that needs to be removed in old.cc/h. std::vector RemovedDecls; // The #includes in old_header.h. std::vector HeaderIncludes; // The #includes in old_cc.cc. std::vector 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 HelperDeclarations; // The unmoved named declarations in old header. llvm::SmallPtrSet 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 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 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 create() override { return std::make_unique(Context, Reporter); } private: // Not owned. ClangMoveContext *const Context; DeclarationReporter *const Reporter; }; } // namespace move } // namespace clang #endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_MOVE_CLANGMOVE_H