Skip to content

Commit

Permalink
[clang-move] Move all code from old.h/cc directly when moving all cla…
Browse files Browse the repository at this point in the history
…ss declarations from old.h.

Summary: When moving all code to new.h/cc,  these code also will be formatted based on the given code style.

Reviewers: ioeric

Subscribers: cfe-commits

Differential Revision: https://reviews.llvm.org/D26236

llvm-svn: 286281
  • Loading branch information
hokein committed Nov 8, 2016
1 parent 11a871b commit 2930be1
Show file tree
Hide file tree
Showing 5 changed files with 193 additions and 34 deletions.
103 changes: 84 additions & 19 deletions clang-tools-extra/clang-move/ClangMove.cpp
Expand Up @@ -122,13 +122,13 @@ class FindAllIncludes : public clang::PPCallbacks {
void InclusionDirective(clang::SourceLocation HashLoc,
const clang::Token & /*IncludeTok*/,
StringRef FileName, bool IsAngled,
clang::CharSourceRange /*FilenameRange*/,
clang::CharSourceRange FilenameRange,
const clang::FileEntry * /*File*/,
StringRef SearchPath, StringRef /*RelativePath*/,
const clang::Module * /*Imported*/) override {
if (const auto *FileEntry = SM.getFileEntryForID(SM.getFileID(HashLoc)))
MoveTool->addIncludes(FileName, IsAngled, SearchPath,
FileEntry->getName(), SM);
FileEntry->getName(), FilenameRange, SM);
}

private:
Expand Down Expand Up @@ -321,20 +321,42 @@ void ClangMoveTool::registerMatchers(ast_matchers::MatchFinder *Finder) {
return;
}

auto InOldHeader = isExpansionInFile(
MakeAbsolutePath(OriginalRunningDirectory, Spec.OldHeader));
auto InOldCC =
isExpansionInFile(MakeAbsolutePath(OriginalRunningDirectory, Spec.OldCC));
auto InOldHeader = isExpansionInFile(makeAbsolutePath(Spec.OldHeader));
auto InOldCC = isExpansionInFile(makeAbsolutePath(Spec.OldCC));
auto InOldFiles = anyOf(InOldHeader, InOldCC);
auto InMovedClass =
hasOutermostEnclosingClass(cxxRecordDecl(*InMovedClassNames));

auto ForwardDecls =
cxxRecordDecl(unless(anyOf(isImplicit(), isDefinition())));

//============================================================================
// Matchers for old header
//============================================================================
// Match all top-level named declarations (e.g. function, variable, enum) in
// old header, exclude forward class declarations and namespace declarations.
//
// The old header which contains only one declaration being moved and forward
// declarations is considered to be moved totally.
auto AllDeclsInHeader = namedDecl(
unless(ForwardDecls), unless(namespaceDecl()),
unless(usingDirectiveDecl()), // using namespace decl.
unless(classTemplateDecl(has(ForwardDecls))), // template forward decl.
InOldHeader,
hasParent(decl(anyOf(namespaceDecl(), translationUnitDecl()))));
Finder->addMatcher(AllDeclsInHeader.bind("decls_in_header"), this);
// Match forward declarations in old header.
Finder->addMatcher(namedDecl(ForwardDecls, InOldHeader).bind("fwd_decl"),
this);

//============================================================================
// Matchers for old files, including old.h/old.cc
//============================================================================
// Match moved class declarations.
auto MovedClass = cxxRecordDecl(
InOldFiles, *InMovedClassNames, isDefinition(),
hasDeclContext(anyOf(namespaceDecl(), translationUnitDecl())));
Finder->addMatcher(MovedClass.bind("moved_class"), this);

// Match moved class methods (static methods included) which are defined
// outside moved class declaration.
Finder->addMatcher(
Expand All @@ -343,6 +365,9 @@ void ClangMoveTool::registerMatchers(ast_matchers::MatchFinder *Finder) {
.bind("class_method"),
this);

//============================================================================
// Matchers for old cc
//============================================================================
// Match static member variable definition of the moved class.
Finder->addMatcher(
varDecl(InMovedClass, InOldCC, isDefinition(), isStaticDataMember())
Expand Down Expand Up @@ -374,16 +399,13 @@ void ClangMoveTool::registerMatchers(ast_matchers::MatchFinder *Finder) {
varDecl(IsOldCCStaticDefinition)))
.bind("static_decls"),
this);

