diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp --- a/clang/lib/Format/Format.cpp +++ b/clang/lib/Format/Format.cpp @@ -38,6 +38,7 @@ #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/Sequence.h" #include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringSet.h" #include "llvm/Support/Allocator.h" #include "llvm/Support/Debug.h" #include "llvm/Support/Path.h" @@ -2858,6 +2859,7 @@ // #include in the duplicate #includes. static void sortCppIncludes(const FormatStyle &Style, const SmallVectorImpl &Includes, + llvm::StringSet<> &AllIncludes, ArrayRef Ranges, StringRef FileName, StringRef Code, tooling::Replacements &Replaces, unsigned *Cursor) { @@ -2900,12 +2902,14 @@ } // Deduplicate #includes. - Indices.erase(std::unique(Indices.begin(), Indices.end(), - [&](unsigned LHSI, unsigned RHSI) { - return Includes[LHSI].Text.trim() == - Includes[RHSI].Text.trim(); - }), - Indices.end()); + + Indices.erase( + std::remove_if( + Indices.begin(), Indices.end(), + [&](unsigned val) { + return !AllIncludes.try_emplace(Includes[val].Text.trim()).second; + }), + Indices.end()); int CurrentCategory = Includes.front().Category; @@ -2966,6 +2970,7 @@ .Default(0); unsigned SearchFrom = 0; SmallVector Matches; + llvm::StringSet<> AllIncludes{}; SmallVector IncludesInBlock; // FIXME: Do some validation, e.g. edit distance of the base name, to fix @@ -3032,16 +3037,20 @@ Categories.getSortIncludePriority(IncludeName, FirstIncludeBlock); // Todo(remove before merge): reason for removal: every header can have // 0,0 priority + IncludesInBlock.push_back( {IncludeName, Line, Prev, Category, Priority}); + } else if (!IncludesInBlock.empty() && !EmptyLineSkipped) { - sortCppIncludes(Style, IncludesInBlock, Ranges, FileName, Code, - Replaces, Cursor); + sortCppIncludes(Style, IncludesInBlock, AllIncludes, Ranges, FileName, + Code, Replaces, Cursor); IncludesInBlock.clear(); - if (Trimmed.startswith("#pragma hdrstop")) // Precompiled headers. + if (Trimmed.startswith("#pragma hdrstop")) { // Precompiled headers. FirstIncludeBlock = true; - else + AllIncludes.clear(); + } else { FirstIncludeBlock = false; + } } } if (Pos == StringRef::npos || Pos + 1 == Code.size()) @@ -3052,8 +3061,8 @@ SearchFrom = Pos + 1; } if (!IncludesInBlock.empty()) { - sortCppIncludes(Style, IncludesInBlock, Ranges, FileName, Code, Replaces, - Cursor); + sortCppIncludes(Style, IncludesInBlock, AllIncludes, Ranges, FileName, Code, + Replaces, Cursor); } return Replaces; } diff --git a/clang/unittests/Format/SortIncludesTest.cpp b/clang/unittests/Format/SortIncludesTest.cpp --- a/clang/unittests/Format/SortIncludesTest.cpp +++ b/clang/unittests/Format/SortIncludesTest.cpp @@ -268,9 +268,7 @@ "#include \n" "#include \n" "/* clang-format offically */\n" - "#include \n" - "#include \n" - "#include \n" + "\n" "/* clang-format onwards */\n", sort("#include \n" "#include \n" @@ -899,6 +897,19 @@ "\n" "#include \n" "#include \n")); + + Style.IncludeBlocks = tooling::IncludeStyle::IBS_Regroup; + EXPECT_EQ("#include \"MyProject/MyLibrary/c_main.h\"\n" + "\n" + "#include \n" + "//\n", + sort("#include \"MyProject/MyLibrary/c_main.h\"\n" + "#include \"MyProject/MyLibrary/c_main.h\"\n" + "#include \n" + "#include \n" + "//\n" + "#include \n", + "c_main.cpp", 2U)); } TEST_F(SortIncludesTest, CalculatesCorrectCursorPositionAfterDeduplicate) { @@ -924,11 +935,10 @@ EXPECT_EQ(26u, newCursor(Code, 52)); } -TEST_F(SortIncludesTest, DeduplicateLocallyInEachBlock) { +TEST_F(SortIncludesTest, DeduplicateInAllBlocks) { EXPECT_EQ("#include \n" "#include \n" "\n" - "#include \n" "#include \n", sort("#include \n" "#include \n"