171ebc9ebSNico Weber //===-- Move.cpp - Implement ClangMove functationalities --------*- C++ -*-===//
271ebc9ebSNico Weber //
371ebc9ebSNico Weber // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
471ebc9ebSNico Weber // See https://llvm.org/LICENSE.txt for license information.
571ebc9ebSNico Weber // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
671ebc9ebSNico Weber //
771ebc9ebSNico Weber //===----------------------------------------------------------------------===//
871ebc9ebSNico Weber
971ebc9ebSNico Weber #include "Move.h"
1071ebc9ebSNico Weber #include "HelperDeclRefGraph.h"
1171ebc9ebSNico Weber #include "clang/ASTMatchers/ASTMatchers.h"
1271ebc9ebSNico Weber #include "clang/Basic/SourceManager.h"
1371ebc9ebSNico Weber #include "clang/Format/Format.h"
1471ebc9ebSNico Weber #include "clang/Frontend/CompilerInstance.h"
1571ebc9ebSNico Weber #include "clang/Lex/Lexer.h"
1671ebc9ebSNico Weber #include "clang/Lex/Preprocessor.h"
1771ebc9ebSNico Weber #include "clang/Rewrite/Core/Rewriter.h"
1871ebc9ebSNico Weber #include "clang/Tooling/Core/Replacement.h"
1971ebc9ebSNico Weber #include "llvm/Support/Debug.h"
2071ebc9ebSNico Weber #include "llvm/Support/Path.h"
2171ebc9ebSNico Weber
2271ebc9ebSNico Weber #define DEBUG_TYPE "clang-move"
2371ebc9ebSNico Weber
2471ebc9ebSNico Weber using namespace clang::ast_matchers;
2571ebc9ebSNico Weber
2671ebc9ebSNico Weber namespace clang {
2771ebc9ebSNico Weber namespace move {
2871ebc9ebSNico Weber namespace {
2971ebc9ebSNico Weber
3071ebc9ebSNico Weber // FIXME: Move to ASTMatchers.
AST_MATCHER(VarDecl,isStaticDataMember)3171ebc9ebSNico Weber AST_MATCHER(VarDecl, isStaticDataMember) { return Node.isStaticDataMember(); }
3271ebc9ebSNico Weber
AST_MATCHER(NamedDecl,notInMacro)3371ebc9ebSNico Weber AST_MATCHER(NamedDecl, notInMacro) { return !Node.getLocation().isMacroID(); }
3471ebc9ebSNico Weber
AST_MATCHER_P(Decl,hasOutermostEnclosingClass,ast_matchers::internal::Matcher<Decl>,InnerMatcher)3571ebc9ebSNico Weber AST_MATCHER_P(Decl, hasOutermostEnclosingClass,
3671ebc9ebSNico Weber ast_matchers::internal::Matcher<Decl>, InnerMatcher) {
3771ebc9ebSNico Weber const auto *Context = Node.getDeclContext();
3871ebc9ebSNico Weber if (!Context)
3971ebc9ebSNico Weber return false;
4071ebc9ebSNico Weber while (const auto *NextContext = Context->getParent()) {
4171ebc9ebSNico Weber if (isa<NamespaceDecl>(NextContext) ||
4271ebc9ebSNico Weber isa<TranslationUnitDecl>(NextContext))
4371ebc9ebSNico Weber break;
4471ebc9ebSNico Weber Context = NextContext;
4571ebc9ebSNico Weber }
4671ebc9ebSNico Weber return InnerMatcher.matches(*Decl::castFromDeclContext(Context), Finder,
4771ebc9ebSNico Weber Builder);
4871ebc9ebSNico Weber }
4971ebc9ebSNico Weber
AST_MATCHER_P(CXXMethodDecl,ofOutermostEnclosingClass,ast_matchers::internal::Matcher<CXXRecordDecl>,InnerMatcher)5071ebc9ebSNico Weber AST_MATCHER_P(CXXMethodDecl, ofOutermostEnclosingClass,
5171ebc9ebSNico Weber ast_matchers::internal::Matcher<CXXRecordDecl>, InnerMatcher) {
5271ebc9ebSNico Weber const CXXRecordDecl *Parent = Node.getParent();
5371ebc9ebSNico Weber if (!Parent)
5471ebc9ebSNico Weber return false;
5571ebc9ebSNico Weber while (const auto *NextParent =
5671ebc9ebSNico Weber dyn_cast<CXXRecordDecl>(Parent->getParent())) {
5771ebc9ebSNico Weber Parent = NextParent;
5871ebc9ebSNico Weber }
5971ebc9ebSNico Weber
6071ebc9ebSNico Weber return InnerMatcher.matches(*Parent, Finder, Builder);
6171ebc9ebSNico Weber }
6271ebc9ebSNico Weber
CleanPath(StringRef PathRef)6371ebc9ebSNico Weber std::string CleanPath(StringRef PathRef) {
6471ebc9ebSNico Weber llvm::SmallString<128> Path(PathRef);
6571ebc9ebSNico Weber llvm::sys::path::remove_dots(Path, /*remove_dot_dot=*/true);
6671ebc9ebSNico Weber // FIXME: figure out why this is necessary.
6771ebc9ebSNico Weber llvm::sys::path::native(Path);
682b00d449SKazu Hirata return std::string(Path);
6971ebc9ebSNico Weber }
7071ebc9ebSNico Weber
7171ebc9ebSNico Weber // Make the Path absolute using the CurrentDir if the Path is not an absolute
7271ebc9ebSNico Weber // path. An empty Path will result in an empty string.
MakeAbsolutePath(StringRef CurrentDir,StringRef Path)7371ebc9ebSNico Weber std::string MakeAbsolutePath(StringRef CurrentDir, StringRef Path) {
7471ebc9ebSNico Weber if (Path.empty())
7571ebc9ebSNico Weber return "";
7671ebc9ebSNico Weber llvm::SmallString<128> InitialDirectory(CurrentDir);
7771ebc9ebSNico Weber llvm::SmallString<128> AbsolutePath(Path);
7871ebc9ebSNico Weber llvm::sys::fs::make_absolute(InitialDirectory, AbsolutePath);
7971ebc9ebSNico Weber return CleanPath(std::move(AbsolutePath));
8071ebc9ebSNico Weber }
8171ebc9ebSNico Weber
8271ebc9ebSNico Weber // Make the Path absolute using the current working directory of the given
8371ebc9ebSNico Weber // SourceManager if the Path is not an absolute path.
8471ebc9ebSNico Weber //
8571ebc9ebSNico Weber // The Path can be a path relative to the build directory, or retrieved from
8671ebc9ebSNico Weber // the SourceManager.
MakeAbsolutePath(const SourceManager & SM,StringRef Path)8771ebc9ebSNico Weber std::string MakeAbsolutePath(const SourceManager &SM, StringRef Path) {
8871ebc9ebSNico Weber llvm::SmallString<128> AbsolutePath(Path);
8971ebc9ebSNico Weber if (std::error_code EC =
90db8a7422SDuncan P. N. Exon Smith SM.getFileManager().getVirtualFileSystem().makeAbsolute(AbsolutePath))
9171ebc9ebSNico Weber llvm::errs() << "Warning: could not make absolute file: '" << EC.message()
9271ebc9ebSNico Weber << '\n';
9371ebc9ebSNico Weber // Handle symbolic link path cases.
9471ebc9ebSNico Weber // We are trying to get the real file path of the symlink.
951dee56aeSJan Svoboda auto Dir = SM.getFileManager().getOptionalDirectoryRef(
9671ebc9ebSNico Weber llvm::sys::path::parent_path(AbsolutePath.str()));
9771ebc9ebSNico Weber if (Dir) {
98a02f8576SHarlan Haskins StringRef DirName = SM.getFileManager().getCanonicalName(*Dir);
9971ebc9ebSNico Weber // FIXME: getCanonicalName might fail to get real path on VFS.
10071ebc9ebSNico Weber if (llvm::sys::path::is_absolute(DirName)) {
10171ebc9ebSNico Weber SmallString<128> AbsoluteFilename;
10271ebc9ebSNico Weber llvm::sys::path::append(AbsoluteFilename, DirName,
10371ebc9ebSNico Weber llvm::sys::path::filename(AbsolutePath.str()));
10471ebc9ebSNico Weber return CleanPath(AbsoluteFilename);
10571ebc9ebSNico Weber }
10671ebc9ebSNico Weber }
10771ebc9ebSNico Weber return CleanPath(AbsolutePath);
10871ebc9ebSNico Weber }
10971ebc9ebSNico Weber
11071ebc9ebSNico Weber // Matches AST nodes that are expanded within the given AbsoluteFilePath.
AST_POLYMORPHIC_MATCHER_P(isExpansionInFile,AST_POLYMORPHIC_SUPPORTED_TYPES (Decl,Stmt,TypeLoc),std::string,AbsoluteFilePath)11171ebc9ebSNico Weber AST_POLYMORPHIC_MATCHER_P(isExpansionInFile,
11271ebc9ebSNico Weber AST_POLYMORPHIC_SUPPORTED_TYPES(Decl, Stmt, TypeLoc),
11371ebc9ebSNico Weber std::string, AbsoluteFilePath) {
11471ebc9ebSNico Weber auto &SourceManager = Finder->getASTContext().getSourceManager();
11571ebc9ebSNico Weber auto ExpansionLoc = SourceManager.getExpansionLoc(Node.getBeginLoc());
11671ebc9ebSNico Weber if (ExpansionLoc.isInvalid())
11771ebc9ebSNico Weber return false;
118797cad9dSJan Svoboda auto FileEntry =
119797cad9dSJan Svoboda SourceManager.getFileEntryRefForID(SourceManager.getFileID(ExpansionLoc));
12071ebc9ebSNico Weber if (!FileEntry)
12171ebc9ebSNico Weber return false;
12271ebc9ebSNico Weber return MakeAbsolutePath(SourceManager, FileEntry->getName()) ==
12371ebc9ebSNico Weber AbsoluteFilePath;
12471ebc9ebSNico Weber }
12571ebc9ebSNico Weber
12671ebc9ebSNico Weber class FindAllIncludes : public PPCallbacks {
12771ebc9ebSNico Weber public:
FindAllIncludes(SourceManager * SM,ClangMoveTool * const MoveTool)12871ebc9ebSNico Weber explicit FindAllIncludes(SourceManager *SM, ClangMoveTool *const MoveTool)
12971ebc9ebSNico Weber : SM(*SM), MoveTool(MoveTool) {}
13071ebc9ebSNico Weber
InclusionDirective(SourceLocation HashLoc,const Token &,StringRef FileName,bool IsAngled,CharSourceRange FilenameRange,OptionalFileEntryRef,StringRef SearchPath,StringRef,const Module *,bool,SrcMgr::CharacteristicKind)13171ebc9ebSNico Weber void InclusionDirective(SourceLocation HashLoc, const Token & /*IncludeTok*/,
13271ebc9ebSNico Weber StringRef FileName, bool IsAngled,
13371ebc9ebSNico Weber CharSourceRange FilenameRange,
134854c10f8SBenjamin Kramer OptionalFileEntryRef /*File*/, StringRef SearchPath,
135205c0589SKrzysztof Parzyszek StringRef /*RelativePath*/,
136*da95d926SJan Svoboda const Module * /*SuggestedModule*/,
137*da95d926SJan Svoboda bool /*ModuleImported*/,
13871ebc9ebSNico Weber SrcMgr::CharacteristicKind /*FileType*/) override {
139797cad9dSJan Svoboda if (auto FileEntry = SM.getFileEntryRefForID(SM.getFileID(HashLoc)))
14071ebc9ebSNico Weber MoveTool->addIncludes(FileName, IsAngled, SearchPath,
14171ebc9ebSNico Weber FileEntry->getName(), FilenameRange, SM);
14271ebc9ebSNico Weber }
14371ebc9ebSNico Weber
14471ebc9ebSNico Weber private:
14571ebc9ebSNico Weber const SourceManager &SM;
14671ebc9ebSNico Weber ClangMoveTool *const MoveTool;
14771ebc9ebSNico Weber };
14871ebc9ebSNico Weber
149dd5571d5SKazuaki Ishizaki /// Add a declaration being moved to new.h/cc. Note that the declaration will
15071ebc9ebSNico Weber /// also be deleted in old.h/cc.
MoveDeclFromOldFileToNewFile(ClangMoveTool * MoveTool,const NamedDecl * D)15171ebc9ebSNico Weber void MoveDeclFromOldFileToNewFile(ClangMoveTool *MoveTool, const NamedDecl *D) {
15271ebc9ebSNico Weber MoveTool->getMovedDecls().push_back(D);
15371ebc9ebSNico Weber MoveTool->addRemovedDecl(D);
15471ebc9ebSNico Weber MoveTool->getUnremovedDeclsInOldHeader().erase(D);
15571ebc9ebSNico Weber }
15671ebc9ebSNico Weber
15771ebc9ebSNico Weber class FunctionDeclarationMatch : public MatchFinder::MatchCallback {
15871ebc9ebSNico Weber public:
FunctionDeclarationMatch(ClangMoveTool * MoveTool)15971ebc9ebSNico Weber explicit FunctionDeclarationMatch(ClangMoveTool *MoveTool)
16071ebc9ebSNico Weber : MoveTool(MoveTool) {}
16171ebc9ebSNico Weber
run(const MatchFinder::MatchResult & Result)16271ebc9ebSNico Weber void run(const MatchFinder::MatchResult &Result) override {
16371ebc9ebSNico Weber const auto *FD = Result.Nodes.getNodeAs<FunctionDecl>("function");
16471ebc9ebSNico Weber assert(FD);
16571ebc9ebSNico Weber const NamedDecl *D = FD;
16671ebc9ebSNico Weber if (const auto *FTD = FD->getDescribedFunctionTemplate())
16771ebc9ebSNico Weber D = FTD;
16871ebc9ebSNico Weber MoveDeclFromOldFileToNewFile(MoveTool, D);
16971ebc9ebSNico Weber }
17071ebc9ebSNico Weber
17171ebc9ebSNico Weber private:
17271ebc9ebSNico Weber ClangMoveTool *MoveTool;
17371ebc9ebSNico Weber };
17471ebc9ebSNico Weber
17571ebc9ebSNico Weber class VarDeclarationMatch : public MatchFinder::MatchCallback {
17671ebc9ebSNico Weber public:
VarDeclarationMatch(ClangMoveTool * MoveTool)17771ebc9ebSNico Weber explicit VarDeclarationMatch(ClangMoveTool *MoveTool)
17871ebc9ebSNico Weber : MoveTool(MoveTool) {}
17971ebc9ebSNico Weber
run(const MatchFinder::MatchResult & Result)18071ebc9ebSNico Weber void run(const MatchFinder::MatchResult &Result) override {
18171ebc9ebSNico Weber const auto *VD = Result.Nodes.getNodeAs<VarDecl>("var");
18271ebc9ebSNico Weber assert(VD);
18371ebc9ebSNico Weber MoveDeclFromOldFileToNewFile(MoveTool, VD);
18471ebc9ebSNico Weber }
18571ebc9ebSNico Weber
18671ebc9ebSNico Weber private:
18771ebc9ebSNico Weber ClangMoveTool *MoveTool;
18871ebc9ebSNico Weber };
18971ebc9ebSNico Weber
19071ebc9ebSNico Weber class TypeAliasMatch : public MatchFinder::MatchCallback {
19171ebc9ebSNico Weber public:
TypeAliasMatch(ClangMoveTool * MoveTool)19271ebc9ebSNico Weber explicit TypeAliasMatch(ClangMoveTool *MoveTool)
19371ebc9ebSNico Weber : MoveTool(MoveTool) {}
19471ebc9ebSNico Weber
run(const MatchFinder::MatchResult & Result)19571ebc9ebSNico Weber void run(const MatchFinder::MatchResult &Result) override {
19671ebc9ebSNico Weber if (const auto *TD = Result.Nodes.getNodeAs<TypedefDecl>("typedef"))
19771ebc9ebSNico Weber MoveDeclFromOldFileToNewFile(MoveTool, TD);
19871ebc9ebSNico Weber else if (const auto *TAD =
19971ebc9ebSNico Weber Result.Nodes.getNodeAs<TypeAliasDecl>("type_alias")) {
20071ebc9ebSNico Weber const NamedDecl * D = TAD;
20171ebc9ebSNico Weber if (const auto * TD = TAD->getDescribedAliasTemplate())
20271ebc9ebSNico Weber D = TD;
20371ebc9ebSNico Weber MoveDeclFromOldFileToNewFile(MoveTool, D);
20471ebc9ebSNico Weber }
20571ebc9ebSNico Weber }
20671ebc9ebSNico Weber
20771ebc9ebSNico Weber private:
20871ebc9ebSNico Weber ClangMoveTool *MoveTool;
20971ebc9ebSNico Weber };
21071ebc9ebSNico Weber
21171ebc9ebSNico Weber class EnumDeclarationMatch : public MatchFinder::MatchCallback {
21271ebc9ebSNico Weber public:
EnumDeclarationMatch(ClangMoveTool * MoveTool)21371ebc9ebSNico Weber explicit EnumDeclarationMatch(ClangMoveTool *MoveTool)
21471ebc9ebSNico Weber : MoveTool(MoveTool) {}
21571ebc9ebSNico Weber
run(const MatchFinder::MatchResult & Result)21671ebc9ebSNico Weber void run(const MatchFinder::MatchResult &Result) override {
21771ebc9ebSNico Weber const auto *ED = Result.Nodes.getNodeAs<EnumDecl>("enum");
21871ebc9ebSNico Weber assert(ED);
21971ebc9ebSNico Weber MoveDeclFromOldFileToNewFile(MoveTool, ED);
22071ebc9ebSNico Weber }
22171ebc9ebSNico Weber
22271ebc9ebSNico Weber private:
22371ebc9ebSNico Weber ClangMoveTool *MoveTool;
22471ebc9ebSNico Weber };
22571ebc9ebSNico Weber
22671ebc9ebSNico Weber class ClassDeclarationMatch : public MatchFinder::MatchCallback {
22771ebc9ebSNico Weber public:
ClassDeclarationMatch(ClangMoveTool * MoveTool)22871ebc9ebSNico Weber explicit ClassDeclarationMatch(ClangMoveTool *MoveTool)
22971ebc9ebSNico Weber : MoveTool(MoveTool) {}
run(const MatchFinder::MatchResult & Result)23071ebc9ebSNico Weber void run(const MatchFinder::MatchResult &Result) override {
23171ebc9ebSNico Weber SourceManager *SM = &Result.Context->getSourceManager();
23271ebc9ebSNico Weber if (const auto *CMD = Result.Nodes.getNodeAs<CXXMethodDecl>("class_method"))
23371ebc9ebSNico Weber MatchClassMethod(CMD, SM);
23471ebc9ebSNico Weber else if (const auto *VD =
23571ebc9ebSNico Weber Result.Nodes.getNodeAs<VarDecl>("class_static_var_decl"))
23671ebc9ebSNico Weber MatchClassStaticVariable(VD, SM);
23771ebc9ebSNico Weber else if (const auto *CD =
23871ebc9ebSNico Weber Result.Nodes.getNodeAs<CXXRecordDecl>("moved_class"))
23971ebc9ebSNico Weber MatchClassDeclaration(CD, SM);
24071ebc9ebSNico Weber }
24171ebc9ebSNico Weber
24271ebc9ebSNico Weber private:
MatchClassMethod(const CXXMethodDecl * CMD,SourceManager * SM)24371ebc9ebSNico Weber void MatchClassMethod(const CXXMethodDecl *CMD, SourceManager *SM) {
24471ebc9ebSNico Weber // Skip inline class methods. isInline() ast matcher doesn't ignore this
24571ebc9ebSNico Weber // case.
24671ebc9ebSNico Weber if (!CMD->isInlined()) {
24771ebc9ebSNico Weber MoveTool->getMovedDecls().push_back(CMD);
24871ebc9ebSNico Weber MoveTool->addRemovedDecl(CMD);
24971ebc9ebSNico Weber // Get template class method from its method declaration as
25071ebc9ebSNico Weber // UnremovedDecls stores template class method.
25171ebc9ebSNico Weber if (const auto *FTD = CMD->getDescribedFunctionTemplate())
25271ebc9ebSNico Weber MoveTool->getUnremovedDeclsInOldHeader().erase(FTD);
25371ebc9ebSNico Weber else
25471ebc9ebSNico Weber MoveTool->getUnremovedDeclsInOldHeader().erase(CMD);
25571ebc9ebSNico Weber }
25671ebc9ebSNico Weber }
25771ebc9ebSNico Weber
MatchClassStaticVariable(const NamedDecl * VD,SourceManager * SM)25871ebc9ebSNico Weber void MatchClassStaticVariable(const NamedDecl *VD, SourceManager *SM) {
25971ebc9ebSNico Weber MoveDeclFromOldFileToNewFile(MoveTool, VD);
26071ebc9ebSNico Weber }
26171ebc9ebSNico Weber
MatchClassDeclaration(const CXXRecordDecl * CD,SourceManager * SM)26271ebc9ebSNico Weber void MatchClassDeclaration(const CXXRecordDecl *CD, SourceManager *SM) {
26371ebc9ebSNico Weber // Get class template from its class declaration as UnremovedDecls stores
26471ebc9ebSNico Weber // class template.
26571ebc9ebSNico Weber if (const auto *TC = CD->getDescribedClassTemplate())
26671ebc9ebSNico Weber MoveTool->getMovedDecls().push_back(TC);
26771ebc9ebSNico Weber else
26871ebc9ebSNico Weber MoveTool->getMovedDecls().push_back(CD);
26971ebc9ebSNico Weber MoveTool->addRemovedDecl(MoveTool->getMovedDecls().back());
27071ebc9ebSNico Weber MoveTool->getUnremovedDeclsInOldHeader().erase(
27171ebc9ebSNico Weber MoveTool->getMovedDecls().back());
27271ebc9ebSNico Weber }
27371ebc9ebSNico Weber
27471ebc9ebSNico Weber ClangMoveTool *MoveTool;
27571ebc9ebSNico Weber };
27671ebc9ebSNico Weber
27771ebc9ebSNico Weber // Expand to get the end location of the line where the EndLoc of the given
27871ebc9ebSNico Weber // Decl.
getLocForEndOfDecl(const Decl * D,const LangOptions & LangOpts=LangOptions ())27971ebc9ebSNico Weber SourceLocation getLocForEndOfDecl(const Decl *D,
28071ebc9ebSNico Weber const LangOptions &LangOpts = LangOptions()) {
28171ebc9ebSNico Weber const auto &SM = D->getASTContext().getSourceManager();
28271ebc9ebSNico Weber // If the expansion range is a character range, this is the location of
28371ebc9ebSNico Weber // the first character past the end. Otherwise it's the location of the
28471ebc9ebSNico Weber // first character in the final token in the range.
28571ebc9ebSNico Weber auto EndExpansionLoc = SM.getExpansionRange(D->getEndLoc()).getEnd();
28671ebc9ebSNico Weber std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(EndExpansionLoc);
28771ebc9ebSNico Weber // Try to load the file buffer.
28871ebc9ebSNico Weber bool InvalidTemp = false;
28971ebc9ebSNico Weber llvm::StringRef File = SM.getBufferData(LocInfo.first, &InvalidTemp);
29071ebc9ebSNico Weber if (InvalidTemp)
29171ebc9ebSNico Weber return SourceLocation();
29271ebc9ebSNico Weber
29371ebc9ebSNico Weber const char *TokBegin = File.data() + LocInfo.second;
29471ebc9ebSNico Weber // Lex from the start of the given location.
29571ebc9ebSNico Weber Lexer Lex(SM.getLocForStartOfFile(LocInfo.first), LangOpts, File.begin(),
29671ebc9ebSNico Weber TokBegin, File.end());
29771ebc9ebSNico Weber
29871ebc9ebSNico Weber llvm::SmallVector<char, 16> Line;
29971ebc9ebSNico Weber // FIXME: this is a bit hacky to get ReadToEndOfLine work.
30071ebc9ebSNico Weber Lex.setParsingPreprocessorDirective(true);
30171ebc9ebSNico Weber Lex.ReadToEndOfLine(&Line);
30271ebc9ebSNico Weber SourceLocation EndLoc = EndExpansionLoc.getLocWithOffset(Line.size());
30371ebc9ebSNico Weber // If we already reach EOF, just return the EOF SourceLocation;
30471ebc9ebSNico Weber // otherwise, move 1 offset ahead to include the trailing newline character
30571ebc9ebSNico Weber // '\n'.
30671ebc9ebSNico Weber return SM.getLocForEndOfFile(LocInfo.first) == EndLoc
30771ebc9ebSNico Weber ? EndLoc
30871ebc9ebSNico Weber : EndLoc.getLocWithOffset(1);
30971ebc9ebSNico Weber }
31071ebc9ebSNico Weber
31171ebc9ebSNico Weber // Get full range of a Decl including the comments associated with it.
getFullRange(const Decl * D,const LangOptions & options=LangOptions ())31271ebc9ebSNico Weber CharSourceRange getFullRange(const Decl *D,
31371ebc9ebSNico Weber const LangOptions &options = LangOptions()) {
31471ebc9ebSNico Weber const auto &SM = D->getASTContext().getSourceManager();
31571ebc9ebSNico Weber SourceRange Full(SM.getExpansionLoc(D->getBeginLoc()), getLocForEndOfDecl(D));
31671ebc9ebSNico Weber // Expand to comments that are associated with the Decl.
31771ebc9ebSNico Weber if (const auto *Comment = D->getASTContext().getRawCommentForDeclNoCache(D)) {
31871ebc9ebSNico Weber if (SM.isBeforeInTranslationUnit(Full.getEnd(), Comment->getEndLoc()))
31971ebc9ebSNico Weber Full.setEnd(Comment->getEndLoc());
32071ebc9ebSNico Weber // FIXME: Don't delete a preceding comment, if there are no other entities
32171ebc9ebSNico Weber // it could refer to.
32271ebc9ebSNico Weber if (SM.isBeforeInTranslationUnit(Comment->getBeginLoc(), Full.getBegin()))
32371ebc9ebSNico Weber Full.setBegin(Comment->getBeginLoc());
32471ebc9ebSNico Weber }
32571ebc9ebSNico Weber
32671ebc9ebSNico Weber return CharSourceRange::getCharRange(Full);
32771ebc9ebSNico Weber }
32871ebc9ebSNico Weber
getDeclarationSourceText(const Decl * D)32971ebc9ebSNico Weber std::string getDeclarationSourceText(const Decl *D) {
33071ebc9ebSNico Weber const auto &SM = D->getASTContext().getSourceManager();
33171ebc9ebSNico Weber llvm::StringRef SourceText =
33271ebc9ebSNico Weber Lexer::getSourceText(getFullRange(D), SM, LangOptions());
33371ebc9ebSNico Weber return SourceText.str();
33471ebc9ebSNico Weber }
33571ebc9ebSNico Weber
isInHeaderFile(const Decl * D,llvm::StringRef OriginalRunningDirectory,llvm::StringRef OldHeader)33671ebc9ebSNico Weber bool isInHeaderFile(const Decl *D, llvm::StringRef OriginalRunningDirectory,
33771ebc9ebSNico Weber llvm::StringRef OldHeader) {
33871ebc9ebSNico Weber const auto &SM = D->getASTContext().getSourceManager();
33971ebc9ebSNico Weber if (OldHeader.empty())
34071ebc9ebSNico Weber return false;
34171ebc9ebSNico Weber auto ExpansionLoc = SM.getExpansionLoc(D->getBeginLoc());
34271ebc9ebSNico Weber if (ExpansionLoc.isInvalid())
34371ebc9ebSNico Weber return false;
34471ebc9ebSNico Weber
345797cad9dSJan Svoboda if (auto FE = SM.getFileEntryRefForID(SM.getFileID(ExpansionLoc))) {
34671ebc9ebSNico Weber return MakeAbsolutePath(SM, FE->getName()) ==
34771ebc9ebSNico Weber MakeAbsolutePath(OriginalRunningDirectory, OldHeader);
34871ebc9ebSNico Weber }
34971ebc9ebSNico Weber
35071ebc9ebSNico Weber return false;
35171ebc9ebSNico Weber }
35271ebc9ebSNico Weber
getNamespaces(const Decl * D)35371ebc9ebSNico Weber std::vector<std::string> getNamespaces(const Decl *D) {
35471ebc9ebSNico Weber std::vector<std::string> Namespaces;
35571ebc9ebSNico Weber for (const auto *Context = D->getDeclContext(); Context;
35671ebc9ebSNico Weber Context = Context->getParent()) {
35771ebc9ebSNico Weber if (llvm::isa<TranslationUnitDecl>(Context) ||
35871ebc9ebSNico Weber llvm::isa<LinkageSpecDecl>(Context))
35971ebc9ebSNico Weber break;
36071ebc9ebSNico Weber
36171ebc9ebSNico Weber if (const auto *ND = llvm::dyn_cast<NamespaceDecl>(Context))
36271ebc9ebSNico Weber Namespaces.push_back(ND->getName().str());
36371ebc9ebSNico Weber }
36471ebc9ebSNico Weber std::reverse(Namespaces.begin(), Namespaces.end());
36571ebc9ebSNico Weber return Namespaces;
36671ebc9ebSNico Weber }
36771ebc9ebSNico Weber
36871ebc9ebSNico Weber tooling::Replacements
createInsertedReplacements(const std::vector<std::string> & Includes,const std::vector<const NamedDecl * > & Decls,llvm::StringRef FileName,bool IsHeader=false,StringRef OldHeaderInclude="")36971ebc9ebSNico Weber createInsertedReplacements(const std::vector<std::string> &Includes,
37071ebc9ebSNico Weber const std::vector<const NamedDecl *> &Decls,
37171ebc9ebSNico Weber llvm::StringRef FileName, bool IsHeader = false,
37271ebc9ebSNico Weber StringRef OldHeaderInclude = "") {
37371ebc9ebSNico Weber std::string NewCode;
37471ebc9ebSNico Weber std::string GuardName(FileName);
37571ebc9ebSNico Weber if (IsHeader) {
37671ebc9ebSNico Weber for (size_t i = 0; i < GuardName.size(); ++i) {
37771ebc9ebSNico Weber if (!isAlphanumeric(GuardName[i]))
37871ebc9ebSNico Weber GuardName[i] = '_';
37971ebc9ebSNico Weber }
38071ebc9ebSNico Weber GuardName = StringRef(GuardName).upper();
38171ebc9ebSNico Weber NewCode += "#ifndef " + GuardName + "\n";
38271ebc9ebSNico Weber NewCode += "#define " + GuardName + "\n\n";
38371ebc9ebSNico Weber }
38471ebc9ebSNico Weber
38571ebc9ebSNico Weber NewCode += OldHeaderInclude;
38671ebc9ebSNico Weber // Add #Includes.
38771ebc9ebSNico Weber for (const auto &Include : Includes)
38871ebc9ebSNico Weber NewCode += Include;
38971ebc9ebSNico Weber
39071ebc9ebSNico Weber if (!Includes.empty())
39171ebc9ebSNico Weber NewCode += "\n";
39271ebc9ebSNico Weber
39371ebc9ebSNico Weber // Add moved class definition and its related declarations. All declarations
39471ebc9ebSNico Weber // in same namespace are grouped together.
39571ebc9ebSNico Weber //
39671ebc9ebSNico Weber // Record namespaces where the current position is in.
39771ebc9ebSNico Weber std::vector<std::string> CurrentNamespaces;
39871ebc9ebSNico Weber for (const auto *MovedDecl : Decls) {
39971ebc9ebSNico Weber // The namespaces of the declaration being moved.
40071ebc9ebSNico Weber std::vector<std::string> DeclNamespaces = getNamespaces(MovedDecl);
40171ebc9ebSNico Weber auto CurrentIt = CurrentNamespaces.begin();
40271ebc9ebSNico Weber auto DeclIt = DeclNamespaces.begin();
40371ebc9ebSNico Weber // Skip the common prefix.
40471ebc9ebSNico Weber while (CurrentIt != CurrentNamespaces.end() &&
40571ebc9ebSNico Weber DeclIt != DeclNamespaces.end()) {
40671ebc9ebSNico Weber if (*CurrentIt != *DeclIt)
40771ebc9ebSNico Weber break;
40871ebc9ebSNico Weber ++CurrentIt;
40971ebc9ebSNico Weber ++DeclIt;
41071ebc9ebSNico Weber }
41171ebc9ebSNico Weber // Calculate the new namespaces after adding MovedDecl in CurrentNamespace,
41271ebc9ebSNico Weber // which is used for next iteration of this loop.
41371ebc9ebSNico Weber std::vector<std::string> NextNamespaces(CurrentNamespaces.begin(),
41471ebc9ebSNico Weber CurrentIt);
41571ebc9ebSNico Weber NextNamespaces.insert(NextNamespaces.end(), DeclIt, DeclNamespaces.end());
41671ebc9ebSNico Weber
41771ebc9ebSNico Weber
41871ebc9ebSNico Weber // End with CurrentNamespace.
41971ebc9ebSNico Weber bool HasEndCurrentNamespace = false;
42071ebc9ebSNico Weber auto RemainingSize = CurrentNamespaces.end() - CurrentIt;
42171ebc9ebSNico Weber for (auto It = CurrentNamespaces.rbegin(); RemainingSize > 0;
42271ebc9ebSNico Weber --RemainingSize, ++It) {
42371ebc9ebSNico Weber assert(It < CurrentNamespaces.rend());
42471ebc9ebSNico Weber NewCode += "} // namespace " + *It + "\n";
42571ebc9ebSNico Weber HasEndCurrentNamespace = true;
42671ebc9ebSNico Weber }
42771ebc9ebSNico Weber // Add trailing '\n' after the nested namespace definition.
42871ebc9ebSNico Weber if (HasEndCurrentNamespace)
42971ebc9ebSNico Weber NewCode += "\n";
43071ebc9ebSNico Weber
43171ebc9ebSNico Weber // If the moved declaration is not in CurrentNamespace, add extra namespace
43271ebc9ebSNico Weber // definitions.
43371ebc9ebSNico Weber bool IsInNewNamespace = false;
43471ebc9ebSNico Weber while (DeclIt != DeclNamespaces.end()) {
43571ebc9ebSNico Weber NewCode += "namespace " + *DeclIt + " {\n";
43671ebc9ebSNico Weber IsInNewNamespace = true;
43771ebc9ebSNico Weber ++DeclIt;
43871ebc9ebSNico Weber }
43971ebc9ebSNico Weber // If the moved declaration is in same namespace CurrentNamespace, add
44071ebc9ebSNico Weber // a preceeding `\n' before the moved declaration.
44171ebc9ebSNico Weber // FIXME: Don't add empty lines between using declarations.
44271ebc9ebSNico Weber if (!IsInNewNamespace)
44371ebc9ebSNico Weber NewCode += "\n";
44471ebc9ebSNico Weber NewCode += getDeclarationSourceText(MovedDecl);
44571ebc9ebSNico Weber CurrentNamespaces = std::move(NextNamespaces);
44671ebc9ebSNico Weber }
44771ebc9ebSNico Weber std::reverse(CurrentNamespaces.begin(), CurrentNamespaces.end());
44871ebc9ebSNico Weber for (const auto &NS : CurrentNamespaces)
44971ebc9ebSNico Weber NewCode += "} // namespace " + NS + "\n";
45071ebc9ebSNico Weber
45171ebc9ebSNico Weber if (IsHeader)
45271ebc9ebSNico Weber NewCode += "\n#endif // " + GuardName + "\n";
45371ebc9ebSNico Weber return tooling::Replacements(tooling::Replacement(FileName, 0, 0, NewCode));
45471ebc9ebSNico Weber }
45571ebc9ebSNico Weber
45671ebc9ebSNico Weber // Return a set of all decls which are used/referenced by the given Decls.
457dd5571d5SKazuaki Ishizaki // Specifically, given a class member declaration, this method will return all
45871ebc9ebSNico Weber // decls which are used by the whole class.
45971ebc9ebSNico Weber llvm::DenseSet<const Decl *>
getUsedDecls(const HelperDeclRefGraph * RG,const std::vector<const NamedDecl * > & Decls)46071ebc9ebSNico Weber getUsedDecls(const HelperDeclRefGraph *RG,
46171ebc9ebSNico Weber const std::vector<const NamedDecl *> &Decls) {
46271ebc9ebSNico Weber assert(RG);
46371ebc9ebSNico Weber llvm::DenseSet<const CallGraphNode *> Nodes;
46471ebc9ebSNico Weber for (const auto *D : Decls) {
46571ebc9ebSNico Weber auto Result = RG->getReachableNodes(
46671ebc9ebSNico Weber HelperDeclRGBuilder::getOutmostClassOrFunDecl(D));
46771ebc9ebSNico Weber Nodes.insert(Result.begin(), Result.end());
46871ebc9ebSNico Weber }
46971ebc9ebSNico Weber llvm::DenseSet<const Decl *> Results;
47071ebc9ebSNico Weber for (const auto *Node : Nodes)
47171ebc9ebSNico Weber Results.insert(Node->getDecl());
47271ebc9ebSNico Weber return Results;
47371ebc9ebSNico Weber }
47471ebc9ebSNico Weber
47571ebc9ebSNico Weber } // namespace
47671ebc9ebSNico Weber
47771ebc9ebSNico Weber std::unique_ptr<ASTConsumer>
CreateASTConsumer(CompilerInstance & Compiler,StringRef)47871ebc9ebSNico Weber ClangMoveAction::CreateASTConsumer(CompilerInstance &Compiler,
47971ebc9ebSNico Weber StringRef /*InFile*/) {
4801c705d9cSJonas Devlieghere Compiler.getPreprocessor().addPPCallbacks(std::make_unique<FindAllIncludes>(
48171ebc9ebSNico Weber &Compiler.getSourceManager(), &MoveTool));
48271ebc9ebSNico Weber return MatchFinder.newASTConsumer();
48371ebc9ebSNico Weber }
48471ebc9ebSNico Weber
ClangMoveTool(ClangMoveContext * const Context,DeclarationReporter * const Reporter)48571ebc9ebSNico Weber ClangMoveTool::ClangMoveTool(ClangMoveContext *const Context,
48671ebc9ebSNico Weber DeclarationReporter *const Reporter)
48771ebc9ebSNico Weber : Context(Context), Reporter(Reporter) {
48871ebc9ebSNico Weber if (!Context->Spec.NewHeader.empty())
48971ebc9ebSNico Weber CCIncludes.push_back("#include \"" + Context->Spec.NewHeader + "\"\n");
49071ebc9ebSNico Weber }
49171ebc9ebSNico Weber
addRemovedDecl(const NamedDecl * Decl)49271ebc9ebSNico Weber void ClangMoveTool::addRemovedDecl(const NamedDecl *Decl) {
49371ebc9ebSNico Weber const auto &SM = Decl->getASTContext().getSourceManager();
49471ebc9ebSNico Weber auto Loc = Decl->getLocation();
49571ebc9ebSNico Weber StringRef FilePath = SM.getFilename(Loc);
49671ebc9ebSNico Weber FilePathToFileID[FilePath] = SM.getFileID(Loc);
49771ebc9ebSNico Weber RemovedDecls.push_back(Decl);
49871ebc9ebSNico Weber }
49971ebc9ebSNico Weber
registerMatchers(ast_matchers::MatchFinder * Finder)50071ebc9ebSNico Weber void ClangMoveTool::registerMatchers(ast_matchers::MatchFinder *Finder) {
50171ebc9ebSNico Weber auto InOldHeader =
50271ebc9ebSNico Weber isExpansionInFile(makeAbsolutePath(Context->Spec.OldHeader));
50371ebc9ebSNico Weber auto InOldCC = isExpansionInFile(makeAbsolutePath(Context->Spec.OldCC));
50471ebc9ebSNico Weber auto InOldFiles = anyOf(InOldHeader, InOldCC);
50571ebc9ebSNico Weber auto classTemplateForwardDecls =
50671ebc9ebSNico Weber classTemplateDecl(unless(has(cxxRecordDecl(isDefinition()))));
50771ebc9ebSNico Weber auto ForwardClassDecls = namedDecl(
50871ebc9ebSNico Weber anyOf(cxxRecordDecl(unless(anyOf(isImplicit(), isDefinition()))),
50971ebc9ebSNico Weber classTemplateForwardDecls));
51071ebc9ebSNico Weber auto TopLevelDecl =
51171ebc9ebSNico Weber hasDeclContext(anyOf(namespaceDecl(), translationUnitDecl()));
51271ebc9ebSNico Weber
51371ebc9ebSNico Weber //============================================================================
51471ebc9ebSNico Weber // Matchers for old header
51571ebc9ebSNico Weber //============================================================================
51671ebc9ebSNico Weber // Match all top-level named declarations (e.g. function, variable, enum) in
51771ebc9ebSNico Weber // old header, exclude forward class declarations and namespace declarations.
51871ebc9ebSNico Weber //
51971ebc9ebSNico Weber // We consider declarations inside a class belongs to the class. So these
52071ebc9ebSNico Weber // declarations will be ignored.
52171ebc9ebSNico Weber auto AllDeclsInHeader = namedDecl(
52271ebc9ebSNico Weber unless(ForwardClassDecls), unless(namespaceDecl()),
52371ebc9ebSNico Weber unless(usingDirectiveDecl()), // using namespace decl.
52471ebc9ebSNico Weber notInMacro(),
52571ebc9ebSNico Weber InOldHeader,
52671ebc9ebSNico Weber hasParent(decl(anyOf(namespaceDecl(), translationUnitDecl()))),
52771ebc9ebSNico Weber hasDeclContext(decl(anyOf(namespaceDecl(), translationUnitDecl()))));
52871ebc9ebSNico Weber Finder->addMatcher(AllDeclsInHeader.bind("decls_in_header"), this);
52971ebc9ebSNico Weber
53071ebc9ebSNico Weber // Don't register other matchers when dumping all declarations in header.
53171ebc9ebSNico Weber if (Context->DumpDeclarations)
53271ebc9ebSNico Weber return;
53371ebc9ebSNico Weber
53471ebc9ebSNico Weber // Match forward declarations in old header.
53571ebc9ebSNico Weber Finder->addMatcher(namedDecl(ForwardClassDecls, InOldHeader).bind("fwd_decl"),
53671ebc9ebSNico Weber this);
53771ebc9ebSNico Weber
53871ebc9ebSNico Weber //============================================================================
53971ebc9ebSNico Weber // Matchers for old cc
54071ebc9ebSNico Weber //============================================================================
54171ebc9ebSNico Weber auto IsOldCCTopLevelDecl = allOf(
54271ebc9ebSNico Weber hasParent(decl(anyOf(namespaceDecl(), translationUnitDecl()))), InOldCC);
54371ebc9ebSNico Weber // Matching using decls/type alias decls which are in named/anonymous/global
54471ebc9ebSNico Weber // namespace, these decls are always copied to new.h/cc. Those in classes,
54571ebc9ebSNico Weber // functions are covered in other matchers.
54671ebc9ebSNico Weber Finder->addMatcher(namedDecl(anyOf(usingDecl(IsOldCCTopLevelDecl),
54771ebc9ebSNico Weber usingDirectiveDecl(unless(isImplicit()),
54871ebc9ebSNico Weber IsOldCCTopLevelDecl),
54971ebc9ebSNico Weber typeAliasDecl(IsOldCCTopLevelDecl)),
55071ebc9ebSNico Weber notInMacro())
55171ebc9ebSNico Weber .bind("using_decl"),
55271ebc9ebSNico Weber this);
55371ebc9ebSNico Weber
55471ebc9ebSNico Weber // Match static functions/variable definitions which are defined in named
55571ebc9ebSNico Weber // namespaces.
55641bbb875SNathan James SmallVector<std::string, 4> QualNames;
55741bbb875SNathan James QualNames.reserve(Context->Spec.Names.size());
55871ebc9ebSNico Weber for (StringRef SymbolName : Context->Spec.Names) {
55941bbb875SNathan James QualNames.push_back(("::" + SymbolName.trim().ltrim(':')).str());
56071ebc9ebSNico Weber }
56171ebc9ebSNico Weber
56241bbb875SNathan James if (QualNames.empty()) {
56371ebc9ebSNico Weber llvm::errs() << "No symbols being moved.\n";
56471ebc9ebSNico Weber return;
56571ebc9ebSNico Weber }
56641bbb875SNathan James
56741bbb875SNathan James ast_matchers::internal::Matcher<NamedDecl> HasAnySymbolNames =
56841bbb875SNathan James hasAnyName(SmallVector<StringRef, 4>(QualNames.begin(), QualNames.end()));
56941bbb875SNathan James
57071ebc9ebSNico Weber auto InMovedClass =
57141bbb875SNathan James hasOutermostEnclosingClass(cxxRecordDecl(HasAnySymbolNames));
57271ebc9ebSNico Weber
57371ebc9ebSNico Weber // Matchers for helper declarations in old.cc.
57471ebc9ebSNico Weber auto InAnonymousNS = hasParent(namespaceDecl(isAnonymous()));
57571ebc9ebSNico Weber auto NotInMovedClass= allOf(unless(InMovedClass), InOldCC);
57671ebc9ebSNico Weber auto IsOldCCHelper =
57771ebc9ebSNico Weber allOf(NotInMovedClass, anyOf(isStaticStorageClass(), InAnonymousNS));
57871ebc9ebSNico Weber // Match helper classes separately with helper functions/variables since we
57971ebc9ebSNico Weber // want to reuse these matchers in finding helpers usage below.
58071ebc9ebSNico Weber //
58171ebc9ebSNico Weber // There could be forward declarations usage for helpers, especially for
58271ebc9ebSNico Weber // classes and functions. We need include these forward declarations.
58371ebc9ebSNico Weber //
58471ebc9ebSNico Weber // Forward declarations for variable helpers will be excluded as these
58571ebc9ebSNico Weber // declarations (with "extern") are not supposed in cpp file.
58671ebc9ebSNico Weber auto HelperFuncOrVar =
58771ebc9ebSNico Weber namedDecl(notInMacro(), anyOf(functionDecl(IsOldCCHelper),
58871ebc9ebSNico Weber varDecl(isDefinition(), IsOldCCHelper)));
58971ebc9ebSNico Weber auto HelperClasses =
59071ebc9ebSNico Weber cxxRecordDecl(notInMacro(), NotInMovedClass, InAnonymousNS);
59171ebc9ebSNico Weber // Save all helper declarations in old.cc.
59271ebc9ebSNico Weber Finder->addMatcher(
59371ebc9ebSNico Weber namedDecl(anyOf(HelperFuncOrVar, HelperClasses)).bind("helper_decls"),
59471ebc9ebSNico Weber this);
59571ebc9ebSNico Weber
59671ebc9ebSNico Weber // Construct an AST-based call graph of helper declarations in old.cc.
59771ebc9ebSNico Weber // In the following matcheres, "dc" is a caller while "helper_decls" and
59871ebc9ebSNico Weber // "used_class" is a callee, so a new edge starting from caller to callee will
59971ebc9ebSNico Weber // be add in the graph.
60071ebc9ebSNico Weber //
60171ebc9ebSNico Weber // Find helper function/variable usages.
60271ebc9ebSNico Weber Finder->addMatcher(
60371ebc9ebSNico Weber declRefExpr(to(HelperFuncOrVar), hasAncestor(decl().bind("dc")))
60471ebc9ebSNico Weber .bind("func_ref"),
60571ebc9ebSNico Weber &RGBuilder);
60671ebc9ebSNico Weber // Find helper class usages.
60771ebc9ebSNico Weber Finder->addMatcher(
60871ebc9ebSNico Weber typeLoc(loc(recordType(hasDeclaration(HelperClasses.bind("used_class")))),
60971ebc9ebSNico Weber hasAncestor(decl().bind("dc"))),
61071ebc9ebSNico Weber &RGBuilder);
61171ebc9ebSNico Weber
61271ebc9ebSNico Weber //============================================================================
61371ebc9ebSNico Weber // Matchers for old files, including old.h/old.cc
61471ebc9ebSNico Weber //============================================================================
61571ebc9ebSNico Weber // Create a MatchCallback for class declarations.
6161c705d9cSJonas Devlieghere MatchCallbacks.push_back(std::make_unique<ClassDeclarationMatch>(this));
61771ebc9ebSNico Weber // Match moved class declarations.
61841bbb875SNathan James auto MovedClass =
61941bbb875SNathan James cxxRecordDecl(InOldFiles, HasAnySymbolNames, isDefinition(), TopLevelDecl)
62071ebc9ebSNico Weber .bind("moved_class");
62171ebc9ebSNico Weber Finder->addMatcher(MovedClass, MatchCallbacks.back().get());
62271ebc9ebSNico Weber // Match moved class methods (static methods included) which are defined
62371ebc9ebSNico Weber // outside moved class declaration.
62441bbb875SNathan James Finder->addMatcher(cxxMethodDecl(InOldFiles,
62541bbb875SNathan James ofOutermostEnclosingClass(HasAnySymbolNames),
62671ebc9ebSNico Weber isDefinition())
62771ebc9ebSNico Weber .bind("class_method"),
62871ebc9ebSNico Weber MatchCallbacks.back().get());
62971ebc9ebSNico Weber // Match static member variable definition of the moved class.
63071ebc9ebSNico Weber Finder->addMatcher(
63171ebc9ebSNico Weber varDecl(InMovedClass, InOldFiles, isDefinition(), isStaticDataMember())
63271ebc9ebSNico Weber .bind("class_static_var_decl"),
63371ebc9ebSNico Weber MatchCallbacks.back().get());
63471ebc9ebSNico Weber
6351c705d9cSJonas Devlieghere MatchCallbacks.push_back(std::make_unique<FunctionDeclarationMatch>(this));
63641bbb875SNathan James Finder->addMatcher(functionDecl(InOldFiles, HasAnySymbolNames, TopLevelDecl)
63771ebc9ebSNico Weber .bind("function"),
63871ebc9ebSNico Weber MatchCallbacks.back().get());
63971ebc9ebSNico Weber
6401c705d9cSJonas Devlieghere MatchCallbacks.push_back(std::make_unique<VarDeclarationMatch>(this));
64171ebc9ebSNico Weber Finder->addMatcher(
64241bbb875SNathan James varDecl(InOldFiles, HasAnySymbolNames, TopLevelDecl).bind("var"),
64371ebc9ebSNico Weber MatchCallbacks.back().get());
64471ebc9ebSNico Weber
64571ebc9ebSNico Weber // Match enum definition in old.h. Enum helpers (which are defined in old.cc)
64671ebc9ebSNico Weber // will not be moved for now no matter whether they are used or not.
6471c705d9cSJonas Devlieghere MatchCallbacks.push_back(std::make_unique<EnumDeclarationMatch>(this));
64871ebc9ebSNico Weber Finder->addMatcher(
64941bbb875SNathan James enumDecl(InOldHeader, HasAnySymbolNames, isDefinition(), TopLevelDecl)
65071ebc9ebSNico Weber .bind("enum"),
65171ebc9ebSNico Weber MatchCallbacks.back().get());
65271ebc9ebSNico Weber
65371ebc9ebSNico Weber // Match type alias in old.h, this includes "typedef" and "using" type alias
65471ebc9ebSNico Weber // declarations. Type alias helpers (which are defined in old.cc) will not be
65571ebc9ebSNico Weber // moved for now no matter whether they are used or not.
6561c705d9cSJonas Devlieghere MatchCallbacks.push_back(std::make_unique<TypeAliasMatch>(this));
65771ebc9ebSNico Weber Finder->addMatcher(namedDecl(anyOf(typedefDecl().bind("typedef"),
65871ebc9ebSNico Weber typeAliasDecl().bind("type_alias")),
65941bbb875SNathan James InOldHeader, HasAnySymbolNames, TopLevelDecl),
66071ebc9ebSNico Weber MatchCallbacks.back().get());
66171ebc9ebSNico Weber }
66271ebc9ebSNico Weber
run(const ast_matchers::MatchFinder::MatchResult & Result)66371ebc9ebSNico Weber void ClangMoveTool::run(const ast_matchers::MatchFinder::MatchResult &Result) {
66471ebc9ebSNico Weber if (const auto *D = Result.Nodes.getNodeAs<NamedDecl>("decls_in_header")) {
66571ebc9ebSNico Weber UnremovedDeclsInOldHeader.insert(D);
66671ebc9ebSNico Weber } else if (const auto *FWD =
66771ebc9ebSNico Weber Result.Nodes.getNodeAs<CXXRecordDecl>("fwd_decl")) {
66871ebc9ebSNico Weber // Skip all forward declarations which appear after moved class declaration.
66971ebc9ebSNico Weber if (RemovedDecls.empty()) {
67071ebc9ebSNico Weber if (const auto *DCT = FWD->getDescribedClassTemplate())
67171ebc9ebSNico Weber MovedDecls.push_back(DCT);
67271ebc9ebSNico Weber else
67371ebc9ebSNico Weber MovedDecls.push_back(FWD);
67471ebc9ebSNico Weber }
67571ebc9ebSNico Weber } else if (const auto *ND =
67671ebc9ebSNico Weber Result.Nodes.getNodeAs<NamedDecl>("helper_decls")) {
67771ebc9ebSNico Weber MovedDecls.push_back(ND);
67871ebc9ebSNico Weber HelperDeclarations.push_back(ND);
67919701458SBruno Ricci LLVM_DEBUG(llvm::dbgs()
68019701458SBruno Ricci << "Add helper : " << ND->getDeclName() << " (" << ND << ")\n");
68171ebc9ebSNico Weber } else if (const auto *UD = Result.Nodes.getNodeAs<NamedDecl>("using_decl")) {
68271ebc9ebSNico Weber MovedDecls.push_back(UD);
68371ebc9ebSNico Weber }
68471ebc9ebSNico Weber }
68571ebc9ebSNico Weber
makeAbsolutePath(StringRef Path)68671ebc9ebSNico Weber std::string ClangMoveTool::makeAbsolutePath(StringRef Path) {
68771ebc9ebSNico Weber return MakeAbsolutePath(Context->OriginalRunningDirectory, Path);
68871ebc9ebSNico Weber }
68971ebc9ebSNico Weber
addIncludes(llvm::StringRef IncludeHeader,bool IsAngled,llvm::StringRef SearchPath,llvm::StringRef FileName,CharSourceRange IncludeFilenameRange,const SourceManager & SM)69071ebc9ebSNico Weber void ClangMoveTool::addIncludes(llvm::StringRef IncludeHeader, bool IsAngled,
69171ebc9ebSNico Weber llvm::StringRef SearchPath,
69271ebc9ebSNico Weber llvm::StringRef FileName,
69371ebc9ebSNico Weber CharSourceRange IncludeFilenameRange,
69471ebc9ebSNico Weber const SourceManager &SM) {
695ca64c894SNathan James SmallString<128> HeaderWithSearchPath;
69671ebc9ebSNico Weber llvm::sys::path::append(HeaderWithSearchPath, SearchPath, IncludeHeader);
69771ebc9ebSNico Weber std::string AbsoluteIncludeHeader =
698ca64c894SNathan James MakeAbsolutePath(SM, HeaderWithSearchPath);
69971ebc9ebSNico Weber std::string IncludeLine =
70071ebc9ebSNico Weber IsAngled ? ("#include <" + IncludeHeader + ">\n").str()
70171ebc9ebSNico Weber : ("#include \"" + IncludeHeader + "\"\n").str();
70271ebc9ebSNico Weber
70371ebc9ebSNico Weber std::string AbsoluteOldHeader = makeAbsolutePath(Context->Spec.OldHeader);
70471ebc9ebSNico Weber std::string AbsoluteCurrentFile = MakeAbsolutePath(SM, FileName);
70571ebc9ebSNico Weber if (AbsoluteOldHeader == AbsoluteCurrentFile) {
70671ebc9ebSNico Weber // Find old.h includes "old.h".
70771ebc9ebSNico Weber if (AbsoluteOldHeader == AbsoluteIncludeHeader) {
70871ebc9ebSNico Weber OldHeaderIncludeRangeInHeader = IncludeFilenameRange;
70971ebc9ebSNico Weber return;
71071ebc9ebSNico Weber }
71171ebc9ebSNico Weber HeaderIncludes.push_back(IncludeLine);
71271ebc9ebSNico Weber } else if (makeAbsolutePath(Context->Spec.OldCC) == AbsoluteCurrentFile) {
71371ebc9ebSNico Weber // Find old.cc includes "old.h".
71471ebc9ebSNico Weber if (AbsoluteOldHeader == AbsoluteIncludeHeader) {
71571ebc9ebSNico Weber OldHeaderIncludeRangeInCC = IncludeFilenameRange;
71671ebc9ebSNico Weber return;
71771ebc9ebSNico Weber }
71871ebc9ebSNico Weber CCIncludes.push_back(IncludeLine);
71971ebc9ebSNico Weber }
72071ebc9ebSNico Weber }
72171ebc9ebSNico Weber
removeDeclsInOldFiles()72271ebc9ebSNico Weber void ClangMoveTool::removeDeclsInOldFiles() {
72371ebc9ebSNico Weber if (RemovedDecls.empty()) return;
72471ebc9ebSNico Weber
72571ebc9ebSNico Weber // If old_header is not specified (only move declarations from old.cc), remain
72671ebc9ebSNico Weber // all the helper function declarations in old.cc as UnremovedDeclsInOldHeader
72771ebc9ebSNico Weber // is empty in this case, there is no way to verify unused/used helpers.
72871ebc9ebSNico Weber if (!Context->Spec.OldHeader.empty()) {
72971ebc9ebSNico Weber std::vector<const NamedDecl *> UnremovedDecls;
73071ebc9ebSNico Weber for (const auto *D : UnremovedDeclsInOldHeader)
73171ebc9ebSNico Weber UnremovedDecls.push_back(D);
73271ebc9ebSNico Weber
73371ebc9ebSNico Weber auto UsedDecls = getUsedDecls(RGBuilder.getGraph(), UnremovedDecls);
73471ebc9ebSNico Weber
73571ebc9ebSNico Weber // We remove the helper declarations which are not used in the old.cc after
73671ebc9ebSNico Weber // moving the given declarations.
73771ebc9ebSNico Weber for (const auto *D : HelperDeclarations) {
73819701458SBruno Ricci LLVM_DEBUG(llvm::dbgs() << "Check helper is used: " << D->getDeclName()
73919701458SBruno Ricci << " (" << D << ")\n");
74071ebc9ebSNico Weber if (!UsedDecls.count(HelperDeclRGBuilder::getOutmostClassOrFunDecl(
74171ebc9ebSNico Weber D->getCanonicalDecl()))) {
74271ebc9ebSNico Weber LLVM_DEBUG(llvm::dbgs() << "Helper removed in old.cc: "
74319701458SBruno Ricci << D->getDeclName() << " (" << D << ")\n");
74471ebc9ebSNico Weber RemovedDecls.push_back(D);
74571ebc9ebSNico Weber }
74671ebc9ebSNico Weber }
74771ebc9ebSNico Weber }
74871ebc9ebSNico Weber
74971ebc9ebSNico Weber for (const auto *RemovedDecl : RemovedDecls) {
75071ebc9ebSNico Weber const auto &SM = RemovedDecl->getASTContext().getSourceManager();
75171ebc9ebSNico Weber auto Range = getFullRange(RemovedDecl);
75271ebc9ebSNico Weber tooling::Replacement RemoveReplacement(
75371ebc9ebSNico Weber SM, CharSourceRange::getCharRange(Range.getBegin(), Range.getEnd()),
75471ebc9ebSNico Weber "");
75571ebc9ebSNico Weber std::string FilePath = RemoveReplacement.getFilePath().str();
75671ebc9ebSNico Weber auto Err = Context->FileToReplacements[FilePath].add(RemoveReplacement);
75771ebc9ebSNico Weber if (Err)
75871ebc9ebSNico Weber llvm::errs() << llvm::toString(std::move(Err)) << "\n";
75971ebc9ebSNico Weber }
76071ebc9ebSNico Weber const auto &SM = RemovedDecls[0]->getASTContext().getSourceManager();
76171ebc9ebSNico Weber
76271ebc9ebSNico Weber // Post process of cleanup around all the replacements.
76371ebc9ebSNico Weber for (auto &FileAndReplacements : Context->FileToReplacements) {
76471ebc9ebSNico Weber StringRef FilePath = FileAndReplacements.first;
76571ebc9ebSNico Weber // Add #include of new header to old header.
76671ebc9ebSNico Weber if (Context->Spec.OldDependOnNew &&
76771ebc9ebSNico Weber MakeAbsolutePath(SM, FilePath) ==
76871ebc9ebSNico Weber makeAbsolutePath(Context->Spec.OldHeader)) {
76943356f56SNico Weber // FIXME: Minimize the include path like clang-include-fixer.
77071ebc9ebSNico Weber std::string IncludeNewH =
77171ebc9ebSNico Weber "#include \"" + Context->Spec.NewHeader + "\"\n";
772dd5571d5SKazuaki Ishizaki // This replacement for inserting header will be cleaned up at the end.
77371ebc9ebSNico Weber auto Err = FileAndReplacements.second.add(
77471ebc9ebSNico Weber tooling::Replacement(FilePath, UINT_MAX, 0, IncludeNewH));
77571ebc9ebSNico Weber if (Err)
77671ebc9ebSNico Weber llvm::errs() << llvm::toString(std::move(Err)) << "\n";
77771ebc9ebSNico Weber }
77871ebc9ebSNico Weber
77971ebc9ebSNico Weber auto SI = FilePathToFileID.find(FilePath);
78071ebc9ebSNico Weber // Ignore replacements for new.h/cc.
78171ebc9ebSNico Weber if (SI == FilePathToFileID.end()) continue;
78271ebc9ebSNico Weber llvm::StringRef Code = SM.getBufferData(SI->second);
78371ebc9ebSNico Weber auto Style = format::getStyle(format::DefaultFormatStyle, FilePath,
78471ebc9ebSNico Weber Context->FallbackStyle);
78571ebc9ebSNico Weber if (!Style) {
78671ebc9ebSNico Weber llvm::errs() << llvm::toString(Style.takeError()) << "\n";
78771ebc9ebSNico Weber continue;
78871ebc9ebSNico Weber }
78971ebc9ebSNico Weber auto CleanReplacements = format::cleanupAroundReplacements(
790adcd0268SBenjamin Kramer Code, Context->FileToReplacements[std::string(FilePath)], *Style);
79171ebc9ebSNico Weber
79271ebc9ebSNico Weber if (!CleanReplacements) {
79371ebc9ebSNico Weber llvm::errs() << llvm::toString(CleanReplacements.takeError()) << "\n";
79471ebc9ebSNico Weber continue;
79571ebc9ebSNico Weber }
796adcd0268SBenjamin Kramer Context->FileToReplacements[std::string(FilePath)] = *CleanReplacements;
79771ebc9ebSNico Weber }
79871ebc9ebSNico Weber }
79971ebc9ebSNico Weber
moveDeclsToNewFiles()80071ebc9ebSNico Weber void ClangMoveTool::moveDeclsToNewFiles() {
80171ebc9ebSNico Weber std::vector<const NamedDecl *> NewHeaderDecls;
80271ebc9ebSNico Weber std::vector<const NamedDecl *> NewCCDecls;
80371ebc9ebSNico Weber for (const auto *MovedDecl : MovedDecls) {
80471ebc9ebSNico Weber if (isInHeaderFile(MovedDecl, Context->OriginalRunningDirectory,
80571ebc9ebSNico Weber Context->Spec.OldHeader))
80671ebc9ebSNico Weber NewHeaderDecls.push_back(MovedDecl);
80771ebc9ebSNico Weber else
80871ebc9ebSNico Weber NewCCDecls.push_back(MovedDecl);
80971ebc9ebSNico Weber }
81071ebc9ebSNico Weber
81171ebc9ebSNico Weber auto UsedDecls = getUsedDecls(RGBuilder.getGraph(), RemovedDecls);
81271ebc9ebSNico Weber std::vector<const NamedDecl *> ActualNewCCDecls;
81371ebc9ebSNico Weber
81471ebc9ebSNico Weber // Filter out all unused helpers in NewCCDecls.
815dd5571d5SKazuaki Ishizaki // We only move the used helpers (including transitively used helpers) and the
81671ebc9ebSNico Weber // given symbols being moved.
81771ebc9ebSNico Weber for (const auto *D : NewCCDecls) {
81871ebc9ebSNico Weber if (llvm::is_contained(HelperDeclarations, D) &&
81971ebc9ebSNico Weber !UsedDecls.count(HelperDeclRGBuilder::getOutmostClassOrFunDecl(
82071ebc9ebSNico Weber D->getCanonicalDecl())))
82171ebc9ebSNico Weber continue;
82271ebc9ebSNico Weber
82319701458SBruno Ricci LLVM_DEBUG(llvm::dbgs() << "Helper used in new.cc: " << D->getDeclName()
82471ebc9ebSNico Weber << " " << D << "\n");
82571ebc9ebSNico Weber ActualNewCCDecls.push_back(D);
82671ebc9ebSNico Weber }
82771ebc9ebSNico Weber
82871ebc9ebSNico Weber if (!Context->Spec.NewHeader.empty()) {
82971ebc9ebSNico Weber std::string OldHeaderInclude =
83071ebc9ebSNico Weber Context->Spec.NewDependOnOld
83171ebc9ebSNico Weber ? "#include \"" + Context->Spec.OldHeader + "\"\n"
83271ebc9ebSNico Weber : "";
83371ebc9ebSNico Weber Context->FileToReplacements[Context->Spec.NewHeader] =
83471ebc9ebSNico Weber createInsertedReplacements(HeaderIncludes, NewHeaderDecls,
83571ebc9ebSNico Weber Context->Spec.NewHeader, /*IsHeader=*/true,
83671ebc9ebSNico Weber OldHeaderInclude);
83771ebc9ebSNico Weber }
83871ebc9ebSNico Weber if (!Context->Spec.NewCC.empty())
83971ebc9ebSNico Weber Context->FileToReplacements[Context->Spec.NewCC] =
84071ebc9ebSNico Weber createInsertedReplacements(CCIncludes, ActualNewCCDecls,
84171ebc9ebSNico Weber Context->Spec.NewCC);
84271ebc9ebSNico Weber }
84371ebc9ebSNico Weber
84471ebc9ebSNico Weber // Move all contents from OldFile to NewFile.
moveAll(SourceManager & SM,StringRef OldFile,StringRef NewFile)84571ebc9ebSNico Weber void ClangMoveTool::moveAll(SourceManager &SM, StringRef OldFile,
84671ebc9ebSNico Weber StringRef NewFile) {
84727254ae5SJan Svoboda auto FE = SM.getFileManager().getOptionalFileRef(makeAbsolutePath(OldFile));
84871ebc9ebSNico Weber if (!FE) {
84971ebc9ebSNico Weber llvm::errs() << "Failed to get file: " << OldFile << "\n";
85071ebc9ebSNico Weber return;
85171ebc9ebSNico Weber }
852a02f8576SHarlan Haskins FileID ID = SM.getOrCreateFileID(*FE, SrcMgr::C_User);
85371ebc9ebSNico Weber auto Begin = SM.getLocForStartOfFile(ID);
85471ebc9ebSNico Weber auto End = SM.getLocForEndOfFile(ID);
85571ebc9ebSNico Weber tooling::Replacement RemoveAll(SM, CharSourceRange::getCharRange(Begin, End),
85671ebc9ebSNico Weber "");
85771ebc9ebSNico Weber std::string FilePath = RemoveAll.getFilePath().str();
85871ebc9ebSNico Weber Context->FileToReplacements[FilePath] = tooling::Replacements(RemoveAll);
85971ebc9ebSNico Weber
86071ebc9ebSNico Weber StringRef Code = SM.getBufferData(ID);
86171ebc9ebSNico Weber if (!NewFile.empty()) {
86271ebc9ebSNico Weber auto AllCode =
86371ebc9ebSNico Weber tooling::Replacements(tooling::Replacement(NewFile, 0, 0, Code));
86471ebc9ebSNico Weber auto ReplaceOldInclude = [&](CharSourceRange OldHeaderIncludeRange) {
86571ebc9ebSNico Weber AllCode = AllCode.merge(tooling::Replacements(tooling::Replacement(
86671ebc9ebSNico Weber SM, OldHeaderIncludeRange, '"' + Context->Spec.NewHeader + '"')));
86771ebc9ebSNico Weber };
86871ebc9ebSNico Weber // Fix the case where old.h/old.cc includes "old.h", we replace the
86971ebc9ebSNico Weber // `#include "old.h"` with `#include "new.h"`.
87071ebc9ebSNico Weber if (Context->Spec.NewCC == NewFile && OldHeaderIncludeRangeInCC.isValid())
87171ebc9ebSNico Weber ReplaceOldInclude(OldHeaderIncludeRangeInCC);
87271ebc9ebSNico Weber else if (Context->Spec.NewHeader == NewFile &&
87371ebc9ebSNico Weber OldHeaderIncludeRangeInHeader.isValid())
87471ebc9ebSNico Weber ReplaceOldInclude(OldHeaderIncludeRangeInHeader);
875adcd0268SBenjamin Kramer Context->FileToReplacements[std::string(NewFile)] = std::move(AllCode);
87671ebc9ebSNico Weber }
87771ebc9ebSNico Weber }
87871ebc9ebSNico Weber
onEndOfTranslationUnit()87971ebc9ebSNico Weber void ClangMoveTool::onEndOfTranslationUnit() {
88071ebc9ebSNico Weber if (Context->DumpDeclarations) {
88171ebc9ebSNico Weber assert(Reporter);
88271ebc9ebSNico Weber for (const auto *Decl : UnremovedDeclsInOldHeader) {
88371ebc9ebSNico Weber auto Kind = Decl->getKind();
88471ebc9ebSNico Weber bool Templated = Decl->isTemplated();
88571ebc9ebSNico Weber const std::string QualifiedName = Decl->getQualifiedNameAsString();
88671ebc9ebSNico Weber if (Kind == Decl::Kind::Var)
88771ebc9ebSNico Weber Reporter->reportDeclaration(QualifiedName, "Variable", Templated);
88871ebc9ebSNico Weber else if (Kind == Decl::Kind::Function ||
88971ebc9ebSNico Weber Kind == Decl::Kind::FunctionTemplate)
89071ebc9ebSNico Weber Reporter->reportDeclaration(QualifiedName, "Function", Templated);
89171ebc9ebSNico Weber else if (Kind == Decl::Kind::ClassTemplate ||
89271ebc9ebSNico Weber Kind == Decl::Kind::CXXRecord)
89371ebc9ebSNico Weber Reporter->reportDeclaration(QualifiedName, "Class", Templated);
89471ebc9ebSNico Weber else if (Kind == Decl::Kind::Enum)
89571ebc9ebSNico Weber Reporter->reportDeclaration(QualifiedName, "Enum", Templated);
89671ebc9ebSNico Weber else if (Kind == Decl::Kind::Typedef || Kind == Decl::Kind::TypeAlias ||
89771ebc9ebSNico Weber Kind == Decl::Kind::TypeAliasTemplate)
89871ebc9ebSNico Weber Reporter->reportDeclaration(QualifiedName, "TypeAlias", Templated);
89971ebc9ebSNico Weber }
90071ebc9ebSNico Weber return;
90171ebc9ebSNico Weber }
90271ebc9ebSNico Weber
90371ebc9ebSNico Weber if (RemovedDecls.empty())
90471ebc9ebSNico Weber return;
90571ebc9ebSNico Weber // Ignore symbols that are not supported when checking if there is unremoved
90671ebc9ebSNico Weber // symbol in old header. This makes sure that we always move old files to new
90771ebc9ebSNico Weber // files when all symbols produced from dump_decls are moved.
90871ebc9ebSNico Weber auto IsSupportedKind = [](const NamedDecl *Decl) {
90971ebc9ebSNico Weber switch (Decl->getKind()) {
91071ebc9ebSNico Weber case Decl::Kind::Function:
91171ebc9ebSNico Weber case Decl::Kind::FunctionTemplate:
91271ebc9ebSNico Weber case Decl::Kind::ClassTemplate:
91371ebc9ebSNico Weber case Decl::Kind::CXXRecord:
91471ebc9ebSNico Weber case Decl::Kind::Enum:
91571ebc9ebSNico Weber case Decl::Kind::Typedef:
91671ebc9ebSNico Weber case Decl::Kind::TypeAlias:
91771ebc9ebSNico Weber case Decl::Kind::TypeAliasTemplate:
91871ebc9ebSNico Weber case Decl::Kind::Var:
91971ebc9ebSNico Weber return true;
92071ebc9ebSNico Weber default:
92171ebc9ebSNico Weber return false;
92271ebc9ebSNico Weber }
92371ebc9ebSNico Weber };
924f5a68feaSKazu Hirata if (llvm::none_of(UnremovedDeclsInOldHeader, IsSupportedKind) &&
92571ebc9ebSNico Weber !Context->Spec.OldHeader.empty()) {
92671ebc9ebSNico Weber auto &SM = RemovedDecls[0]->getASTContext().getSourceManager();
92771ebc9ebSNico Weber moveAll(SM, Context->Spec.OldHeader, Context->Spec.NewHeader);
92871ebc9ebSNico Weber moveAll(SM, Context->Spec.OldCC, Context->Spec.NewCC);
92971ebc9ebSNico Weber return;
93071ebc9ebSNico Weber }
93171ebc9ebSNico Weber LLVM_DEBUG(RGBuilder.getGraph()->dump());
93271ebc9ebSNico Weber moveDeclsToNewFiles();
93371ebc9ebSNico Weber removeDeclsInOldFiles();
93471ebc9ebSNico Weber }
93571ebc9ebSNico Weber
93671ebc9ebSNico Weber } // namespace move
93771ebc9ebSNico Weber } // namespace clang
938