diff --git a/clang-tools-extra/clangd/ClangdServer.h b/clang-tools-extra/clangd/ClangdServer.h --- a/clang-tools-extra/clangd/ClangdServer.h +++ b/clang-tools-extra/clangd/ClangdServer.h @@ -120,6 +120,9 @@ /// If true, turn on the `-frecovery-ast` clang flag. bool BuildRecoveryAST = false; + /// If true, turn on the `-frecovery-ast-type` clang flag. + bool PreserveRecoveryASTType = 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. @@ -349,6 +352,8 @@ // If true, preserve expressions in AST for broken code. bool BuildRecoveryAST = false; + // If true, preserve the type for recovery AST. + bool PreserveRecoveryASTType = false; std::function TweakFilter; diff --git a/clang-tools-extra/clangd/ClangdServer.cpp b/clang-tools-extra/clangd/ClangdServer.cpp --- a/clang-tools-extra/clangd/ClangdServer.cpp +++ b/clang-tools-extra/clangd/ClangdServer.cpp @@ -135,8 +135,9 @@ : nullptr), GetClangTidyOptions(Opts.GetClangTidyOptions), SuggestMissingIncludes(Opts.SuggestMissingIncludes), - BuildRecoveryAST(Opts.BuildRecoveryAST), TweakFilter(Opts.TweakFilter), - WorkspaceRoot(Opts.WorkspaceRoot), + BuildRecoveryAST(Opts.BuildRecoveryAST), + PreserveRecoveryASTType(Opts.PreserveRecoveryASTType), + 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. @@ -194,6 +195,7 @@ Inputs.Opts = std::move(Opts); Inputs.Index = Index; Inputs.Opts.BuildRecoveryAST = BuildRecoveryAST; + Inputs.Opts.PreserveRecoveryASTType = PreserveRecoveryASTType; 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/CodeComplete.cpp b/clang-tools-extra/clangd/CodeComplete.cpp --- a/clang-tools-extra/clangd/CodeComplete.cpp +++ b/clang-tools-extra/clangd/CodeComplete.cpp @@ -1069,6 +1069,7 @@ ParseInput.CompileCommand = Input.Command; ParseInput.FS = VFS; ParseInput.Contents = std::string(Input.Contents); + // FIXME: setup the recoveryAST and recoveryASTType in ParseInput properly. IgnoreDiagnostics IgnoreDiags; auto CI = buildCompilerInvocation(ParseInput, IgnoreDiags); diff --git a/clang-tools-extra/clangd/Compiler.h b/clang-tools-extra/clangd/Compiler.h --- a/clang-tools-extra/clangd/Compiler.h +++ b/clang-tools-extra/clangd/Compiler.h @@ -39,6 +39,7 @@ tidy::ClangTidyOptions ClangTidyOpts; bool SuggestMissingIncludes = false; bool BuildRecoveryAST = false; + bool PreserveRecoveryASTType = false; }; /// Information required to run clang, e.g. to parse AST or do code completion. diff --git a/clang-tools-extra/clangd/Compiler.cpp b/clang-tools-extra/clangd/Compiler.cpp --- a/clang-tools-extra/clangd/Compiler.cpp +++ b/clang-tools-extra/clangd/Compiler.cpp @@ -84,8 +84,10 @@ CI->getPreprocessorOpts().PCHWithHdrStopCreate = false; // Recovery expression currently only works for C++. - if (CI->getLangOpts()->CPlusPlus) + if (CI->getLangOpts()->CPlusPlus) { CI->getLangOpts()->RecoveryAST = Inputs.Opts.BuildRecoveryAST; + CI->getLangOpts()->RecoveryASTType = Inputs.Opts.PreserveRecoveryASTType; + } return CI; } diff --git a/clang-tools-extra/clangd/tool/ClangdMain.cpp b/clang-tools-extra/clangd/tool/ClangdMain.cpp --- a/clang-tools-extra/clangd/tool/ClangdMain.cpp +++ b/clang-tools-extra/clangd/tool/ClangdMain.cpp @@ -289,6 +289,14 @@ init(false), Hidden, }; +opt RecoveryASTType{ + "recovery-ast-type", + cat(Features), + desc("Preserve the type for recovery AST. Note that " + "this feature is experimental and may lead to crashes"), + init(false), + Hidden, +}; opt WorkerThreadsCount{ "j", @@ -636,6 +644,7 @@ Opts.StaticIndex = StaticIdx.get(); Opts.AsyncThreadsCount = WorkerThreadsCount; Opts.BuildRecoveryAST = RecoveryAST; + Opts.PreserveRecoveryASTType = RecoveryASTType; clangd::CodeCompleteOptions CCOpts; CCOpts.IncludeIneligibleResults = IncludeIneligibleResults; diff --git a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp --- a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp +++ b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp @@ -752,6 +752,19 @@ EXPECT_THAT(Results, ElementsAre(Named("ifndef"))); } +// FIXME: enable it. +TEST(CompletionTest, DISABLED_CompletionRecoveryASTType) { + auto Results = completions(R"cpp( + struct S { int member; }; + S overloaded(int); + void foo() { + // No overload matches, but we have recovery-expr with the correct type. + overloaded().^ + })cpp") + .Completions; + EXPECT_THAT(Results, ElementsAre(Named("member"))); +} + TEST(CompletionTest, DynamicIndexIncludeInsertion) { MockFSProvider FS; MockCompilationDatabase CDB; diff --git a/clang-tools-extra/clangd/unittests/FindTargetTests.cpp b/clang-tools-extra/clangd/unittests/FindTargetTests.cpp --- a/clang-tools-extra/clangd/unittests/FindTargetTests.cpp +++ b/clang-tools-extra/clangd/unittests/FindTargetTests.cpp @@ -158,6 +158,19 @@ EXPECT_DECLS("UnresolvedLookupExpr", "int f()", "int f(int, int)"); } +TEST_F(TargetDeclTest, RecoveryType) { + Code = R"cpp( + // error-ok: testing behavior on broken code + struct S { int member; }; + S overloaded(int); + void foo() { + // No overload matches, but we have recovery-expr with the correct type. + overloaded().[[member]]; + } + )cpp"; + EXPECT_DECLS("MemberExpr", "int member"); +} + TEST_F(TargetDeclTest, UsingDecl) { Code = R"cpp( namespace foo { diff --git a/clang-tools-extra/clangd/unittests/TestTU.cpp b/clang-tools-extra/clangd/unittests/TestTU.cpp --- a/clang-tools-extra/clangd/unittests/TestTU.cpp +++ b/clang-tools-extra/clangd/unittests/TestTU.cpp @@ -57,6 +57,7 @@ Inputs.FS = buildTestFS(Files); Inputs.Opts = ParseOptions(); Inputs.Opts.BuildRecoveryAST = true; + Inputs.Opts.PreserveRecoveryASTType = true; Inputs.Opts.ClangTidyOpts.Checks = ClangTidyChecks; Inputs.Opts.ClangTidyOpts.WarningsAsErrors = ClangTidyWarningsAsErrors; Inputs.Index = ExternalIndex;