Skip to content

Commit 67c67a0

Browse files
authored
Merge pull request #69648 from hyp/eng/namespace-completion
[cxx-interop] code-complete namespace members
2 parents c3af0ff + 358c688 commit 67c67a0

File tree

4 files changed

+149
-0
lines changed

4 files changed

+149
-0
lines changed

lib/Sema/LookupVisibleDecls.cpp

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "TypeChecker.h"
1919
#include "clang/AST/DeclObjC.h"
2020
#include "swift/AST/ASTContext.h"
21+
#include "swift/AST/ClangModuleLoader.h"
2122
#include "swift/AST/GenericSignature.h"
2223
#include "swift/AST/ImportCache.h"
2324
#include "swift/AST/Initializer.h"
@@ -29,8 +30,11 @@
2930
#include "swift/AST/SourceFile.h"
3031
#include "swift/Basic/SourceManager.h"
3132
#include "swift/Basic/STLExtras.h"
33+
#include "swift/ClangImporter/ClangImporterRequests.h"
3234
#include "swift/Sema/IDETypeCheckingRequests.h"
3335
#include "swift/Sema/IDETypeChecking.h"
36+
#include "clang/Basic/Module.h"
37+
#include "clang/Lex/Preprocessor.h"
3438
#include "llvm/ADT/SetVector.h"
3539
#include <set>
3640

@@ -558,6 +562,63 @@ static void
558562
getReasonForSuper(Reason), Sig, Visited);
559563
}
560564

