Index: clangd/ClangdUnit.cpp =================================================================== --- clangd/ClangdUnit.cpp +++ clangd/ClangdUnit.cpp @@ -328,6 +328,8 @@ // to read back. We rely on dynamic index for the comments instead. CI.getPreprocessorOpts().WriteCommentListToPCH = false; + PreambleDiagsEngine->setSuppressAfterFatalError(false); + CppFilePreambleCallbacks SerializedDeclsCollector(FileName, PreambleCallback); if (Inputs.FS->setCurrentWorkingDirectory(Inputs.CompileCommand.Directory)) { log("Couldn't set working directory when building the preamble."); Index: clangd/Compiler.cpp =================================================================== --- clangd/Compiler.cpp +++ clangd/Compiler.cpp @@ -63,6 +63,7 @@ auto Clang = llvm::make_unique(PCHs); Clang->setInvocation(std::move(CI)); Clang->createDiagnostics(&DiagsClient, false); + Clang->getDiagnostics().setSuppressAfterFatalError(false); if (auto VFSWithRemapping = createVFSFromCompilerInvocation( Clang->getInvocation(), Clang->getDiagnostics(), VFS)) Index: test/clangd/missing-includes.test =================================================================== --- /dev/null +++ test/clangd/missing-includes.test @@ -0,0 +1,13 @@ +# RUN: clangd -lit-test < %s | FileCheck %s +{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}} +--- +{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///foo.c","languageId":"c","version":1,"text":"#include \n#include \nint x;\n#include \n#include \n"}}} +# CHECK: "method": "textDocument/publishDiagnostics", +# CHECK: "message": "'a' file not found", +# CHECK: "message": "'b' file not found", +# CHECK: "message": "'c' file not found", +# CHECK: "message": "'d' file not found", +--- +{"jsonrpc":"2.0","id":5,"method":"shutdown"} +--- +{"jsonrpc":"2.0","method":"exit"} Index: unittests/clangd/ClangdTests.cpp =================================================================== --- unittests/clangd/ClangdTests.cpp +++ unittests/clangd/ClangdTests.cpp @@ -15,6 +15,8 @@ #include "TestFS.h" #include "URI.h" #include "clang/Config/config.h" +#include "clang/Frontend/CompilerInvocation.h" +#include "clang/Frontend/PCHContainerOperations.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringMap.h" #include "llvm/Support/Errc.h" @@ -37,6 +39,7 @@ using ::testing::Eq; using ::testing::Field; using ::testing::Gt; +using ::testing::HasSubstr; using ::testing::IsEmpty; using ::testing::Pair; using ::testing::UnorderedElementsAre; @@ -963,6 +966,53 @@ Field(&CodeCompletion::Name, "baz"))); } +TEST(DiagnosticsTest, RecoverAfterFatalError) { + auto FooCpp = testPath("foo.cpp"); + + ParseInputs PI; + PI.CompileCommand.Directory = testRoot(); + PI.CompileCommand.Filename = FooCpp; + PI.CompileCommand.CommandLine = {"clang", "-xc++", FooCpp}; + + llvm::StringMap Files; + Files[FooCpp] = ""; + PI.FS = buildTestFS(Files); + + PI.Contents = R"cpp( + #include "preamble1" + #include "preamble2" + // end of the preamble + int x; + #include "main1" + #include "main2" + )cpp"; + + auto PreambleCI = buildCompilerInvocation(PI); + + auto Preamble = buildPreamble( + FooCpp, *PreambleCI, /*OldPreamble=*/nullptr, tooling::CompileCommand(), + PI, std::make_shared(), /*StoreInMemory=*/true, + /*PreambleCallback=*/nullptr); + ASSERT_TRUE(Preamble) << "Failed to build preamble"; + + ASSERT_EQ(Preamble->Diags.size(), 2u); + EXPECT_THAT(Preamble->Diags[0].Message, HasSubstr("preamble1")); + EXPECT_THAT(Preamble->Diags[1].Message, HasSubstr("preamble2")); + + auto MainFileCI = buildCompilerInvocation(PI); + auto AST = + ParsedAST::build(std::move(MainFileCI), Preamble, + llvm::MemoryBuffer::getMemBufferCopy(PI.Contents), + std::make_shared(), PI.FS); + ASSERT_TRUE(AST.hasValue()) << "Failed to build AST"; + + ASSERT_EQ(AST->getDiagnostics().size(), 4u); + EXPECT_THAT(AST->getDiagnostics()[0].Message, HasSubstr("preamble1")); + EXPECT_THAT(AST->getDiagnostics()[1].Message, HasSubstr("preamble2")); + EXPECT_THAT(AST->getDiagnostics()[2].Message, HasSubstr("main1")); + EXPECT_THAT(AST->getDiagnostics()[3].Message, HasSubstr("main2")); +} + } // namespace } // namespace clangd } // namespace clang