Author: Miko
Date: 2026-08-25T03:59:46Z
New Revision: b5240b889b6ca55af51d246a1f43e7feba9483ce

URL: 
https://github.com/llvm/llvm-project/commit/b5240b889b6ca55af51d246a1f43e7feba9483ce
DIFF: 
https://github.com/llvm/llvm-project/commit/b5240b889b6ca55af51d246a1f43e7feba9483ce.diff

LOG: [Sema] [Modules] Remove unrelated module partitions from suggestion list 
(#187657)

This PR removes module partitions that don't belong to the current
file's module from the list of suggested module imports in the code
completion, so that it contains only primary modules or partitions
directly relevant to the declared primary module.

Partially addresses clangd/clangd#2622.

Added: 
    clang/test/CodeCompletion/module-partitions.cpp

Modified: 
    clang/lib/Lex/PPDirectives.cpp
    clang/lib/Lex/Preprocessor.cpp
    clang/lib/Sema/SemaCodeComplete.cpp

Removed: 
    


################################################################################
diff  --git a/clang/lib/Lex/PPDirectives.cpp b/clang/lib/Lex/PPDirectives.cpp
index 29236bd0f4782..b431fb8e1d221 100644
--- a/clang/lib/Lex/PPDirectives.cpp
+++ b/clang/lib/Lex/PPDirectives.cpp
@@ -4226,6 +4226,7 @@ void Preprocessor::HandleCXXImportDirective(Token 
ImportTok) {
     UseLoc = Tok.getLocation();
     Lex(Tok);
     [[fallthrough]];
+  case tok::code_completion:
   case tok::identifier: {
     if (HandleModuleName(ImportTok.getIdentifierInfo()->getName(), UseLoc, Tok,
                          Path, DirToks, /*AllowMacroExpansion=*/true,

diff  --git a/clang/lib/Lex/Preprocessor.cpp b/clang/lib/Lex/Preprocessor.cpp
index 62d67e08e6083..780f7936cd00c 100644
--- a/clang/lib/Lex/Preprocessor.cpp
+++ b/clang/lib/Lex/Preprocessor.cpp
@@ -1380,7 +1380,7 @@ bool Preprocessor::HandleModuleContextualKeyword(Token 
&Result) {
       if (NextTok->is(tok::raw_identifier))
         LookUpIdentifierInfo(*NextTok);
       if (NextTok->isOneOf(tok::header_name, tok::identifier, tok::colon,
-                           tok::less)) {
+                           tok::less, tok::code_completion)) {
         Result.setKind(tok::kw_import);
         ModuleImportLoc = Result.getLocation();
         return true;

diff  --git a/clang/lib/Sema/SemaCodeComplete.cpp 
b/clang/lib/Sema/SemaCodeComplete.cpp
index 4b9b604d27b16..b906a67f4e45e 100644
--- a/clang/lib/Sema/SemaCodeComplete.cpp
+++ b/clang/lib/Sema/SemaCodeComplete.cpp
@@ -30,6 +30,7 @@
 #include "clang/Basic/OperatorKinds.h"
 #include "clang/Basic/Specifiers.h"
 #include "clang/Lex/HeaderSearch.h"
+#include "clang/Lex/HeaderSearchOptions.h"
 #include "clang/Lex/MacroInfo.h"
 #include "clang/Lex/Preprocessor.h"
 #include "clang/Sema/CodeCompleteConsumer.h"
@@ -50,6 +51,7 @@
 #include "llvm/ADT/SmallBitVector.h"
 #include "llvm/ADT/SmallPtrSet.h"
 #include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringSet.h"
 #include "llvm/ADT/StringSwitch.h"
 #include "llvm/ADT/Twine.h"
 #include "llvm/ADT/iterator_range.h"
@@ -4741,13 +4743,52 @@ void 
SemaCodeCompletion::CodeCompleteModuleImport(SourceLocation ImportLoc,
     // Enumerate all top-level modules.
     SmallVector<Module *, 8> Modules;
     SemaRef.PP.getHeaderSearchInfo().collectAllModules(Modules);
+    // Determine the primary module interface name of the current file's
+    // declared module, if any. Prefer Sema's view, but fall back to the
+    // preprocessor's module declaration state: module declarations are
+    // processed as preprocessor directives, so the preprocessor may know the
+    // declared module before Sema has acted on it (e.g. when completing an
+    // import right after the module declaration).
+    StringRef CurrentPrimary;
+    if (Module *CurrentModule = SemaRef.getCurrentModule())
+      CurrentPrimary = CurrentModule->getPrimaryModuleInterfaceName();
+    else if (SemaRef.PP.isInNamedModule())
+      CurrentPrimary = SemaRef.PP.getNamedModuleName().split(':').first;
+    llvm::StringSet<> AddedModules;
     for (unsigned I = 0, N = Modules.size(); I != N; ++I) {
+      // Skip module partitions that don't belong to the current file's 
declared
+      // module.
+      if (Modules[I]->isModulePartition()) {
+        if (CurrentPrimary.empty() ||
+            Modules[I]->getPrimaryModuleInterfaceName() != CurrentPrimary)
+          continue;
+      }
       Builder.AddTypedTextChunk(
           Builder.getAllocator().CopyString(Modules[I]->Name));
       Results.AddResult(Result(
           Builder.TakeString(), CCP_Declaration, CXCursor_ModuleImportDecl,
           Modules[I]->isAvailable() ? CXAvailability_Available
                                     : CXAvailability_NotAvailable));
+      AddedModules.insert(Modules[I]->Name);
+    }
+
+    // Also suggest C++20 named modules from -fmodule-file=<name>=<path> that
+    // haven't been loaded into the module map yet.
+    for (const auto &Entry : SemaRef.PP.getHeaderSearchInfo()
+                                 .getHeaderSearchOpts()
+                                 .PrebuiltModuleFiles) {
+      if (AddedModules.count(Entry.first))
+        continue;
+      StringRef Name = Entry.first;
+      // Apply the same partition filtering as above.
+      if (auto [Primary, Partition] = Name.split(':'); !Partition.empty()) {
+        if (CurrentPrimary.empty() || Primary != CurrentPrimary)
+          continue;
+      }
+      Builder.AddTypedTextChunk(Builder.getAllocator().CopyString(Name));
+      Results.AddResult(Result(Builder.TakeString(), CCP_Declaration,
+                               CXCursor_ModuleImportDecl,
+                               CXAvailability_Available));
     }
   } else if (getLangOpts().Modules) {
     // Load the named module.

diff  --git a/clang/test/CodeCompletion/module-partitions.cpp 
b/clang/test/CodeCompletion/module-partitions.cpp
new file mode 100644
index 0000000000000..698d18eea7f1b
--- /dev/null
+++ b/clang/test/CodeCompletion/module-partitions.cpp
@@ -0,0 +1,26 @@
+// RUN: rm -rf %t && mkdir %t
+
+// Set up a partition of module M.
+// RUN: printf 'export module M:Part;\nexport int mpart_func();\n' > 
%t/m-part.cppm
+// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/m-part.cppm -o 
%t/M-Part.pcm
+
+// Set up module N with a partition.
+// RUN: printf 'export module N:OtherPart;\nexport int npart_func();\n' > 
%t/n-otherpart.cppm
+// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/n-otherpart.cppm -o 
%t/N-OtherPart.pcm
+// RUN: printf 'export module N;\nexport import :OtherPart;\n' > %t/n.cppm
+// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/n.cppm 
-fmodule-file=N:OtherPart=%t/N-OtherPart.pcm -o %t/N.pcm
+
+// Complete at the module name position in an "import" inside module M.
+// Own partition (M:Part) should be suggested; another module's partition
+// (N:OtherPart) should be filtered out.
+// RUN: %clang_cc1 -std=c++20 -code-completion-at=%s:%(line+6):8 %s \
+// RUN:   -fmodule-file=M:Part=%t/M-Part.pcm \
+// RUN:   -fmodule-file=N=%t/N.pcm \
+// RUN:   -fmodule-file=N:OtherPart=%t/N-OtherPart.pcm | FileCheck %s
+
+export module M;
+import ;
+
+// CHECK-NOT: OtherPart
+// CHECK: M:Part
+// CHECK-NOT: OtherPart


        
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to