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