// Match forward declarations in old header.
Finder->addMatcher(
cxxRecordDecl(unless(anyOf(isImplicit(), isDefinition())), InOldHeader)
.bind("fwd_decl"),
this);
}

void ClangMoveTool::run(const ast_matchers::MatchFinder::MatchResult &Result) {
if (const auto *CMD =
if (const auto *D =
Result.Nodes.getNodeAs<clang::NamedDecl>("decls_in_header")) {
UnremovedDeclsInOldHeader.insert(D);
} else if (const auto *CMD =
Result.Nodes.getNodeAs<clang::CXXMethodDecl>("class_method")) {
// Skip inline class methods. isInline() ast matcher doesn't ignore this
// case.
Expand All @@ -399,6 +421,7 @@ void ClangMoveTool::run(const ast_matchers::MatchFinder::MatchResult &Result) {
Result.Nodes.getNodeAs<clang::CXXRecordDecl>("moved_class")) {
MovedDecls.emplace_back(class_decl, &Result.Context->getSourceManager());
RemovedDecls.push_back(MovedDecls.back());
UnremovedDeclsInOldHeader.erase(class_decl);
} else if (const auto *FWD =
Result.Nodes.getNodeAs<clang::CXXRecordDecl>("fwd_decl")) {
// Skip all forwad declarations which appear after moved class declaration.
Expand All @@ -421,21 +444,27 @@ void ClangMoveTool::run(const ast_matchers::MatchFinder::MatchResult &Result) {
}
}

std::string ClangMoveTool::makeAbsolutePath(StringRef Path) {
return MakeAbsolutePath(OriginalRunningDirectory, Path);
}

void ClangMoveTool::addIncludes(llvm::StringRef IncludeHeader, bool IsAngled,
llvm::StringRef SearchPath,
llvm::StringRef FileName,
clang::CharSourceRange IncludeFilenameRange,
const SourceManager &SM) {
SmallVector<char, 128> HeaderWithSearchPath;
llvm::sys::path::append(HeaderWithSearchPath, SearchPath, IncludeHeader);
std::string AbsoluteOldHeader =
MakeAbsolutePath(OriginalRunningDirectory, Spec.OldHeader);
std::string AbsoluteOldHeader = makeAbsolutePath(Spec.OldHeader);
// FIXME: Add old.h to the new.cc/h when the new target has dependencies on
// old.h/c. For instance, when moved class uses another class defined in
// old.h, the old.h should be added in new.h.
if (AbsoluteOldHeader ==
MakeAbsolutePath(SM, llvm::StringRef(HeaderWithSearchPath.data(),
HeaderWithSearchPath.size())))
HeaderWithSearchPath.size()))) {
OldHeaderIncludeRange = IncludeFilenameRange;
return;
}

std::string IncludeLine =
IsAngled ? ("#include <" + IncludeHeader + ">\n").str()
Expand All @@ -444,8 +473,7 @@ void ClangMoveTool::addIncludes(llvm::StringRef IncludeHeader, bool IsAngled,
std::string AbsoluteCurrentFile = MakeAbsolutePath(SM, FileName);
if (AbsoluteOldHeader == AbsoluteCurrentFile) {
HeaderIncludes.push_back(IncludeLine);
} else if (MakeAbsolutePath(OriginalRunningDirectory, Spec.OldCC) ==
AbsoluteCurrentFile) {
} else if (makeAbsolutePath(Spec.OldCC) == AbsoluteCurrentFile) {
CCIncludes.push_back(IncludeLine);
}
}
Expand Down Expand Up @@ -499,9 +527,46 @@ void ClangMoveTool::moveClassDefinitionToNewFiles() {
createInsertedReplacements(CCIncludes, NewCCDecls, Spec.NewCC);
}

// Move all contents from OldFile to NewFile.
void ClangMoveTool::moveAll(SourceManager &SM, StringRef OldFile,
StringRef NewFile) {
const FileEntry *FE = SM.getFileManager().getFile(makeAbsolutePath(OldFile));
if (!FE) {
llvm::errs() << "Failed to get file: " << OldFile << "\n";
return;
}
FileID ID = SM.getOrCreateFileID(FE, SrcMgr::C_User);
auto Begin = SM.getLocForStartOfFile(ID);
auto End = SM.getLocForEndOfFile(ID);
clang::tooling::Replacement RemoveAll (
SM, clang::CharSourceRange::getCharRange(Begin, End), "");
std::string FilePath = RemoveAll.getFilePath().str();
FileToReplacements[FilePath] = clang::tooling::Replacements(RemoveAll);

StringRef Code = SM.getBufferData(ID);
if (!NewFile.empty()) {
auto AllCode = clang::tooling::Replacements(
clang::tooling::Replacement(NewFile, 0, 0, Code));
// If we are moving from old.cc, an extra step is required: excluding
// the #include of "old.h", instead, we replace it with #include of "new.h".
if (Spec.NewCC == NewFile && OldHeaderIncludeRange.isValid()) {
AllCode = AllCode.merge(
clang::tooling::Replacements(clang::tooling::Replacement(
SM, OldHeaderIncludeRange, '"' + Spec.NewHeader + '"')));
}
FileToReplacements[NewFile] = std::move(AllCode);
}
}

void ClangMoveTool::onEndOfTranslationUnit() {
if (RemovedDecls.empty())
return;
if (UnremovedDeclsInOldHeader.empty() && !Spec.OldHeader.empty()) {
auto &SM = *RemovedDecls[0].SM;
moveAll(SM, Spec.OldHeader, Spec.NewHeader);
moveAll(SM, Spec.OldCC, Spec.NewCC);
return;
}
removeClassDefinitionInOldFiles();
moveClassDefinitionToNewFiles();
}
Expand Down
18 changes: 18 additions & 0 deletions clang-tools-extra/clang-move/ClangMove.h
Expand Up @@ -14,6 +14,7 @@
#include "clang/Frontend/FrontendAction.h"
#include "clang/Tooling/Core/Replacement.h"
#include "clang/Tooling/Tooling.h"
#include "llvm/ADT/SmallPtrSet.h"
#include <map>
#include <string>
#include <vector>
Expand All @@ -23,6 +24,9 @@ namespace move {

// FIXME: Make it support more types, e.g. function definitions.
// Currently only support moving class definition.
//
// When moving all class declarations in old header, all code in old.h/cc will
// be moved.
class ClangMoveTool : public ast_matchers::MatchFinder::MatchCallback {
public:
// Information about the declaration being moved.
Expand Down Expand Up @@ -68,14 +72,22 @@ class ClangMoveTool : public ast_matchers::MatchFinder::MatchCallback {
/// \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 IncludeRange 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);

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 removeClassDefinitionInOldFiles();
void moveClassDefinitionToNewFiles();
void moveAll(SourceManager& SM, StringRef OldFile, StringRef NewFile);

MoveDefinitionSpec Spec;
// The Key is file path, value is the replacements being applied to the file.
Expand All @@ -97,6 +109,12 @@ class ClangMoveTool : public ast_matchers::MatchFinder::MatchCallback {
std::string OriginalRunningDirectory;
// The name of a predefined code style.
std::string FallbackStyle;
// 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 OldHeaderIncludeRange;
};

class ClangMoveAction : public clang::ASTFrontendAction {
Expand Down
3 changes: 3 additions & 0 deletions clang-tools-extra/test/clang-move/Inputs/test.h
@@ -1,7 +1,10 @@
#ifndef TEST_H // comment 1
#define TEST_H
namespace a {
class Foo {
public:
int f();
int f2(int a, int b);
};
} // namespace a
#endif // TEST_H
17 changes: 8 additions & 9 deletions clang-tools-extra/test/clang-move/move-class.cpp
Expand Up @@ -9,29 +9,29 @@
// RUN: clang-move -names="a::Foo" -new_cc=%T/clang-move/new_test.cpp -new_header=%T/clang-move/new_test.h -old_cc=../src/test.cpp -old_header=../include/test.h %T/clang-move/src/test.cpp
// RUN: FileCheck -input-file=%T/clang-move/new_test.cpp -check-prefix=CHECK-NEW-TEST-CPP %s
// RUN: FileCheck -input-file=%T/clang-move/new_test.h -check-prefix=CHECK-NEW-TEST-H %s
// RUN: FileCheck -input-file=%T/clang-move/src/test.cpp -check-prefix=CHECK-OLD-TEST-CPP %s
// RUN: FileCheck -input-file=%T/clang-move/include/test.h %s -implicit-check-not='{{namespace.*}}'
// RUN: FileCheck -input-file=%T/clang-move/src/test.cpp -check-prefix=CHECK-OLD-TEST-EMPTY -allow-empty %s
// RUN: FileCheck -input-file=%T/clang-move/include/test.h -check-prefix=CHECK-OLD-TEST-EMPTY -allow-empty %s
//
// RUN: cp %S/Inputs/test.h %T/clang-move/include
// RUN: cp %S/Inputs/test.cpp %T/clang-move/src
// RUN: cd %T/clang-move/build
// RUN: clang-move -names="a::Foo" -new_cc=%T/clang-move/new_test.cpp -new_header=%T/clang-move/new_test.h -old_cc=%T/clang-move/src/test.cpp -old_header=%T/clang-move/include/test.h %T/clang-move/src/test.cpp
// RUN: FileCheck -input-file=%T/clang-move/new_test.cpp -check-prefix=CHECK-NEW-TEST-CPP %s
// RUN: FileCheck -input-file=%T/clang-move/new_test.h -check-prefix=CHECK-NEW-TEST-H %s
// RUN: FileCheck -input-file=%T/clang-move/src/test.cpp -check-prefix=CHECK-OLD-TEST-CPP %s
// RUN: FileCheck -input-file=%T/clang-move/include/test.h %s -implicit-check-not='{{namespace.*}}'
// RUN: FileCheck -input-file=%T/clang-move/src/test.cpp -check-prefix=CHECK-OLD-TEST-EMPTY -allow-empty %s
// RUN: FileCheck -input-file=%T/clang-move/include/test.h -check-prefix=CHECK-OLD-TEST-EMPTY -allow-empty %s
//
//
// CHECK-NEW-TEST-H: #ifndef {{.*}}CLANG_MOVE_NEW_TEST_H
// CHECK-NEW-TEST-H: #define {{.*}}CLANG_MOVE_NEW_TEST_H
// CHECK-NEW-TEST-H: #ifndef TEST_H // comment 1
// CHECK-NEW-TEST-H: #define TEST_H
// CHECK-NEW-TEST-H: namespace a {
// CHECK-NEW-TEST-H: class Foo {
// CHECK-NEW-TEST-H: public:
// CHECK-NEW-TEST-H: int f();
// CHECK-NEW-TEST-H: int f2(int a, int b);
// CHECK-NEW-TEST-H: };
// CHECK-NEW-TEST-H: } // namespace a
// CHECK-NEW-TEST-H: #endif // {{.*}}CLANG_MOVE_NEW_TEST_H
// CHECK-NEW-TEST-H: #endif // TEST_H
//
// CHECK-NEW-TEST-CPP: #include "{{.*}}new_test.h"
// CHECK-NEW-TEST-CPP: #include "test2.h"
Expand All @@ -40,5 +40,4 @@
// CHECK-NEW-TEST-CPP: int Foo::f2(int a, int b) { return a + b; }
// CHECK-NEW-TEST-CPP: } // namespace a
//
// CHECK-OLD-TEST-CPP: #include "test.h"
// CHECK-OLD-TEST-CPP: #include "test2.h"
// CHECK-OLD-TEST-EMPTY: {{^}}{{$}}

0 comments on commit 2930be1

Please sign in to comment.