565+
static void lookupVisibleCxxNamespaceMemberDecls(
566+
EnumDecl *swiftDecl, const clang::NamespaceDecl *clangNamespace,
567+
VisibleDeclConsumer &Consumer, VisitedSet &Visited) {
568+
if (!Visited.insert(swiftDecl).second)
569+
return;
570+
auto &ctx = swiftDecl->getASTContext();
571+
auto namespaceDecl = clangNamespace;
572+
573+
// This is only to keep track of the members we've already seen.
574+
llvm::SmallPtrSet<Decl *, 16> addedMembers;
575+
for (auto redecl : namespaceDecl->redecls()) {
576+
for (auto member : redecl->decls()) {
577+
auto lookupAndAddMembers = [&](DeclName name) {
578+
auto allResults = evaluateOrDefault(
579+
ctx.evaluator, ClangDirectLookupRequest({swiftDecl, redecl, name}),
580+
{});
581+
582+
for (auto found : allResults) {
583+
auto clangMember = found.get<clang::NamedDecl *>();
584+
if (auto importedDecl =
585+
ctx.getClangModuleLoader()->importDeclDirectly(
586+
cast<clang::NamedDecl>(clangMember))) {
587+
if (addedMembers.insert(importedDecl).second) {
588+
if (importedDecl->getDeclContext()->getAsDecl() != swiftDecl) {
589+
return;
590+
}
591+
Consumer.foundDecl(cast<ValueDecl>(importedDecl),
592+
DeclVisibilityKind::MemberOfCurrentNominal);
593+
}
594+
}
595+
}
596+
};
597+
598+
auto namedDecl = dyn_cast<clang::NamedDecl>(member);
599+
if (!namedDecl)
600+
continue;
601+
auto name = ctx.getClangModuleLoader()->importName(namedDecl);
602+
if (!name)
603+
continue;
604+
lookupAndAddMembers(name);
605+
606+
// Unscoped enums could have their enumerators present
607+
// in the parent namespace.
608+
if (auto *ed = dyn_cast<clang::EnumDecl>(member)) {
609+
if (!ed->isScoped()) {
610+
for (const auto *ecd : ed->enumerators()) {
611+
auto name = ctx.getClangModuleLoader()->importName(ecd);
612+
if (!name)
613+
continue;
614+
lookupAndAddMembers(name);
615+
}
616+
}
617+
}
618+
}
619+
}
620+
}
621+
561622
static void lookupVisibleMemberDeclsImpl(
562623
Type BaseTy, VisibleDeclConsumer &Consumer, const DeclContext *CurrDC,
563624
LookupState LS, DeclVisibilityKind Reason, GenericSignature Sig,
@@ -647,6 +708,16 @@ static void lookupVisibleMemberDeclsImpl(
647708
return;
648709
}
649710

711+
// Lookup members of C++ namespace without looking type members, as
712+
// C++ namespace uses lazy lookup.
713+
if (auto *ET = BaseTy->getAs<EnumType>()) {
714+
if (auto *clangNamespace = dyn_cast_or_null<clang::NamespaceDecl>(
715+
ET->getDecl()->getClangDecl())) {
716+
lookupVisibleCxxNamespaceMemberDecls(ET->getDecl(), clangNamespace,
717+
Consumer, Visited);
718+
}
719+
}
720+
650721
// If we're looking into a type parameter and we have a GenericSignature,
651722
// query the signature to resolve where we should look.
652723
if (BaseTy->isTypeParameter() && Sig) {
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#ifndef TEST_INTEROP_CXX_NAMESPACE_INPUTS_ENUMS_H
2+
#define TEST_INTEROP_CXX_NAMESPACE_INPUTS_ENUMS_H
3+
4+
namespace EnumNS1 {
5+
6+
enum AnEnum {
7+
one,
8+
two,
9+
three
10+
};
11+
12+
} // namespace EnumNS1
13+
14+
#endif // TEST_INTEROP_CXX_NAMESPACE_INPUTS_ENUMS_H

test/Interop/Cxx/namespace/Inputs/module.modulemap

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,3 +54,8 @@ module Extensions {
5454
header "extensions.h"
5555
requires cplusplus
5656
}
57+
58+
module Enums {
59+
header "enums.h"
60+
requires cplusplus
61+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// RUN: %target-swift-ide-test -code-completion -enable-experimental-cxx-interop -source-filename %s -code-completion-token=NS1 -I %S/Inputs | %FileCheck %s -check-prefix=CHECK-NS1
2+
// RUN: %target-swift-ide-test -code-completion -enable-experimental-cxx-interop -source-filename %s -code-completion-token=NS2 -I %S/Inputs | %FileCheck %s -check-prefix=CHECK-NS2
3+
// RUN: %target-swift-ide-test -code-completion -enable-experimental-cxx-interop -source-filename %s -code-completion-token=TemplatesNS1 -I %S/Inputs | %FileCheck %s -check-prefix=CHECK-TNS1
4+
// RUN: %target-swift-ide-test -code-completion -enable-experimental-cxx-interop -source-filename %s -code-completion-token=EnumsNS1 -I %S/Inputs | %FileCheck %s -check-prefix=CHECK-ENS1
5+
6+
import Submodules
7+
import Templates
8+
import Enums
9+
10+
func ns1() {
11+
NS1.#^NS1^#
12+
}
13+
// CHECK-NS1: Begin completions, 5 items
14+
// CHECK-NS1-NEXT: Keyword[self]/CurrNominal: self[#NS1.Type#]; name=self
15+
// CHECK-NS1-NEXT: Keyword/CurrNominal: Type[#NS1.Type#]; name=Type
16+
// CHECK-NS1-NEXT: Decl[Enum]/CurrNominal: NS2[#NS1.NS2#]; name=NS2
17+
// CHECK-NS1-NEXT: Decl[Struct]/CurrNominal: BasicB[#NS1.BasicB#]; name=BasicB
18+
// CHECK-NS1-NEXT: Decl[Struct]/CurrNominal: BasicA[#NS1.BasicA#]; name=BasicA
19+
// CHECK-NS1-NEXT: End completions
20+
21+
func ns2() {
22+
NS1.NS2.#^NS2^#
23+
}
24+
25+
// CHECK-NS2: Begin completions, 4 items
26+
// CHECK-NS2-NEXT: Keyword[self]/CurrNominal: self[#NS1.NS2.Type#]; name=self
27+
// CHECK-NS2-NEXT: Keyword/CurrNominal: Type[#NS1.NS2.Type#]; name=Type
28+
// CHECK-NS2-NEXT: Decl[Struct]/CurrNominal: BasicDeepB[#NS1.NS2.BasicDeepB#]; name=BasicDeepB
29+
// CHECK-NS2-NEXT: Decl[Struct]/CurrNominal: BasicDeepA[#NS1.NS2.BasicDeepA#]; name=BasicDeepA
30+
// CHECK-NS2-NEXT: End completions
31+
32+
func nsTemplates() {
33+
TemplatesNS1.#^TemplatesNS1^#
34+
}
35+
36+
// CHECK-TNS1: Begin completions, 10 items
37+
// CHECK-TNS1-NEXT: Keyword[self]/CurrNominal: self[#TemplatesNS1.Type#]; name=self
38+
// CHECK-TNS1-NEXT: Keyword/CurrNominal: Type[#TemplatesNS1.Type#]; name=Type
39+
// CHECK-TNS1-NEXT: Decl[StaticMethod]/CurrNominal: basicFunctionTemplateDefinedInDefs({#T#})[#UnsafePointer<CChar>!#]; name=basicFunctionTemplateDefinedInDefs()
40+
// CHECK-TNS1-NEXT: Decl[TypeAlias]/CurrNominal: UseTemplate[#TemplatesNS4.HasSpecialization<CChar>#]; name=UseTemplate
41+
// CHECK-TNS1-NEXT: Decl[TypeAlias]/CurrNominal: UseSpecialized[#TemplatesNS4.HasSpecialization<CInt>#]; name=UseSpecialized
42+
// CHECK-TNS1-NEXT: Decl[Enum]/CurrNominal: TemplatesNS2[#TemplatesNS1.TemplatesNS2#]; name=TemplatesNS2
43+
// CHECK-TNS1-NEXT: Decl[Enum]/CurrNominal: TemplatesNS3[#TemplatesNS1.TemplatesNS3#]; name=TemplatesNS3
44+
// CHECK-TNS1-NEXT: Decl[TypeAlias]/CurrNominal: ForwardDeclaredClassTemplateChar[#TemplatesNS1.TemplatesNS2.ForwardDeclaredClassTemplate<CChar>#]; name=ForwardDeclaredClassTemplateChar
45+
// CHECK-TNS1-NEXT: Decl[StaticMethod]/CurrNominal: basicFunctionTemplate({#T#})[#UnsafePointer<CChar>!#]; name=basicFunctionTemplate()
46+
// CHECK-TNS1-NEXT: Decl[TypeAlias]/CurrNominal: BasicClassTemplateChar[#TemplatesNS1.BasicClassTemplate<CChar>#]; name=BasicClassTemplateChar
47+
// CHECK-TNS1-NEXT: End completions
48+
49+
func nsEnums() {
50+
EnumNS1.#^EnumsNS1^#
51+
}
52+
53+
// CHECK-ENS1: Keyword[self]/CurrNominal: self[#EnumNS1.Type#]; name=self
54+
// CHECK-ENS1-NEXT: Keyword/CurrNominal: Type[#EnumNS1.Type#]; name=Type
55+
// CHECK-ENS1-NEXT: Decl[Struct]/CurrNominal: AnEnum[#EnumNS1.AnEnum#]; name=AnEnum
56+
// CHECK-ENS1-NEXT: Decl[StaticVar]/CurrNominal: one[#EnumNS1.AnEnum#]; name=one
57+
// CHECK-ENS1-NEXT: Decl[StaticVar]/CurrNominal: two[#EnumNS1.AnEnum#]; name=two
58+
// CHECK-ENS1-NEXT: Decl[StaticVar]/CurrNominal: three[#EnumNS1.AnEnum#]; name=three
59+
// CHECK-ENS1-NEXT: End completions

0 commit comments

Comments
 (0)