[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 llvm-svn: 282253
This commit is contained in:
parent
465c18973d
commit
c0d3a80123
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue