diff --git a/clang-tools-extra/clangd/IncludeCleaner.cpp b/clang-tools-extra/clangd/IncludeCleaner.cpp --- a/clang-tools-extra/clangd/IncludeCleaner.cpp +++ b/clang-tools-extra/clangd/IncludeCleaner.cpp @@ -121,7 +121,11 @@ if (!Macros.insert(FID).second) return; const auto &Exp = SM.getSLocEntry(FID).getExpansion(); - add(Exp.getSpellingLoc()); + // For nested macro expansions, the spelling location can be within a + // temporary buffer that Clang creates (scratch space or ScratchBuffer). + // That is not a real file we can include. + if (!SM.isWrittenInScratchSpace(Exp.getSpellingLoc())) + add(Exp.getSpellingLoc()); add(Exp.getExpansionLocStart()); add(Exp.getExpansionLocEnd()); } 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 @@ -167,6 +167,38 @@ UnorderedElementsAre("\"unused.h\"", "\"dir/unused.h\"")); } +TEST(IncludeCleaner, ScratchBuffer) { + TestTU TU; + TU.Filename = "foo.cpp"; + TU.Code = R"cpp( + #include "macro_spelling_in_scratch_buffer.h" + + using flags::FLAGS_FOO; + )cpp"; + // The pasting operator in combination with DEFINE_FLAG will create + // ScratchBuffer with `flags::FLAGS_FOO` that will have FileID but not + // FileEntry. + TU.AdditionalFiles["macro_spelling_in_scratch_buffer.h"] = R"cpp( + #define DEFINE_FLAG(X) \ + namespace flags { \ + int FLAGS_##X; \ + } \ + + DEFINE_FLAG(FOO) + )cpp"; + ParsedAST AST = TU.build(); + auto &SM = AST.getSourceManager(); + auto &Includes = AST.getIncludeStructure(); + auto ReferencedFiles = translateToHeaderIDs( + findReferencedFiles(findReferencedLocations(AST), SM), Includes, SM); + auto Entry = SM.getFileManager().getFile( + testPath("macro_spelling_in_scratch_buffer.h")); + ASSERT_TRUE(Entry); + auto HeaderID = Includes.getID(*Entry); + EXPECT_THAT(ReferencedFiles, UnorderedElementsAre(HeaderID)); + EXPECT_THAT(getUnused(Includes, ReferencedFiles), ::testing::IsEmpty()); +} + } // namespace } // namespace clangd } // namespace clang