summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEric Liu <ioeric@google.com>2016-09-23 15:10:56 +0000
committerEric Liu <ioeric@google.com>2016-09-23 15:10:56 +0000
commit04f6547d75b397343691045a9c8589d6bdf6434b (patch)
tree79b06facc60329fdb6df91169aac62871531bae9
parent7981b20f318488a10e7c0c8e0f0ca502e02e74cd (diff)
downloadclang-04f6547d75b397343691045a9c8589d6bdf6434b.tar.gz
[clang-format] support header deletion in cleanupAroundReplacemnts.
Summary: - If a replacement has offset UINT_MAX, length 0, and a replacement text that is an #include directive, this will insert the #include into the correct block in the \p Code. - If a replacement has offset UINT_MAX, length 1, and a replacement text that is the name of the header to be removed, the header will be removed from \p Code if it exists. Reviewers: djasper Subscribers: cfe-commits, klimek Differential Revision: https://reviews.llvm.org/D24829 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@282253 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--include/clang/Format/Format.h9
-rw-r--r--lib/Format/Format.cpp31
-rw-r--r--unittests/Format/CleanupTest.cpp59
3 files changed, 93 insertions, 6 deletions
diff --git a/include/clang/Format/Format.h b/include/clang/Format/Format.h
index 858989182b..ac1c9687ad 100644
--- a/include/clang/Format/Format.h
+++ b/include/clang/Format/Format.h
@@ -783,8 +783,13 @@ formatReplacements(StringRef Code, const tooling::Replacements &Replaces,
/// \brief Returns the replacements corresponding to applying \p Replaces and
/// cleaning up the code after that on success; otherwise, return an llvm::Error
/// carrying llvm::StringError.
-/// This also inserts a C++ #include directive into the correct block if the
-/// replacement corresponding to the header insertion has offset UINT_MAX.
+/// This also supports inserting/deleting C++ #include directives:
+/// - If a replacement has offset UINT_MAX, length 0, and a replacement text
+/// that is an #include directive, this will insert the #include into the
+/// correct block in the \p Code.
+/// - If a replacement has offset UINT_MAX, length 1, and a replacement text
+/// that is the name of the header to be removed, the header will be removed
+/// from \p Code if it exists.
llvm::Expected<tooling::Replacements>
cleanupAroundReplacements(StringRef Code, const tooling::Replacements &Replaces,
const FormatStyle &Style);
diff --git a/lib/Format/Format.cpp b/lib/Format/Format.cpp
index d799eb2e82..4fd6c2a8d7 100644
--- a/lib/Format/Format.cpp
+++ b/lib/Format/Format.cpp
@@ -1504,10 +1504,14 @@ formatReplacements(StringRef Code, const tooling::Replacements &Replaces,
namespace {
inline bool isHeaderInsertion(const tooling::Replacement &Replace) {
- return Replace.getOffset() == UINT_MAX &&
+ return Replace.getOffset() == UINT_MAX && Replace.getLength() == 0 &&
llvm::Regex(IncludeRegexPattern).match(Replace.getReplacementText());
}
+inline bool isHeaderDeletion(const tooling::Replacement &Replace) {
+ return Replace.getOffset() == UINT_MAX && Replace.getLength() == 1;
+}
+
void skipComments(Lexer &Lex, Token &Tok) {
while (Tok.is(tok::comment))
if (Lex.LexFromRawLexer(Tok))
@@ -1548,6 +1552,12 @@ unsigned getOffsetAfterHeaderGuardsAndComments(StringRef FileName,
return AfterComments;
}
+bool isDeletedHeader(llvm::StringRef HeaderName,
+ const std::set<llvm::StringRef> HeadersToDelete) {
+ return HeadersToDelete.find(HeaderName) != HeadersToDelete.end() ||
+ HeadersToDelete.find(HeaderName.trim("\"<>")) != HeadersToDelete.end();
+}
+
// FIXME: we also need to insert a '\n' at the end of the code if we have an
// insertion with offset Code.size(), and there is no '\n' at the end of the
// code.
@@ -1561,12 +1571,15 @@ fixCppIncludeInsertions(StringRef Code, const tooling::Replacements &Replaces,
return Replaces;
tooling::Replacements HeaderInsertions;
+ std::set<llvm::StringRef> HeadersToDelete;
tooling::Replacements Result;
for (const auto &R : Replaces) {
if (isHeaderInsertion(R)) {
// Replacements from \p Replaces must be conflict-free already, so we can
// simply consume the error.
llvm::consumeError(HeaderInsertions.add(R));
+ } else if (isHeaderDeletion(R)) {
+ HeadersToDelete.insert(R.getReplacementText());
} else if (R.getOffset() == UINT_MAX) {
llvm::errs() << "Insertions other than header #include insertion are "
"not supported! "
@@ -1575,7 +1588,7 @@ fixCppIncludeInsertions(StringRef Code, const tooling::Replacements &Replaces,
llvm::consumeError(Result.add(R));
}
}
- if (HeaderInsertions.empty())
+ if (HeaderInsertions.empty() && HeadersToDelete.empty())
return Replaces;
llvm::Regex IncludeRegex(IncludeRegexPattern);
@@ -1605,6 +1618,7 @@ fixCppIncludeInsertions(StringRef Code, const tooling::Replacements &Replaces,
for (auto Line : Lines) {
NextLineOffset = std::min(Code.size(), Offset + Line.size() + 1);
if (IncludeRegex.match(Line, &Matches)) {
+ // The header name with quotes or angle brackets.
StringRef IncludeName = Matches[2];
ExistingIncludes.insert(IncludeName);
int Category = Categories.getIncludePriority(
@@ -1612,6 +1626,19 @@ fixCppIncludeInsertions(StringRef Code, const tooling::Replacements &Replaces,
CategoryEndOffsets[Category] = NextLineOffset;
if (FirstIncludeOffset < 0)
FirstIncludeOffset = Offset;
+ if (isDeletedHeader(IncludeName, HeadersToDelete)) {
+ // If this is the last line without trailing newline, we need to make
+ // sure we don't delete across the file boundary.
+ unsigned Length = std::min(Line.size() + 1, Code.size() - Offset);
+ llvm::Error Err =
+ Result.add(tooling::Replacement(FileName, Offset, Length, ""));
+ if (Err) {
+ // Ignore the deletion on conflict.
+ llvm::errs() << "Failed to add header deletion replacement for "
+ << IncludeName << ": " << llvm::toString(std::move(Err))
+ << "\n";
+ }
+ }
}
Offset = NextLineOffset;
}
diff --git a/unittests/Format/CleanupTest.cpp b/unittests/Format/CleanupTest.cpp
index 6072b226e4..c3e297f797 100644
--- a/unittests/Format/CleanupTest.cpp
+++ b/unittests/Format/CleanupTest.cpp
@@ -247,8 +247,12 @@ protected:
return tooling::Replacement(FileName, Offset, Length, Text);
}
- tooling::Replacement createInsertion(StringRef HeaderName) {
- return createReplacement(UINT_MAX, 0, HeaderName);
+ tooling::Replacement createInsertion(StringRef IncludeDirective) {
+ return createReplacement(UINT_MAX, 0, IncludeDirective);
+ }
+
+ tooling::Replacement createDeletion(StringRef HeaderName) {
+ return createReplacement(UINT_MAX, 1, HeaderName);
}
inline std::string apply(StringRef Code,
@@ -740,6 +744,57 @@ TEST_F(CleanUpReplacementsTest, AddIncludesWithDifferentForms) {
EXPECT_EQ(Expected, apply(Code, Replaces));
}
+TEST_F(CleanUpReplacementsTest, SimpleDeleteIncludes) {
+ std::string Code = "#include \"abc.h\"\n"
+ "#include \"xyz.h\" // comment\n"
+ "#include \"xyz\"\n"
+ "int x;\n";
+ std::string Expected = "#include \"xyz\"\n"
+ "int x;\n";
+ tooling::Replacements Replaces =
+ toReplacements({createDeletion("abc.h"), createDeletion("xyz.h")});
+ EXPECT_EQ(Expected, apply(Code, Replaces));
+}
+
+TEST_F(CleanUpReplacementsTest, DeleteAllCode) {
+ std::string Code = "#include \"xyz.h\"\n"
+ "#include <xyz.h>";
+ std::string Expected = "";
+ tooling::Replacements Replaces = toReplacements({createDeletion("xyz.h")});
+ EXPECT_EQ(Expected, apply(Code, Replaces));
+}
+
+TEST_F(CleanUpReplacementsTest, DeleteAllIncludesWithSameNameIfNoType) {
+ std::string Code = "#include \"xyz.h\"\n"
+ "#include \"xyz\"\n"
+ "#include <xyz.h>\n";
+ std::string Expected = "#include \"xyz\"\n";
+ tooling::Replacements Replaces = toReplacements({createDeletion("xyz.h")});
+ EXPECT_EQ(Expected, apply(Code, Replaces));
+}
+
+TEST_F(CleanUpReplacementsTest, OnlyDeleteHeaderWithType) {
+ std::string Code = "#include \"xyz.h\"\n"
+ "#include \"xyz\"\n"
+ "#include <xyz.h>";
+ std::string Expected = "#include \"xyz.h\"\n"
+ "#include \"xyz\"\n";
+ tooling::Replacements Replaces = toReplacements({createDeletion("<xyz.h>")});
+ EXPECT_EQ(Expected, apply(Code, Replaces));
+}
+
+TEST_F(CleanUpReplacementsTest, InsertionAndDeleteHeader) {
+ std::string Code = "#include \"a.h\"\n"
+ "\n"
+ "#include <vector>\n";
+ std::string Expected = "#include \"a.h\"\n"
+ "\n"
+ "#include <map>\n";
+ tooling::Replacements Replaces = toReplacements(
+ {createDeletion("<vector>"), createInsertion("#include <map>")});
+ EXPECT_EQ(Expected, apply(Code, Replaces));
+}
+
} // end namespace
} // end namespace format
} // end namespace clang