[clang-move] Clever on handling header file which includes itself.

Summary:
Previously, we assume only old.cc includes "old.h", which would
introduce incorrect fixes for the cases where old.h also includes `#include "old.h"`

Although it should not be occurred in real projects, clang-move should handle this.

Old.h:

```
class Foo {};
```

after moving to a new old.h:

```
class Foo {};
```

Reviewers: ioeric

Reviewed By: ioeric

Subscribers: cfe-commits, klimek

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

llvm-svn: 323865
This commit is contained in:
Haojian Wu 2018-01-31 12:12:29 +00:00
parent 2f335af5c0
commit fb68ca1825
3 changed files with 56 additions and 15 deletions

View File

@ -694,22 +694,28 @@ void ClangMoveTool::addIncludes(llvm::StringRef IncludeHeader, bool IsAngled,
const SourceManager &SM) { const SourceManager &SM) {
SmallVector<char, 128> HeaderWithSearchPath; SmallVector<char, 128> HeaderWithSearchPath;
llvm::sys::path::append(HeaderWithSearchPath, SearchPath, IncludeHeader); llvm::sys::path::append(HeaderWithSearchPath, SearchPath, IncludeHeader);
std::string AbsoluteOldHeader = makeAbsolutePath(Context->Spec.OldHeader); std::string AbsoluteIncludeHeader =
if (AbsoluteOldHeader ==
MakeAbsolutePath(SM, llvm::StringRef(HeaderWithSearchPath.data(), MakeAbsolutePath(SM, llvm::StringRef(HeaderWithSearchPath.data(),
HeaderWithSearchPath.size()))) { HeaderWithSearchPath.size()));
OldHeaderIncludeRange = IncludeFilenameRange;
return;
}
std::string IncludeLine = std::string IncludeLine =
IsAngled ? ("#include <" + IncludeHeader + ">\n").str() IsAngled ? ("#include <" + IncludeHeader + ">\n").str()
: ("#include \"" + IncludeHeader + "\"\n").str(); : ("#include \"" + IncludeHeader + "\"\n").str();
std::string AbsoluteOldHeader = makeAbsolutePath(Context->Spec.OldHeader);
std::string AbsoluteCurrentFile = MakeAbsolutePath(SM, FileName); std::string AbsoluteCurrentFile = MakeAbsolutePath(SM, FileName);
if (AbsoluteOldHeader == AbsoluteCurrentFile) { if (AbsoluteOldHeader == AbsoluteCurrentFile) {
// Find old.h includes "old.h".
if (AbsoluteOldHeader == AbsoluteIncludeHeader) {
OldHeaderIncludeRangeInHeader = IncludeFilenameRange;
return;
}
HeaderIncludes.push_back(IncludeLine); HeaderIncludes.push_back(IncludeLine);
} else if (makeAbsolutePath(Context->Spec.OldCC) == AbsoluteCurrentFile) { } else if (makeAbsolutePath(Context->Spec.OldCC) == AbsoluteCurrentFile) {
// Find old.cc includes "old.h".
if (AbsoluteOldHeader == AbsoluteIncludeHeader) {
OldHeaderIncludeRangeInCC = IncludeFilenameRange;
return;
}
CCIncludes.push_back(IncludeLine); CCIncludes.push_back(IncludeLine);
} }
} }
@ -857,13 +863,18 @@ void ClangMoveTool::moveAll(SourceManager &SM, StringRef OldFile,
if (!NewFile.empty()) { if (!NewFile.empty()) {
auto AllCode = clang::tooling::Replacements( auto AllCode = clang::tooling::Replacements(
clang::tooling::Replacement(NewFile, 0, 0, Code)); clang::tooling::Replacement(NewFile, 0, 0, Code));
// If we are moving from old.cc, an extra step is required: excluding auto ReplaceOldInclude = [&](clang::CharSourceRange OldHeaderIncludeRange) {
// the #include of "old.h", instead, we replace it with #include of "new.h". AllCode = AllCode.merge(clang::tooling::Replacements(
if (Context->Spec.NewCC == NewFile && OldHeaderIncludeRange.isValid()) { clang::tooling::Replacement(SM, OldHeaderIncludeRange,
AllCode = AllCode.merge( '"' + Context->Spec.NewHeader + '"')));
clang::tooling::Replacements(clang::tooling::Replacement( };
SM, OldHeaderIncludeRange, '"' + Context->Spec.NewHeader + '"'))); // Fix the case where old.h/old.cc includes "old.h", we replace the
} // `#include "old.h"` with `#include "new.h"`.
if (Context->Spec.NewCC == NewFile && OldHeaderIncludeRangeInCC.isValid())
ReplaceOldInclude(OldHeaderIncludeRangeInCC);
else if (Context->Spec.NewHeader == NewFile &&
OldHeaderIncludeRangeInHeader.isValid())
ReplaceOldInclude(OldHeaderIncludeRangeInHeader);
Context->FileToReplacements[NewFile] = std::move(AllCode); Context->FileToReplacements[NewFile] = std::move(AllCode);
} }
} }

View File

@ -176,7 +176,11 @@ private:
/// The source range for the written file name in #include (i.e. "old.h" for /// 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 /// #include "old.h") in old.cc, including the enclosing quotes or angle
/// brackets. /// brackets.
clang::CharSourceRange OldHeaderIncludeRange; 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 /// Mapping from FilePath to FileID, which can be used in post processes like
/// cleanup around replacements. /// cleanup around replacements.
llvm::StringMap<FileID> FilePathToFileID; llvm::StringMap<FileID> FilePathToFileID;

View File

@ -293,6 +293,32 @@ TEST(ClangMove, MoveNonExistClass) {
EXPECT_EQ(0u, Results.size()); EXPECT_EQ(0u, Results.size());
} }
TEST(ClangMove, HeaderIncludeSelf) {
move::MoveDefinitionSpec Spec;
Spec.Names = {std::string("Foo")};
Spec.OldHeader = "foo.h";
Spec.OldCC = "foo.cc";
Spec.NewHeader = "new_foo.h";
Spec.NewCC = "new_foo.cc";
const char TestHeader[] = "#ifndef FOO_H\n"
"#define FOO_H\n"
"#include \"foo.h\"\n"
"class Foo {};\n"
"#endif\n";
const char TestCode[] = "#include \"foo.h\"";
const char ExpectedNewHeader[] = "#ifndef FOO_H\n"
"#define FOO_H\n"
"#include \"new_foo.h\"\n"
"class Foo {};\n"
"#endif\n";
const char ExpectedNewCC[] = "#include \"new_foo.h\"";
auto Results = runClangMoveOnCode(Spec, TestHeader, TestCode);
EXPECT_EQ("", Results[Spec.OldHeader]);
EXPECT_EQ(ExpectedNewHeader, Results[Spec.NewHeader]);
EXPECT_EQ(ExpectedNewCC, Results[Spec.NewCC]);
}
TEST(ClangMove, MoveAll) { TEST(ClangMove, MoveAll) {
std::vector<std::string> TestHeaders = { std::vector<std::string> TestHeaders = {
"class A {\npublic:\n int f();\n};", "class A {\npublic:\n int f();\n};",