diff --git a/clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp b/clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp --- a/clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp +++ b/clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp @@ -214,7 +214,7 @@ })cpp"); TestTU TU; - TU.Filename = "foo.cpp"; + TU.Filename = "main.cpp"; TU.AdditionalFiles["a.h"] = guard("#include \"b.h\""); TU.AdditionalFiles["b.h"] = guard("void b();"); diff --git a/clang-tools-extra/include-cleaner/unittests/AnalysisTest.cpp b/clang-tools-extra/include-cleaner/unittests/AnalysisTest.cpp --- a/clang-tools-extra/include-cleaner/unittests/AnalysisTest.cpp +++ b/clang-tools-extra/include-cleaner/unittests/AnalysisTest.cpp @@ -311,12 +311,9 @@ Results = {}; Results.Missing.push_back("\"d.h\""); Code = R"cpp(#include "a.h")cpp"; - // FIXME: this isn't correct, the main-file header d.h should be added before - // a.h. EXPECT_EQ(fixIncludes(Results, "d.cc", Code, format::getLLVMStyle()), -R"cpp(#include "a.h" -#include "d.h" -)cpp"); +R"cpp(#include "d.h" +#include "a.h")cpp"); } MATCHER_P3(expandedAt, FileID, Offset, SM, "") { diff --git a/clang/include/clang/Tooling/Inclusions/HeaderIncludes.h b/clang/include/clang/Tooling/Inclusions/HeaderIncludes.h --- a/clang/include/clang/Tooling/Inclusions/HeaderIncludes.h +++ b/clang/include/clang/Tooling/Inclusions/HeaderIncludes.h @@ -128,6 +128,8 @@ // inserting new #includes into the actual code section (e.g. after a // declaration). unsigned MaxInsertOffset; + // True if we find the main-file header in the Code. + bool MainIncludeFound; IncludeCategoryManager Categories; // Record the offset of the end of the last include in each category. std::unordered_map CategoryEndOffsets; diff --git a/clang/lib/Tooling/Inclusions/HeaderIncludes.cpp b/clang/lib/Tooling/Inclusions/HeaderIncludes.cpp --- a/clang/lib/Tooling/Inclusions/HeaderIncludes.cpp +++ b/clang/lib/Tooling/Inclusions/HeaderIncludes.cpp @@ -276,6 +276,7 @@ MaxInsertOffset(MinInsertOffset + getMaxHeaderInsertionOffset( FileName, Code.drop_front(MinInsertOffset), Style)), + MainIncludeFound(false), Categories(Style, FileName) { // Add 0 for main header and INT_MAX for headers that are not in any // category. @@ -335,7 +336,9 @@ // Only record the offset of current #include if we can insert after it. if (CurInclude.R.getOffset() <= MaxInsertOffset) { int Priority = Categories.getIncludePriority( - CurInclude.Name, /*CheckMainHeader=*/FirstIncludeOffset < 0); + CurInclude.Name, /*CheckMainHeader=*/!MainIncludeFound); + if (Priority == 0) + MainIncludeFound = true; CategoryEndOffsets[Priority] = NextLineOffset; IncludesByPriority[Priority].push_back(&CurInclude); if (FirstIncludeOffset < 0) @@ -362,7 +365,7 @@ std::string(llvm::formatv(IsAngled ? "<{0}>" : "\"{0}\"", IncludeName)); StringRef QuotedName = Quoted; int Priority = Categories.getIncludePriority( - QuotedName, /*CheckMainHeader=*/FirstIncludeOffset < 0); + QuotedName, /*CheckMainHeader=*/!MainIncludeFound); auto CatOffset = CategoryEndOffsets.find(Priority); assert(CatOffset != CategoryEndOffsets.end()); unsigned InsertOffset = CatOffset->second; // Fall back offset diff --git a/clang/unittests/Tooling/HeaderIncludesTest.cpp b/clang/unittests/Tooling/HeaderIncludesTest.cpp --- a/clang/unittests/Tooling/HeaderIncludesTest.cpp +++ b/clang/unittests/Tooling/HeaderIncludesTest.cpp @@ -143,6 +143,19 @@ EXPECT_NE(Expected, insert(Code, "")) << "Not main header"; } +TEST_F(HeaderIncludesTest, InsertMainHeader) { + Style = format::getGoogleStyle(format::FormatStyle::LanguageKind::LK_Cpp) + .IncludeStyle; + FileName = "fix.cpp"; + EXPECT_EQ(R"cpp(#include "fix.h" +#include "a.h")cpp", insert("#include \"a.h\"", "\"fix.h\"")); + + // Respect the original main-file header. + EXPECT_EQ(R"cpp(#include "z/fix.h" +#include "a/fix.h" +)cpp", insert("#include \"z/fix.h\"", "\"a/fix.h\"")); +} + TEST_F(HeaderIncludesTest, InsertBeforeSystemHeaderLLVM) { std::string Code = "#include \n" "\n"