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 @@ -405,10 +405,12 @@ // through an #include. while (SM.getFileID(Loc) != SM.getMainFileID()) Loc = SM.getIncludeLoc(SM.getFileID(Loc)); - const auto *Token = AST.getTokens().spelledTokenAt(Loc); - MissingIncludeDiagInfo DiagInfo{Ref.Target, Token->range(SM), - Providers}; - MissingIncludes.push_back(std::move(DiagInfo)); + + if (const auto *Token = AST.getTokens().spelledTokenAt(Loc)) { + MissingIncludeDiagInfo DiagInfo{Ref.Target, Token->range(SM), + Providers}; + MissingIncludes.push_back(std::move(DiagInfo)); + } }); std::vector UnusedIncludes = getUnused(AST, Used, /*ReferencedPublicHeaders*/ {}); 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 @@ -442,6 +442,23 @@ EXPECT_EQ(RefRange, Findings[1].SymRefRange); } +TEST(IncludeCleaner, NoCrash) { + TestTU TU; + TU.Code = R"cpp( + #include "all.h" + void test() { + 1s; + } + )cpp"; + TU.AdditionalFiles["foo.h"] = + guard("int operator\"\"s(unsigned long long) { return 0; }"); + TU.AdditionalFiles["all.h"] = guard("#include \"foo.h\""); + ParsedAST AST = TU.build(); + auto Findings = computeIncludeCleanerFindings(AST); + // FIXME: this is not ideal, but at least we don't crash. + EXPECT_THAT(Findings.MissingIncludes, IsEmpty()); +} + } // namespace } // namespace clangd } // namespace clang