[clang-tools-extra] [clangd] [C++20] [Modules] Support code complete for C++20 modules (PR #110083)
Chuanqi Xu via cfe-commits
cfe-commits at lists.llvm.org
Sat Sep 28 19:10:25 PDT 2024
https://github.com/ChuanqiXu9 updated https://github.com/llvm/llvm-project/pull/110083
>From e35e600159c99736de7d2bc735c738002f592988 Mon Sep 17 00:00:00 2001
From: Chuanqi Xu <yedeng.yd at linux.alibaba.com>
Date: Thu, 26 Sep 2024 13:43:51 +0800
Subject: [PATCH 1/3] [clangd] [C++20] [Modules] Support code complete for
C++20 modules
---
clang-tools-extra/clangd/CodeComplete.cpp | 18 ++++++---
clang-tools-extra/clangd/CodeComplete.h | 2 +-
.../unittests/PrerequisiteModulesTest.cpp | 40 +++++++++++++++++++
3 files changed, 54 insertions(+), 6 deletions(-)
diff --git a/clang-tools-extra/clangd/CodeComplete.cpp b/clang-tools-extra/clangd/CodeComplete.cpp
index 89eee392837af4..f6f0d9a1be3c37 100644
--- a/clang-tools-extra/clangd/CodeComplete.cpp
+++ b/clang-tools-extra/clangd/CodeComplete.cpp
@@ -892,8 +892,9 @@ static bool isExcludedMember(const NamedDecl &D) {
// within the callback.
struct CompletionRecorder : public CodeCompleteConsumer {
CompletionRecorder(const CodeCompleteOptions &Opts,
+ bool ForceLoadExternal,
llvm::unique_function<void()> ResultsCallback)
- : CodeCompleteConsumer(Opts.getClangCompleteOpts()),
+ : CodeCompleteConsumer(Opts.getClangCompleteOpts(ForceLoadExternal)),
CCContext(CodeCompletionContext::CCC_Other), Opts(Opts),
CCAllocator(std::make_shared<GlobalCodeCompletionAllocator>()),
CCTUInfo(CCAllocator), ResultsCallback(std::move(ResultsCallback)) {
@@ -1409,6 +1410,9 @@ bool semaCodeComplete(std::unique_ptr<CodeCompleteConsumer> Consumer,
Clang->getPreprocessorOpts().SingleFileParseMode = CompletingInPreamble;
Clang->setCodeCompletionConsumer(Consumer.release());
+ if (Input.Preamble.RequiredModules)
+ Input.Preamble.RequiredModules->adjustHeaderSearchOptions(Clang->getHeaderSearchOpts());
+
SyntaxOnlyAction Action;
if (!Action.BeginSourceFile(*Clang, Clang->getFrontendOpts().Inputs[0])) {
log("BeginSourceFile() failed when running codeComplete for {0}",
@@ -1629,11 +1633,15 @@ class CodeCompleteFlow {
SpecFuzzyFind->Result = startAsyncFuzzyFind(*Opts.Index, *SpecReq);
}
+ // FIXME: If we're using C++20 modules, force the lookup process to load external decls,
+ // since currently the index doesn't support C++20 modules.
+ bool ForceLoadExternal = (bool)SemaCCInput.Preamble.RequiredModules;
+
// We run Sema code completion first. It builds an AST and calculates:
// - completion results based on the AST.
// - partial identifier and context. We need these for the index query.
CodeCompleteResult Output;
- auto RecorderOwner = std::make_unique<CompletionRecorder>(Opts, [&]() {
+ auto RecorderOwner = std::make_unique<CompletionRecorder>(Opts, ForceLoadExternal, [&]() {
assert(Recorder && "Recorder is not set");
CCContextKind = Recorder->CCContext.getKind();
IsUsingDeclaration = Recorder->CCContext.isUsingDeclaration();
@@ -1691,7 +1699,7 @@ class CodeCompleteFlow {
Recorder = RecorderOwner.get();
- semaCodeComplete(std::move(RecorderOwner), Opts.getClangCompleteOpts(),
+ semaCodeComplete(std::move(RecorderOwner), Opts.getClangCompleteOpts(ForceLoadExternal),
SemaCCInput, &Includes);
logResults(Output, Tracer);
return Output;
@@ -2108,7 +2116,7 @@ class CodeCompleteFlow {
} // namespace
-clang::CodeCompleteOptions CodeCompleteOptions::getClangCompleteOpts() const {
+clang::CodeCompleteOptions CodeCompleteOptions::getClangCompleteOpts(bool ForceLoadExternal) const {
clang::CodeCompleteOptions Result;
Result.IncludeCodePatterns = EnableSnippets;
Result.IncludeMacros = true;
@@ -2122,7 +2130,7 @@ clang::CodeCompleteOptions CodeCompleteOptions::getClangCompleteOpts() const {
// When an is used, Sema is responsible for completing the main file,
// the index can provide results from the preamble.
// Tell Sema not to deserialize the preamble to look for results.
- Result.LoadExternal = !Index;
+ Result.LoadExternal = ForceLoadExternal || !Index;
Result.IncludeFixIts = IncludeFixIts;
return Result;
diff --git a/clang-tools-extra/clangd/CodeComplete.h b/clang-tools-extra/clangd/CodeComplete.h
index a7c1ae95dcbf49..336e84f0a7724c 100644
--- a/clang-tools-extra/clangd/CodeComplete.h
+++ b/clang-tools-extra/clangd/CodeComplete.h
@@ -41,7 +41,7 @@ struct CodeCompletion;
struct CodeCompleteOptions {
/// Returns options that can be passed to clang's completion engine.
- clang::CodeCompleteOptions getClangCompleteOpts() const;
+ clang::CodeCompleteOptions getClangCompleteOpts(bool ForceLoadExternal) const;
/// When true, completion items will contain expandable code snippets in
/// completion (e.g. `return ${1:expression}` or `foo(${1:int a}, ${2:int
diff --git a/clang-tools-extra/clangd/unittests/PrerequisiteModulesTest.cpp b/clang-tools-extra/clangd/unittests/PrerequisiteModulesTest.cpp
index 7bbb95c8b8d67f..f1cdf9e899f4d8 100644
--- a/clang-tools-extra/clangd/unittests/PrerequisiteModulesTest.cpp
+++ b/clang-tools-extra/clangd/unittests/PrerequisiteModulesTest.cpp
@@ -402,6 +402,46 @@ import A;
EXPECT_TRUE(D.isFromASTFile());
}
+// An end to end test for code complete in modules
+TEST_F(PrerequisiteModulesTests, CodeCompleteTest) {
+ MockDirectoryCompilationDatabase CDB(TestDir, FS);
+
+ CDB.addFile("A.cppm", R"cpp(
+export module A;
+export void printA();
+ )cpp");
+
+ llvm::StringLiteral UserContents = R"cpp(
+import A;
+void func() {
+ print^
+}
+)cpp";
+
+ CDB.addFile("Use.cpp", UserContents);
+ Annotations Test(UserContents);
+
+ ModulesBuilder Builder(CDB);
+
+ ParseInputs Use = getInputs("Use.cpp", CDB);
+ Use.ModulesManager = &Builder;
+
+ std::unique_ptr<CompilerInvocation> CI =
+ buildCompilerInvocation(Use, DiagConsumer);
+ EXPECT_TRUE(CI);
+
+ auto Preamble =
+ buildPreamble(getFullPath("Use.cpp"), *CI, Use, /*InMemory=*/true,
+ /*Callback=*/nullptr);
+ EXPECT_TRUE(Preamble);
+ EXPECT_TRUE(Preamble->RequiredModules);
+
+ auto Result = codeComplete(getFullPath("Use.cpp"), Test.point(),
+ Preamble.get(), Use, {});
+ EXPECT_FALSE(Result.Completions.empty());
+ EXPECT_EQ(Result.Completions[0].Name, "printA");
+}
+
} // namespace
} // namespace clang::clangd
>From d07a5435fcc0d405bc1b5ecacbeaf313501c7c96 Mon Sep 17 00:00:00 2001
From: Chuanqi Xu <yedeng.yd at linux.alibaba.com>
Date: Fri, 27 Sep 2024 10:21:39 +0800
Subject: [PATCH 2/3] Update
---
.../unittests/PrerequisiteModulesTest.cpp | 40 +++++++++++++++++++
1 file changed, 40 insertions(+)
diff --git a/clang-tools-extra/clangd/unittests/PrerequisiteModulesTest.cpp b/clang-tools-extra/clangd/unittests/PrerequisiteModulesTest.cpp
index f1cdf9e899f4d8..691a93e7acd0af 100644
--- a/clang-tools-extra/clangd/unittests/PrerequisiteModulesTest.cpp
+++ b/clang-tools-extra/clangd/unittests/PrerequisiteModulesTest.cpp
@@ -442,6 +442,46 @@ void func() {
EXPECT_EQ(Result.Completions[0].Name, "printA");
}
+TEST_F(PrerequisiteModulesTests, SignatureHelpTest) {
+ MockDirectoryCompilationDatabase CDB(TestDir, FS);
+
+ CDB.addFile("A.cppm", R"cpp(
+export module A;
+export void printA(int a);
+ )cpp");
+
+ llvm::StringLiteral UserContents = R"cpp(
+import A;
+void func() {
+ printA(^);
+}
+)cpp";
+
+ CDB.addFile("Use.cpp", UserContents);
+ Annotations Test(UserContents);
+
+ ModulesBuilder Builder(CDB);
+
+ ParseInputs Use = getInputs("Use.cpp", CDB);
+ Use.ModulesManager = &Builder;
+
+ std::unique_ptr<CompilerInvocation> CI =
+ buildCompilerInvocation(Use, DiagConsumer);
+ EXPECT_TRUE(CI);
+
+ auto Preamble =
+ buildPreamble(getFullPath("Use.cpp"), *CI, Use, /*InMemory=*/true,
+ /*Callback=*/nullptr);
+ EXPECT_TRUE(Preamble);
+ EXPECT_TRUE(Preamble->RequiredModules);
+
+ auto Result = signatureHelp(getFullPath("Use.cpp"), Test.point(),
+ *Preamble.get(), Use, MarkupKind::PlainText);
+ EXPECT_FALSE(Result.signatures.empty());
+ EXPECT_EQ(Result.signatures[0].label, "printA(int a) -> void");
+ EXPECT_EQ(Result.signatures[0].parameters[0].labelString, "int a");
+}
+
} // namespace
} // namespace clang::clangd
>From c1755db71dff183f5000e6ed0592889a1a2deff8 Mon Sep 17 00:00:00 2001
From: Chuanqi Xu <yedeng.yd at linux.alibaba.com>
Date: Sun, 29 Sep 2024 10:09:59 +0800
Subject: [PATCH 3/3] update
---
clang-tools-extra/clangd/CodeComplete.cpp | 18 ++++++++----------
clang-tools-extra/clangd/CodeComplete.h | 5 ++++-
clang-tools-extra/clangd/tool/ClangdMain.cpp | 1 +
3 files changed, 13 insertions(+), 11 deletions(-)
diff --git a/clang-tools-extra/clangd/CodeComplete.cpp b/clang-tools-extra/clangd/CodeComplete.cpp
index f6f0d9a1be3c37..5553a33338bb60 100644
--- a/clang-tools-extra/clangd/CodeComplete.cpp
+++ b/clang-tools-extra/clangd/CodeComplete.cpp
@@ -892,9 +892,8 @@ static bool isExcludedMember(const NamedDecl &D) {
// within the callback.
struct CompletionRecorder : public CodeCompleteConsumer {
CompletionRecorder(const CodeCompleteOptions &Opts,
- bool ForceLoadExternal,
llvm::unique_function<void()> ResultsCallback)
- : CodeCompleteConsumer(Opts.getClangCompleteOpts(ForceLoadExternal)),
+ : CodeCompleteConsumer(Opts.getClangCompleteOpts()),
CCContext(CodeCompletionContext::CCC_Other), Opts(Opts),
CCAllocator(std::make_shared<GlobalCodeCompletionAllocator>()),
CCTUInfo(CCAllocator), ResultsCallback(std::move(ResultsCallback)) {
@@ -1633,15 +1632,11 @@ class CodeCompleteFlow {
SpecFuzzyFind->Result = startAsyncFuzzyFind(*Opts.Index, *SpecReq);
}
- // FIXME: If we're using C++20 modules, force the lookup process to load external decls,
- // since currently the index doesn't support C++20 modules.
- bool ForceLoadExternal = (bool)SemaCCInput.Preamble.RequiredModules;
-
// We run Sema code completion first. It builds an AST and calculates:
// - completion results based on the AST.
// - partial identifier and context. We need these for the index query.
CodeCompleteResult Output;
- auto RecorderOwner = std::make_unique<CompletionRecorder>(Opts, ForceLoadExternal, [&]() {
+ auto RecorderOwner = std::make_unique<CompletionRecorder>(Opts, [&]() {
assert(Recorder && "Recorder is not set");
CCContextKind = Recorder->CCContext.getKind();
IsUsingDeclaration = Recorder->CCContext.isUsingDeclaration();
@@ -1699,7 +1694,7 @@ class CodeCompleteFlow {
Recorder = RecorderOwner.get();
- semaCodeComplete(std::move(RecorderOwner), Opts.getClangCompleteOpts(ForceLoadExternal),
+ semaCodeComplete(std::move(RecorderOwner), Opts.getClangCompleteOpts(),
SemaCCInput, &Includes);
logResults(Output, Tracer);
return Output;
@@ -2116,7 +2111,7 @@ class CodeCompleteFlow {
} // namespace
-clang::CodeCompleteOptions CodeCompleteOptions::getClangCompleteOpts(bool ForceLoadExternal) const {
+clang::CodeCompleteOptions CodeCompleteOptions::getClangCompleteOpts() const {
clang::CodeCompleteOptions Result;
Result.IncludeCodePatterns = EnableSnippets;
Result.IncludeMacros = true;
@@ -2130,7 +2125,10 @@ clang::CodeCompleteOptions CodeCompleteOptions::getClangCompleteOpts(bool ForceL
// When an is used, Sema is responsible for completing the main file,
// the index can provide results from the preamble.
// Tell Sema not to deserialize the preamble to look for results.
- Result.LoadExternal = ForceLoadExternal || !Index;
+ //
+ // FIXME: If we're using C++20 modules, force the lookup process to load external decls,
+ // since currently the index doesn't support C++20 modules.
+ Result.LoadExternal = ExperimentalModulesSupport || !Index;
Result.IncludeFixIts = IncludeFixIts;
return Result;
diff --git a/clang-tools-extra/clangd/CodeComplete.h b/clang-tools-extra/clangd/CodeComplete.h
index 336e84f0a7724c..f78473e1189194 100644
--- a/clang-tools-extra/clangd/CodeComplete.h
+++ b/clang-tools-extra/clangd/CodeComplete.h
@@ -41,7 +41,7 @@ struct CodeCompletion;
struct CodeCompleteOptions {
/// Returns options that can be passed to clang's completion engine.
- clang::CodeCompleteOptions getClangCompleteOpts(bool ForceLoadExternal) const;
+ clang::CodeCompleteOptions getClangCompleteOpts() const;
/// When true, completion items will contain expandable code snippets in
/// completion (e.g. `return ${1:expression}` or `foo(${1:int a}, ${2:int
@@ -52,6 +52,9 @@ struct CodeCompleteOptions {
/// For example, private members are usually inaccessible.
bool IncludeIneligibleResults = false;
+ /// Whether the experimental modules support are enabled.
+ bool ExperimentalModulesSupport = false;
+
/// Combine overloads into a single completion item where possible.
/// If none, the implementation may choose an appropriate behavior.
/// (In practice, ClangdLSPServer enables bundling if the client claims
diff --git a/clang-tools-extra/clangd/tool/ClangdMain.cpp b/clang-tools-extra/clangd/tool/ClangdMain.cpp
index 3a5449ac8c7994..9aa3c8855ed526 100644
--- a/clang-tools-extra/clangd/tool/ClangdMain.cpp
+++ b/clang-tools-extra/clangd/tool/ClangdMain.cpp
@@ -919,6 +919,7 @@ clangd accepts flags on the commandline, and in the CLANGD_FLAGS environment var
Opts.CodeComplete.EnableFunctionArgSnippets = EnableFunctionArgSnippets;
Opts.CodeComplete.RunParser = CodeCompletionParse;
Opts.CodeComplete.RankingModel = RankingModel;
+ Opts.CodeComplete.ExperimentalModulesSupport = ExperimentalModulesSupport;
RealThreadsafeFS TFS;
std::vector<std::unique_ptr<config::Provider>> ProviderStack;
More information about the cfe-commits
mailing list