diff options
author | Eric Liu <ioeric@google.com> | 2016-09-23 15:10:56 +0000 |
---|---|---|
committer | Eric Liu <ioeric@google.com> | 2016-09-23 15:10:56 +0000 |
commit | 04f6547d75b397343691045a9c8589d6bdf6434b (patch) | |
tree | 79b06facc60329fdb6df91169aac62871531bae9 | |
parent | 7981b20f318488a10e7c0c8e0f0ca502e02e74cd (diff) | |
download | clang-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.h | 9 | ||||
-rw-r--r-- | lib/Format/Format.cpp | 31 | ||||
-rw-r--r-- | unittests/Format/CleanupTest.cpp | 59 |
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 |