diff --git a/clang-tools-extra/clangd/ClangdServer.cpp b/clang-tools-extra/clangd/ClangdServer.cpp index 36af6c98d18b4b..0882784f7031e9 100644 --- a/clang-tools-extra/clangd/ClangdServer.cpp +++ b/clang-tools-extra/clangd/ClangdServer.cpp @@ -134,7 +134,8 @@ ClangdServer::ClangdServer(const GlobalCompilationDatabase &CDB, : nullptr), GetClangTidyOptions(Opts.GetClangTidyOptions), SuggestMissingIncludes(Opts.SuggestMissingIncludes), - TweakFilter(Opts.TweakFilter), WorkspaceRoot(Opts.WorkspaceRoot), + BuildRecoveryAST(Opts.BuildRecoveryAST), TweakFilter(Opts.TweakFilter), + WorkspaceRoot(Opts.WorkspaceRoot), // Pass a callback into `WorkScheduler` to extract symbols from a newly // parsed file and rebuild the file index synchronously each time an AST // is parsed. @@ -191,6 +192,7 @@ void ClangdServer::addDocument(PathRef File, llvm::StringRef Contents, Inputs.ForceRebuild = ForceRebuild; Inputs.Opts = std::move(Opts); Inputs.Index = Index; + Inputs.Opts.BuildRecoveryAST = BuildRecoveryAST; bool NewFile = WorkScheduler.update(File, Inputs, WantDiags); // If we loaded Foo.h, we want to make sure Foo.cpp is indexed. if (NewFile && BackgroundIdx) diff --git a/clang-tools-extra/clangd/ClangdServer.h b/clang-tools-extra/clangd/ClangdServer.h index a0659c7c3d229c..f1e981e6c14f13 100644 --- a/clang-tools-extra/clangd/ClangdServer.h +++ b/clang-tools-extra/clangd/ClangdServer.h @@ -118,6 +118,9 @@ class ClangdServer { /// enabled. ClangTidyOptionsBuilder GetClangTidyOptions; + /// If true, turn on the `-frecovery-ast` clang flag. + bool BuildRecoveryAST = false; + /// Clangd's workspace root. Relevant for "workspace" operations not bound /// to a particular file. /// FIXME: If not set, should use the current working directory. @@ -345,6 +348,9 @@ class ClangdServer { // can be caused by missing includes (e.g. member access in incomplete type). bool SuggestMissingIncludes = false; + // If true, preserve expressions in AST for broken code. + bool BuildRecoveryAST = false; + std::function TweakFilter; // GUARDED_BY(CachedCompletionFuzzyFindRequestMutex) diff --git a/clang-tools-extra/clangd/Compiler.h b/clang-tools-extra/clangd/Compiler.h index ef5386bb0d17b3..b7cc174455f317 100644 --- a/clang-tools-extra/clangd/Compiler.h +++ b/clang-tools-extra/clangd/Compiler.h @@ -38,6 +38,7 @@ class IgnoreDiagnostics : public DiagnosticConsumer { struct ParseOptions { tidy::ClangTidyOptions ClangTidyOpts; bool SuggestMissingIncludes = false; + bool BuildRecoveryAST = false; }; /// Information required to run clang, e.g. to parse AST or do code completion. diff --git a/clang-tools-extra/clangd/ParsedAST.cpp b/clang-tools-extra/clangd/ParsedAST.cpp index 1d6997f0b4d4d7..2c7cb5d2b85db0 100644 --- a/clang-tools-extra/clangd/ParsedAST.cpp +++ b/clang-tools-extra/clangd/ParsedAST.cpp @@ -253,6 +253,10 @@ ParsedAST::build(llvm::StringRef Version, const PrecompiledPreamble *PreamblePCH = Preamble ? &Preamble->Preamble : nullptr; + // Recovery expression currently only works for C++. + if (CI->getLangOpts()->CPlusPlus) + CI->getLangOpts()->RecoveryAST = Opts.BuildRecoveryAST; + StoreDiags ASTDiags; std::string Content = std::string(Buffer->getBuffer()); std::string Filename = diff --git a/clang-tools-extra/clangd/Preamble.cpp b/clang-tools-extra/clangd/Preamble.cpp index fdee71fd22449a..48f15420032fa9 100644 --- a/clang-tools-extra/clangd/Preamble.cpp +++ b/clang-tools-extra/clangd/Preamble.cpp @@ -132,6 +132,10 @@ buildPreamble(PathRef FileName, CompilerInvocation &CI, // to read back. We rely on dynamic index for the comments instead. CI.getPreprocessorOpts().WriteCommentListToPCH = false; + // Recovery expression currently only works for C++. + if (CI.getLangOpts()->CPlusPlus) + CI.getLangOpts()->RecoveryAST = Inputs.Opts.BuildRecoveryAST; + CppFilePreambleCallbacks SerializedDeclsCollector(FileName, PreambleCallback); if (Inputs.FS->setCurrentWorkingDirectory(Inputs.CompileCommand.Directory)) { log("Couldn't set working directory when building the preamble."); diff --git a/clang-tools-extra/clangd/tool/ClangdMain.cpp b/clang-tools-extra/clangd/tool/ClangdMain.cpp index 7a7bb9b0718e02..9bfc58b55f713e 100644 --- a/clang-tools-extra/clangd/tool/ClangdMain.cpp +++ b/clang-tools-extra/clangd/tool/ClangdMain.cpp @@ -281,6 +281,15 @@ opt CrossFileRename{ Hidden, }; +opt RecoveryAST{ + "recovery-ast", + cat(Features), + desc("Preserve expressions in AST for broken code (C++ only). Note that " + "this feature is experimental and may lead to crashes"), + init(false), + Hidden, +}; + opt WorkerThreadsCount{ "j", cat(Misc), @@ -629,6 +638,7 @@ clangd accepts flags on the commandline, and in the CLANGD_FLAGS environment var } Opts.StaticIndex = StaticIdx.get(); Opts.AsyncThreadsCount = WorkerThreadsCount; + Opts.BuildRecoveryAST = RecoveryAST; clangd::CodeCompleteOptions CCOpts; CCOpts.IncludeIneligibleResults = IncludeIneligibleResults; diff --git a/clang-tools-extra/clangd/unittests/FindTargetTests.cpp b/clang-tools-extra/clangd/unittests/FindTargetTests.cpp index 53d29a236a0728..7b6fff292e6612 100644 --- a/clang-tools-extra/clangd/unittests/FindTargetTests.cpp +++ b/clang-tools-extra/clangd/unittests/FindTargetTests.cpp @@ -132,6 +132,16 @@ TEST_F(TargetDeclTest, Exprs) { EXPECT_DECLS("CXXOperatorCallExpr", "void operator()(int n)"); } +TEST_F(TargetDeclTest, Recovery) { + Code = R"cpp( + // error-ok: testing behavior on broken code + int f(); + int f(int, int); + int x = [[f]](42); + )cpp"; + EXPECT_DECLS("UnresolvedLookupExpr", "int f()", "int f(int, int)"); +} + TEST_F(TargetDeclTest, UsingDecl) { Code = R"cpp( namespace foo { @@ -685,6 +695,15 @@ TEST_F(FindExplicitReferencesTest, All) { )cpp", "0: targets = {x}\n" "1: targets = {X::a}\n"}, + {R"cpp( + // error-ok: testing with broken code + int bar(); + int foo() { + return $0^bar() + $1^bar(42); + } + )cpp", + "0: targets = {bar}\n" + "1: targets = {bar}\n"}, // Namespaces and aliases. {R"cpp( namespace ns {} diff --git a/clang-tools-extra/clangd/unittests/TestTU.cpp b/clang-tools-extra/clangd/unittests/TestTU.cpp index dfcdb0884e27c6..2adcfc338cc2e4 100644 --- a/clang-tools-extra/clangd/unittests/TestTU.cpp +++ b/clang-tools-extra/clangd/unittests/TestTU.cpp @@ -56,6 +56,7 @@ ParseInputs TestTU::inputs() const { Inputs.Contents = Code; Inputs.FS = buildTestFS(Files); Inputs.Opts = ParseOptions(); + Inputs.Opts.BuildRecoveryAST = true; Inputs.Opts.ClangTidyOpts.Checks = ClangTidyChecks; Inputs.Opts.ClangTidyOpts.WarningsAsErrors = ClangTidyWarningsAsErrors; Inputs.Index = ExternalIndex;