ioeric updated this revision to Diff 58952.
ioeric marked 9 inline comments as done.
ioeric added a comment.
- Addressed reviewer's comments.
http://reviews.llvm.org/D20734
Files:
include/clang/Format/Format.h
lib/Format/Format.cpp
unittests/Format/CleanupTest.cpp
Index: unittests/Format/CleanupTest.cpp
===================================================================
--- unittests/Format/CleanupTest.cpp
+++ unittests/Format/CleanupTest.cpp
@@ -281,6 +281,323 @@
EXPECT_EQ(Expected, applyAllReplacements(Code, FinalReplaces));
}
+TEST_F(CleanUpReplacementsTest, NoExistingIncludeWithoutDefine) {
+ std::string Code = "int main() {}";
+ std::string Expected = "#include \"a.h\"\n"
+ "int main() {}";
+ Context.createInMemoryFile("fix.cpp", Code);
+ tooling::Replacements Replaces;
+ Replaces.insert(tooling::Replacement("fix.cpp", -1U, 0, "#include \"a.h\""));
+ format::FormatStyle Style = format::getLLVMStyle();
+ auto NewReplaces = cleanupAroundReplacements(Code, Replaces, Style);
+ EXPECT_EQ(Expected, applyAllReplacements(Code, NewReplaces));
+}
+
+TEST_F(CleanUpReplacementsTest, NoExistingIncludeWithDefine) {
+ std::string Code = "#ifndef __A_H__\n"
+ "#define __A_H__\n"
+ "class A {};\n"
+ "#define MMM 123\n"
+ "#endif";
+ std::string Expected = "#ifndef __A_H__\n"
+ "#define __A_H__\n"
+ "\n"
+ "#include \"b.h\"\n"
+ "class A {};\n"
+ "#define MMM 123\n"
+ "#endif";
+
+ Context.createInMemoryFile("fix.cpp", Code);
+ tooling::Replacements Replaces;
+ Replaces.insert(tooling::Replacement("fix.cpp", -1U, 0, "#include \"b.h\""));
+ format::FormatStyle Style = format::getLLVMStyle();
+ auto NewReplaces = cleanupAroundReplacements(Code, Replaces, Style);
+ EXPECT_EQ(Expected, applyAllReplacements(Code, NewReplaces));
+}
+
+TEST_F(CleanUpReplacementsTest, InsertBeforeCategoryWithLowerPriority) {
+ std::string Code = "#ifndef __A_H__\n"
+ "#define __A_H__\n"
+ "\n"
+ "\n"
+ "\n"
+ "#include <vector>\n"
+ "class A {};\n"
+ "#define MMM 123\n"
+ "#endif";
+ std::string Expected = "#ifndef __A_H__\n"
+ "#define __A_H__\n"
+ "\n"
+ "\n"
+ "\n"
+ "#include \"a.h\"\n"
+ "#include <vector>\n"
+ "class A {};\n"
+ "#define MMM 123\n"
+ "#endif";
+
+ Context.createInMemoryFile("fix.cpp", Code);
+ tooling::Replacements Replaces;
+ Replaces.insert(tooling::Replacement("fix.cpp", -1U, 0, "#include \"a.h\""));
+ format::FormatStyle Style = format::getLLVMStyle();
+ auto NewReplaces = cleanupAroundReplacements(Code, Replaces, Style);
+ EXPECT_EQ(Expected, applyAllReplacements(Code, NewReplaces));
+}
+
+TEST_F(CleanUpReplacementsTest, InsertAfterMainHeader) {
+ std::string Code = "#include \"fix.h\"\n"
+ "\n"
+ "int main() {}";
+ std::string Expected = "#include \"fix.h\"\n"
+ "#include <a>\n"
+ "\n"
+ "int main() {}";
+ Context.createInMemoryFile("fix.cpp", Code);
+ tooling::Replacements Replaces;
+ Replaces.insert(tooling::Replacement("fix.cpp", -1U, 0, "#include <a>"));
+ format::FormatStyle Style =
+ format::getGoogleStyle(format::FormatStyle::LanguageKind::LK_Cpp);
+ auto NewReplaces = cleanupAroundReplacements(Code, Replaces, Style);
+ EXPECT_EQ(Expected, applyAllReplacements(Code, NewReplaces));
+}
+
+TEST_F(CleanUpReplacementsTest, InsertBeforeSystemHeaderLLVM) {
+ std::string Code = "#include <memory>\n"
+ "\n"
+ "int main() {}";
+ std::string Expected = "#include \"z.h\"\n"
+ "#include <memory>\n"
+ "\n"
+ "int main() {}";
+ Context.createInMemoryFile("fix.cpp", Code);
+ tooling::Replacements Replaces;
+ Replaces.insert(tooling::Replacement("fix.cpp", -1U, 0, "#include \"z.h\""));
+ format::FormatStyle Style = format::getLLVMStyle();
+ auto NewReplaces = cleanupAroundReplacements(Code, Replaces, Style);
+ EXPECT_EQ(Expected, applyAllReplacements(Code, NewReplaces));
+}
+
+TEST_F(CleanUpReplacementsTest, InsertAfterSystemHeaderGoogle) {
+ std::string Code = "#include <memory>\n"
+ "\n"
+ "int main() {}";
+ std::string Expected = "#include <memory>\n"
+ "#include \"z.h\"\n"
+ "\n"
+ "int main() {}";
+ Context.createInMemoryFile("fix.cpp", Code);
+ tooling::Replacements Replaces;
+ Replaces.insert(tooling::Replacement("fix.cpp", -1U, 0, "#include \"z.h\""));
+ format::FormatStyle Style =
+ format::getGoogleStyle(format::FormatStyle::LanguageKind::LK_Cpp);
+ auto NewReplaces = cleanupAroundReplacements(Code, Replaces, Style);
+ EXPECT_EQ(Expected, applyAllReplacements(Code, NewReplaces));
+}
+
+TEST_F(CleanUpReplacementsTest, InsertOneIncludeLLVMStyle) {
+ std::string Code = "#include \"x/fix.h\"\n"
+ "#include \"a.h\"\n"
+ "#include \"b.h\"\n"
+ "#include \"clang/Format/Format.h\"\n"
+ "#include <memory>\n";
+ std::string Expected = "#include \"x/fix.h\"\n"
+ "#include \"a.h\"\n"
+ "#include \"b.h\"\n"
+ "#include \"d.h\"\n"
+ "#include \"clang/Format/Format.h\"\n"
+ "#include \"llvm/x/y.h\"\n"
+ "#include <memory>\n";
+ Context.createInMemoryFile("fix.cpp", Code);
+ tooling::Replacements Replaces;
+ Replaces.insert(tooling::Replacement("fix.cpp", -1U, 0, "#include \"d.h\""));
+ Replaces.insert(
+ tooling::Replacement("fix.cpp", -1U, 0, "#include \"llvm/x/y.h\""));
+ format::FormatStyle Style = format::getLLVMStyle();
+ auto NewReplaces = cleanupAroundReplacements(Code, Replaces, Style);
+ EXPECT_EQ(Expected, applyAllReplacements(Code, NewReplaces));
+}
+
+TEST_F(CleanUpReplacementsTest, InsertMultipleIncludesLLVMStyle) {
+ std::string Code = "#include \"x/fix.h\"\n"
+ "#include \"a.h\"\n"
+ "#include \"b.h\"\n"
+ "#include \"clang/Format/Format.h\"\n"
+ "#include <memory>\n";
+ std::string Expected = "#include \"x/fix.h\"\n"
+ "#include \"a.h\"\n"
+ "#include \"b.h\"\n"
+ "#include \"new/new.h\"\n"
+ "#include \"clang/Format/Format.h\"\n"
+ "#include <memory>\n"
+ "#include <list>\n";
+ Context.createInMemoryFile("fix.cpp", Code);
+ tooling::Replacements Replaces;
+ Replaces.insert(tooling::Replacement("fix.cpp", -1U, 0, "#include <list>"));
+ Replaces.insert(
+ tooling::Replacement("fix.cpp", -1U, 0, "#include \"new/new.h\""));
+ format::FormatStyle Style = format::getLLVMStyle();
+ auto NewReplaces = cleanupAroundReplacements(Code, Replaces, Style);
+ EXPECT_EQ(Expected, applyAllReplacements(Code, NewReplaces));
+}
+
+TEST_F(CleanUpReplacementsTest, InsertNewSystemIncludeGoogleStyle) {
+ std::string Code = "#include \"x/fix.h\"\n"
+ "\n"
+ "#include \"y/a.h\"\n"
+ "#include \"z/b.h\"\n";
+ // FIXME: inserting after the empty line following the main header might be
+ // prefered.
+ std::string Expected = "#include \"x/fix.h\"\n"
+ "#include <vector>\n"
+ "\n"
+ "#include \"y/a.h\"\n"
+ "#include \"z/b.h\"\n";
+ Context.createInMemoryFile("x/fix.cpp", Code);
+ tooling::Replacements Replaces;
+ Replaces.insert(
+ tooling::Replacement("fix.cpp", -1U, 0, "#include <vector>"));
+ format::FormatStyle Style =
+ format::getGoogleStyle(format::FormatStyle::LanguageKind::LK_Cpp);
+ auto NewReplaces = cleanupAroundReplacements(Code, Replaces, Style);
+ EXPECT_EQ(Expected, applyAllReplacements(Code, NewReplaces));
+}
+
+TEST_F(CleanUpReplacementsTest, InsertMultipleIncludesGoogleStyle) {
+ std::string Code = "#include \"x/fix.h\"\n"
+ "\n"
+ "#include <vector>\n"
+ "\n"
+ "#include \"y/a.h\"\n"
+ "#include \"z/b.h\"\n";
+ std::string Expected = "#include \"x/fix.h\"\n"
+ "\n"
+ "#include <vector>\n"
+ "#include <list>\n"
+ "\n"
+ "#include \"y/a.h\"\n"
+ "#include \"z/b.h\"\n"
+ "#include \"x/x.h\"\n";
+ Context.createInMemoryFile("x/fix.cpp", Code);
+ tooling::Replacements Replaces;
+ Replaces.insert(
+ tooling::Replacement("fix.cpp", -1U, 0, "#include <list>"));
+ Replaces.insert(
+ tooling::Replacement("fix.cpp", -1U, 0, "#include \"x/x.h\""));
+ format::FormatStyle Style =
+ format::getGoogleStyle(format::FormatStyle::LanguageKind::LK_Cpp);
+ auto NewReplaces = cleanupAroundReplacements(Code, Replaces, Style);
+ EXPECT_EQ(Expected, applyAllReplacements(Code, NewReplaces));
+}
+
+TEST_F(CleanUpReplacementsTest, InsertMultipleNewHeadersAndSortLLVM) {
+ std::string Code = "\nint x;";
+ std::string Expected = "#include \"fix.h\"\n"
+ "#include \"a.h\"\n"
+ "#include \"b.h\"\n"
+ "#include \"c.h\"\n"
+ "#include <list>\n"
+ "#include <vector>\n"
+ "\nint x;";
+ Context.createInMemoryFile("fix.cpp", Code);
+ tooling::Replacements Replaces;
+ Replaces.insert(
+ tooling::Replacement("fix.cpp", -1U, 0, "#include \"a.h\""));
+ Replaces.insert(
+ tooling::Replacement("fix.cpp", -1U, 0, "#include \"c.h\""));
+ Replaces.insert(
+ tooling::Replacement("fix.cpp", -1U, 0, "#include \"b.h\""));
+ Replaces.insert(
+ tooling::Replacement("fix.cpp", -1U, 0, "#include <vector>"));
+ Replaces.insert(
+ tooling::Replacement("fix.cpp", -1U, 0, "#include <list>"));
+ Replaces.insert(
+ tooling::Replacement("fix.cpp", -1U, 0, "#include \"fix.h\""));
+ format::FormatStyle Style = format::getLLVMStyle();
+ auto NewReplaces = cleanupAroundReplacements(Code, Replaces, Style);
+ EXPECT_EQ(Expected, applyAllReplacements(
+ Code, formatReplacements(Code, NewReplaces, Style)));
+}
+
+TEST_F(CleanUpReplacementsTest, InsertMultipleNewHeadersAndSortGoogle) {
+ std::string Code = "\nint x;";
+ std::string Expected = "#include \"fix.h\"\n"
+ "#include <list>\n"
+ "#include <vector>\n"
+ "#include \"a.h\"\n"
+ "#include \"b.h\"\n"
+ "#include \"c.h\"\n"
+ "\nint x;";
+ Context.createInMemoryFile("fix.cpp", Code);
+ tooling::Replacements Replaces;
+ Replaces.insert(
+ tooling::Replacement("fix.cpp", -1U, 0, "#include \"a.h\""));
+ Replaces.insert(
+ tooling::Replacement("fix.cpp", -1U, 0, "#include \"c.h\""));
+ Replaces.insert(
+ tooling::Replacement("fix.cpp", -1U, 0, "#include \"b.h\""));
+ Replaces.insert(
+ tooling::Replacement("fix.cpp", -1U, 0, "#include <vector>"));
+ Replaces.insert(
+ tooling::Replacement("fix.cpp", -1U, 0, "#include <list>"));
+ Replaces.insert(
+ tooling::Replacement("fix.cpp", -1U, 0, "#include \"fix.h\""));
+ format::FormatStyle Style =
+ format::getGoogleStyle(format::FormatStyle::LanguageKind::LK_Cpp);
+ auto NewReplaces = cleanupAroundReplacements(Code, Replaces, Style);
+ EXPECT_EQ(Expected, applyAllReplacements(
+ Code, formatReplacements(Code, NewReplaces, Style)));
+}
+
+TEST_F(CleanUpReplacementsTest, FormatCorrectLineWhenHeadersAreInserted) {
+ std::string Code = "\n"
+ "int a;\n"
+ "int a;\n"
+ "int a;";
+
+ std::string Expected = "#include \"x.h\"\n"
+ "#include \"y.h\"\n"
+ "#include \"clang/x/x.h\"\n"
+ "#include <list>\n"
+ "#include <vector>\n"
+ "\n"
+ "int a;\n"
+ "int b;\n"
+ "int a;";
+ FileID ID = Context.createInMemoryFile("fix.cpp", Code);
+ tooling::Replacements Replaces;
+ Replaces.insert(tooling::Replacement(Context.Sources,
+ Context.getLocation(ID, 3, 8), 1, "b"));
+ Replaces.insert(tooling::Replacement("fix.cpp", -1U, 0, "#include <vector>"));
+ Replaces.insert(
+ tooling::Replacement("fix.cpp", -1U, 0, "#include <list>"));
+ Replaces.insert(
+ tooling::Replacement("fix.cpp", -1U, 0, "#include \"clang/x/x.h\""));
+ Replaces.insert(
+ tooling::Replacement("fix.cpp", -1U, 0, "#include \"y.h\""));
+ Replaces.insert(
+ tooling::Replacement("fix.cpp", -1U, 0, "#include \"x.h\""));
+ format::FormatStyle Style = format::getLLVMStyle();
+ auto NewReplaces = cleanupAroundReplacements(Code, Replaces, Style);
+ EXPECT_EQ(Expected, applyAllReplacements(
+ Code, formatReplacements(Code, NewReplaces, Style)));
+}
+
+TEST_F(CleanUpReplacementsTest, NotConfusedByDefine) {
+ std::string Code = "void f() {}\n"
+ "#define A \\\n"
+ " int i;";
+ std::string Expected = "#include <vector>\n"
+ "void f() {}\n"
+ "#define A \\\n"
+ " int i;";
+ Context.createInMemoryFile("fix.cpp", Code);
+ tooling::Replacements Replaces;
+ Replaces.insert(tooling::Replacement("fix.cpp", -1U, 0, "#include <vector>"));
+ format::FormatStyle Style = format::getLLVMStyle();
+ auto NewReplaces = cleanupAroundReplacements(Code, Replaces, Style);
+ EXPECT_EQ(Expected, applyAllReplacements(Code, NewReplaces));
+}
+
} // end namespace
} // end namespace format
} // end namespace clang
Index: lib/Format/Format.cpp
===================================================================
--- lib/Format/Format.cpp
+++ lib/Format/Format.cpp
@@ -1262,15 +1262,74 @@
result.size(), result));
}
+namespace {
+
+// This class manages priorities of #include categories and calculates
+// priorities for headers.
+class IncludeCategoryManager {
+public:
+ IncludeCategoryManager(const FormatStyle &Style, StringRef FileName)
+ : Style(Style), FileName(FileName) {
+ FileStem = llvm::sys::path::stem(FileName);
+ for (const auto &Category : Style.IncludeCategories)
+ CategoryRegexs.emplace_back(Category.Regex);
+ IsMainFile = FileName.endswith(".c") || FileName.endswith(".cc") ||
+ FileName.endswith(".cpp") || FileName.endswith(".c++") ||
+ FileName.endswith(".cxx") || FileName.endswith(".m") ||
+ FileName.endswith(".mm");
+ }
+
+ // Returns the priority of the category which \p IncludeName belongs to, and
+ // if \p CheckMainHeader is true and \p IncldueName is a
+ // main header, returns 0. Otherwise, returns INT_MAX if \p IncludeName does
+ // not match any category pattern.
+ int getIncludePriority(StringRef IncludeName, bool CheckMainHeader) {
+ int Ret = INT_MAX;
+ for (unsigned I = 0, E = CategoryRegexs.size(); I != E; ++I)
+ if (CategoryRegexs[I].match(IncludeName)) {
+ Ret = Style.IncludeCategories[I].Priority;
+ break;
+ }
+ if (CheckMainHeader && IsMainFile && Ret > 0 && isMainHeader(IncludeName))
+ Ret = 0;
+ return Ret;
+ }
+
+private:
+ bool isMainHeader(StringRef IncludeName) const {
+ if (!IncludeName.startswith("\""))
+ return false;
+ StringRef HeaderStem =
+ llvm::sys::path::stem(IncludeName.drop_front(1).drop_back(1));
+ if (FileStem.startswith(HeaderStem)) {
+ llvm::Regex MainIncludeRegex(
+ (HeaderStem + Style.IncludeIsMainRegex).str());
+ if (MainIncludeRegex.match(FileStem))
+ return true;
+ }
+ return false;
+ }
+
+ const FormatStyle &Style;
+ bool IsMainFile;
+ StringRef FileName;
+ StringRef FileStem;
+ SmallVector<llvm::Regex, 4> CategoryRegexs;
+};
+
+const char IncludeRegexPattern[] =
+ R"(^[\t\ ]*#[\t\ ]*(import|include)[^"<]*(["<][^">]*[">]))";
+
+} // anonymous namespace
+
tooling::Replacements sortCppIncludes(const FormatStyle &Style, StringRef Code,
ArrayRef<tooling::Range> Ranges,
StringRef FileName,
tooling::Replacements &Replaces,
unsigned *Cursor) {
unsigned Prev = 0;
unsigned SearchFrom = 0;
- llvm::Regex IncludeRegex(
- R"(^[\t\ ]*#[\t\ ]*(import|include)[^"<]*(["<][^">]*[">]))");
+ llvm::Regex IncludeRegex(IncludeRegexPattern);
SmallVector<StringRef, 4> Matches;
SmallVector<IncludeDirective, 16> IncludesInBlock;
@@ -1281,19 +1340,9 @@
//
// FIXME: Do some sanity checking, e.g. edit distance of the base name, to fix
// cases where the first #include is unlikely to be the main header.
- bool IsSource = FileName.endswith(".c") || FileName.endswith(".cc") ||
- FileName.endswith(".cpp") || FileName.endswith(".c++") ||
- FileName.endswith(".cxx") || FileName.endswith(".m") ||
- FileName.endswith(".mm");
- StringRef FileStem = llvm::sys::path::stem(FileName);
+ IncludeCategoryManager Categories(Style, FileName);
bool FirstIncludeBlock = true;
bool MainIncludeFound = false;
-
- // Create pre-compiled regular expressions for the #include categories.
- SmallVector<llvm::Regex, 4> CategoryRegexs;
- for (const auto &Category : Style.IncludeCategories)
- CategoryRegexs.emplace_back(Category.Regex);
-
bool FormattingOff = false;
for (;;) {
@@ -1310,26 +1359,11 @@
if (!FormattingOff && !Line.endswith("\\")) {
if (IncludeRegex.match(Line, &Matches)) {
StringRef IncludeName = Matches[2];
- int Category = INT_MAX;
- for (unsigned i = 0, e = CategoryRegexs.size(); i != e; ++i) {
- if (CategoryRegexs[i].match(IncludeName)) {
- Category = Style.IncludeCategories[i].Priority;
- break;
- }
- }
- if (IsSource && !MainIncludeFound && Category > 0 &&
- FirstIncludeBlock && IncludeName.startswith("\"")) {
- StringRef HeaderStem =
- llvm::sys::path::stem(IncludeName.drop_front(1).drop_back(1));
- if (FileStem.startswith(HeaderStem)) {
- llvm::Regex MainIncludeRegex(
- (HeaderStem + Style.IncludeIsMainRegex).str());
- if (MainIncludeRegex.match(FileStem)) {
- Category = 0;
- MainIncludeFound = true;
- }
- }
- }
+ int Category = Categories.getIncludePriority(
+ IncludeName,
+ /*CheckMainHeader=*/!MainIncludeFound && FirstIncludeBlock);
+ if (Category == 0)
+ MainIncludeFound = true;
IncludesInBlock.push_back({IncludeName, Line, Prev, Category});
} else if (!IncludesInBlock.empty()) {
sortCppIncludes(Style, IncludesInBlock, Ranges, FileName, Replaces,
@@ -1402,6 +1436,139 @@
return processReplacements(Reformat, Code, SortedReplaces, Style);
}
+namespace {
+
+// FIXME: do not insert headers into conditional #include blocks, e.g. #includes
+// surrounded by compile condition "#if...".
+// FIXME: do not insert existing headers.
+// FIXME: insert empty lines between newly created blocks.
+tooling::Replacements
+fixCppIncludeInsertions(StringRef Code, const tooling::Replacements &Replaces,
+ const FormatStyle &Style) {
+ if (Replaces.empty())
+ return tooling::Replacements();
+
+ llvm::Regex IncludeRegex(IncludeRegexPattern);
+ llvm::Regex DefineRegex(R"(^[\t\ ]*#[\t\ ]*define[\t\ ]*[^\\]*$)");
+ SmallVector<StringRef, 4> Matches;
+
+ StringRef FileName = Replaces.begin()->getFilePath();
+ IncludeCategoryManager Categories(Style, FileName);
+ int MaxPriority = 0;
+ for (const auto &Category : Style.IncludeCategories)
+ if (Category.Priority > MaxPriority)
+ MaxPriority = Category.Priority;
+
+ // Record the offset of the end of the last include in each category.
+ std::map<int, int> CategoryEndOffsets;
+ // All possible priorities.
+ // Add 0 for main header and INT_MAX for headers that are not in any category.
+ std::vector<int> Priorities = {0, INT_MAX};
+ for (const auto &Category : Style.IncludeCategories)
+ Priorities.push_back(Category.Priority);
+ // Sort and deduplicate priorities.
+ std::sort(Priorities.begin(), Priorities.end());
+ Priorities.erase(std::unique(Priorities.begin(), Priorities.end()),
+ Priorities.end());
+ int FirstIncludeOffset = -1;
+ int Offset = 0;
+ int AfterHeaderGuard = 0;
+ SmallVector<StringRef, 32> Lines;
+ Code.split(Lines, '\n');
+ for (auto Line : Lines) {
+ if (IncludeRegex.match(Line, &Matches)) {
+ StringRef IncludeName = Matches[2];
+ int Category = Categories.getIncludePriority(
+ IncludeName, /*CheckMainHeader=*/FirstIncludeOffset < 0);
+ CategoryEndOffsets[Category] = Offset + Line.size() + 1;
+ if (FirstIncludeOffset < 0)
+ FirstIncludeOffset = Offset;
+ }
+ Offset += Line.size() + 1;
+ // FIXME: make header guard matching more strict, e.g. consider #ifndef.
+ if (AfterHeaderGuard == 0 && DefineRegex.match(Line))
+ AfterHeaderGuard = Offset;
+ }
+
+ // Set EndOffset of each category that is not set yet to be the end of the
+ // last category with higher priority. If there is no category with higher
+ // priority, then we look for the next block with lower category; if such
+ // block exists, then the EndOffset is set to the beginning of this category.
+ // Otherwise, we can say there is no existing header and set the EndOffset to
+ // be the end of header guard or the top of the code.
+ // To simplify this, we treat the category with the highest priority
+ // specially. If its EndOffset has not been set, we set its EndOffset to be
+ // the begin of the first #include if there is existing #include. Otherwise,
+ // we set its EndOffset to be AfterHeaderGuard. With this setup, the category
+ // with the highest priority has been intialized so that all other categories
+ // can find a category with higher priority and initialized EndOffset.
+ // FIXME: skip comment section in the beginning of the code if there is no
+ // existing #include and #define.
+ tooling::Replacements Results;
+ int Highest = Priorities[0];
+ if (CategoryEndOffsets.find(Highest) == CategoryEndOffsets.end()) {
+ if (FirstIncludeOffset >= 0) {
+ CategoryEndOffsets[Highest] = FirstIncludeOffset;
+ } else {
+ // Insert a new line after header guard.
+ // FIXME: since the new line and the end offset are both set to
+ // AfterHeaderGuard, the order of these two insertions can not be
+ // guaranteed (the new line is inserted after the new #include). So we
+ // replace the new line at the end of the #define directive with two empty
+ // lines to walk around this issue.
+ if (AfterHeaderGuard > 0)
+ Results.insert(
+ tooling::Replacement(FileName, AfterHeaderGuard-1, 1, "\n\n"));
+ CategoryEndOffsets[Highest] = AfterHeaderGuard;
+ }
+ }
+ for (int I = 1, E = Priorities.size(); I != E; I++)
+ if (CategoryEndOffsets.find(Priorities[I]) == CategoryEndOffsets.end())
+ CategoryEndOffsets[Priorities[I]] = CategoryEndOffsets[Priorities[I - 1]];
+
+ for (const auto &R : Replaces) {
+ auto IncludeDirective = R.getReplacementText();
+ if (!IncludeRegex.match(IncludeDirective, &Matches)) {
+ llvm::errs() << IncludeDirective
+ << " is not a valid #include directive.\n";
+ continue;
+ }
+ auto IncludeName = Matches[2];
+ int Category =
+ Categories.getIncludePriority(IncludeName, /*CheckMainHeader=*/false);
+ Offset = CategoryEndOffsets[Category];
+ std::string NewInclude =
+ (!IncludeDirective.endswith("\n"))
+ ? (IncludeDirective + "\n").str()
+ : IncludeDirective.str();
+ Results.insert(tooling::Replacement(FileName, Offset, 0, NewInclude));
+ }
+ return Results;
+}
+
+// Insert #include directives into the correct blocks.
+tooling::Replacements fixHeaderInsertions(StringRef Code,
+ const tooling::Replacements &Replaces,
+ const FormatStyle &Style) {
+ if (Style.Language != FormatStyle::LanguageKind::LK_Cpp)
+ return Replaces;
+ tooling::Replacements HeaderInsertionReplaces;
+ tooling::Replacements NewReplaces;
+ for (const auto &R : Replaces) {
+ if (R.getOffset() == -1U)
+ HeaderInsertionReplaces.insert(R);
+ else
+ NewReplaces.insert(R);
+ }
+ HeaderInsertionReplaces =
+ fixCppIncludeInsertions(Code, HeaderInsertionReplaces, Style);
+ NewReplaces.insert(HeaderInsertionReplaces.begin(),
+ HeaderInsertionReplaces.end());
+ return NewReplaces;
+}
+
+} // anonymous namespace
+
tooling::Replacements
cleanupAroundReplacements(StringRef Code, const tooling::Replacements &Replaces,
const FormatStyle &Style) {
@@ -1412,7 +1579,10 @@
StringRef FileName) -> tooling::Replacements {
return cleanup(Style, Code, Ranges, FileName);
};
- return processReplacements(Cleanup, Code, Replaces, Style);
+ // Make header insertion replacements insert new headers into correct blocks.
+ tooling::Replacements NewReplaces =
+ fixHeaderInsertions(Code, Replaces, Style);
+ return processReplacements(Cleanup, Code, NewReplaces, Style);
}
tooling::Replacements reformat(const FormatStyle &Style, SourceManager &SM,
Index: include/clang/Format/Format.h
===================================================================
--- include/clang/Format/Format.h
+++ include/clang/Format/Format.h
@@ -773,6 +773,9 @@
/// \brief Returns the replacements corresponding to applying \p Replaces and
/// cleaning up the code after that.
+/// This also inserts a C++ #include directive into the correct block if the
+/// replacement corresponding to the header insertion has offset UINT_MAX (i.e.
+/// -1U).
tooling::Replacements
cleanupAroundReplacements(StringRef Code, const tooling::Replacements &Replaces,
const FormatStyle &Style);
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits