Skip to content

Commit 9203080

Browse files
committed
[CodeCompletion] Fix several completion failure in multi-file scenario
- Default memberwise initializer for struct was not suggested - Dynamic member lookup didn't work - `init(rawValue:)` was not suggested for `RawRepresentable` types - '$name' was not suggested for `@propertyWrapper`ed value rdar://problem/56391233
1 parent 51d7d6a commit 9203080

File tree

2 files changed

+144
-0
lines changed

2 files changed

+144
-0
lines changed

lib/Sema/LookupVisibleDecls.cpp

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
//
1616
//===----------------------------------------------------------------------===//
1717

18+
#include "TypeChecker.h"
1819
#include "swift/AST/ASTContext.h"
1920
#include "swift/AST/GenericSignature.h"
2021
#include "swift/AST/GenericSignatureBuilder.h"
@@ -24,6 +25,7 @@
2425
#include "swift/AST/ModuleNameLookup.h"
2526
#include "swift/AST/NameLookup.h"
2627
#include "swift/AST/ProtocolConformance.h"
28+
#include "swift/AST/PropertyWrappers.h"
2729
#include "swift/AST/SourceFile.h"
2830
#include "swift/Basic/SourceManager.h"
2931
#include "swift/Basic/STLExtras.h"
@@ -474,6 +476,53 @@ static void
474476
lookupTypeMembers(BaseTy, PT, Consumer, CurrDC, LS, Reason);
475477
}
476478

479+
/// Trigger synthesizing implicit member declarations to make them "visible".
480+
static void synthesizeMemberDeclsForLookup(NominalTypeDecl *NTD,
481+
const DeclContext *DC) {
482+
// Synthesize the memberwise initializer for structs or default initializer
483+
// for classes.
484+
if (!NTD->hasInterfaceType())
485+
TypeChecker::addImplicitConstructors(NTD);
486+
487+
// Check all conformances to trigger the synthesized decl generation.
488+
// e.g. init(rawValue:) for RawRepresentable.
489+
for (auto Conformance : NTD->getAllConformances()) {
490+
auto Proto = Conformance->getProtocol();
491+
if (!Proto->isAccessibleFrom(DC))
492+
continue;
493+
auto NormalConformance = dyn_cast<NormalProtocolConformance>(
494+
Conformance->getRootConformance());
495+
if (!NormalConformance)
496+
continue;
497+
for (auto Member : Proto->getMembers()) {
498+
auto *VD = dyn_cast<ValueDecl>(Member);
499+
if (!VD || !VD->isProtocolRequirement())
500+
continue;
501+
if (auto *ATD = dyn_cast<AssociatedTypeDecl>(Member))
502+
(void)NormalConformance->getTypeWitnessAndDecl(ATD);
503+
else
504+
(void)NormalConformance->getWitness(VD);
505+
}
506+
}
507+
508+
// Generate '$' and '_' prefixed variables that have attached property
509+
// wrappers.
510+
auto synthesizePropertyWrappers = [](IterableDeclContext *IDC) {
511+
for (auto Member : IDC->getMembers()) {
512+
if (auto var = dyn_cast<VarDecl>(Member)) {
513+
if (var->hasAttachedPropertyWrapper()) {
514+
auto sourceFile = var->getDeclContext()->getParentSourceFile();
515+
if (sourceFile && sourceFile->Kind != SourceFileKind::Interface)
516+
(void)var->getPropertyWrapperBackingPropertyInfo();
517+
}
518+
}
519+
}
520+
};
521+
synthesizePropertyWrappers(NTD);
522+
for (auto ED : NTD->getExtensions())
523+
synthesizePropertyWrappers(ED);
524+
}
525+
477526
static void lookupVisibleMemberDeclsImpl(
478527
Type BaseTy, VisibleDeclConsumer &Consumer, const DeclContext *CurrDC,
479528
LookupState LS, DeclVisibilityKind Reason, GenericSignatureBuilder *GSB,
@@ -583,6 +632,8 @@ static void lookupVisibleMemberDeclsImpl(
583632
if (!CurNominal)
584633
break;
585634

635+
synthesizeMemberDeclsForLookup(CurNominal, CurrDC);
636+
586637
// Look in for members of a nominal type.
587638
lookupTypeMembers(BaseTy, BaseTy, Consumer, CurrDC, LS, Reason);
588639
lookupDeclsFromProtocolsBeingConformedTo(BaseTy, Consumer, LS, CurrDC,

test/IDE/complete_multifile.swift

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %{python} %utils/split_file.py -o %t %s
3+
4+
// RUN: %target-swift-ide-test -code-completion -source-filename %t/Main.swift %t/Library.swift -code-completion-token=POINT_PAREN | %FileCheck --check-prefix=POINT_PAREN %s
5+
// RUN: %target-swift-ide-test -code-completion -source-filename %t/Main.swift %t/Library.swift -code-completion-token=POINT_DOT | %FileCheck --check-prefix=POINT_DOT %s
6+
// RUN: %target-swift-ide-test -code-completion -source-filename %t/Main.swift %t/Library.swift -code-completion-token=LENS_DOT | %FileCheck --check-prefix=LENS_DOT %s
7+
// RUN: %target-swift-ide-test -code-completion -source-filename %t/Main.swift %t/Library.swift -code-completion-token=MYENUM_DOT | %FileCheck --check-prefix=MYENUM_DOT %s
8+
// RUN: %target-swift-ide-test -code-completion -source-filename %t/Main.swift %t/Library.swift -code-completion-token=HASWRAPPED_DOT| %FileCheck --check-prefix=HASWRAPPED_DOT %s
9+
10+
// BEGIN Library.swift
11+
12+
public struct Point {
13+
var x: Int
14+
var y: Int
15+
}
16+
17+
@dynamicMemberLookup
18+
struct Lens<T> {
19+
var obj: T
20+
init(_ obj: T) { self.obj = obj }
21+
22+
subscript<U>(dynamicMember member: WritableKeyPath<T, U>) -> Lens<U> {
23+
get { return Lens<U>(obj[keyPath: member]) }
24+
set { obj[keyPath: member] = newValue.obj }
25+
}
26+
}
27+
28+
enum MyEnum: String {
29+
case foo = "foo"
30+
case bar = "bar"
31+
}
32+
33+
@propertyWrapper
34+
struct Wrap<T> {
35+
var wrappedValue: T
36+
37+
public var projectedValue: Self {
38+
get { self }
39+
set { self = newValue }
40+
}
41+
}
42+
43+
struct HasWrapped {
44+
@Wrap
45+
var wrapped: Int = 1
46+
}
47+
48+
// BEGIN Main.swift
49+
50+
func testStructDefaultInit() {
51+
Point(#^POINT_PAREN^#
52+
// POINT_PAREN: Begin completions, 1 items
53+
// POINT_PAREN-DAG: Decl[Constructor]/CurrNominal: ['(']{#x: Int#}, {#y: Int#}[')'][#Point#];
54+
// POINT_PAREN: End completions
55+
func sync() {}
56+
Point.#^POINT_DOT^#
57+
// POINT_DOT: Begin completions, 3 items
58+
// POINT_DOT-DAG: Keyword[self]/CurrNominal: self[#Point.Type#];
59+
// POINT_DOT-DAG: Keyword/CurrNominal: Type[#Point.Type#];
60+
// POINT_DOT-DAG: Decl[Constructor]/CurrNominal: init({#x: Int#}, {#y: Int#})[#Point#];
61+
// POINT_DOT: End completions
62+
}
63+
64+
func testDynamicMemberLookup(lens: Lens<Point>) {
65+
_ = lens.#^LENS_DOT^#
66+
// LENS_DOT: Begin completions, 4 items
67+
// LENS_DOT-DAG: Keyword[self]/CurrNominal: self[#Lens<Point>#];
68+
// LENS_DOT-DAG: Decl[InstanceVar]/CurrNominal: x[#Lens<Int>#];
69+
// LENS_DOT-DAG: Decl[InstanceVar]/CurrNominal: y[#Lens<Int>#];
70+
// LENS_DOT-DAG: Decl[InstanceVar]/CurrNominal: obj[#Point#];
71+
// LENS_DOT: End completions
72+
}
73+
func testRawRepresentable() {
74+
MyEnum.#^MYENUM_DOT^#
75+
// MYENUM_DOT: Begin completions, 7 items
76+
// MYENUM_DOT-DAG: Keyword[self]/CurrNominal: self[#MyEnum.Type#];
77+
// MYENUM_DOT-DAG: Keyword/CurrNominal: Type[#MyEnum.Type#];
78+
// MYENUM_DOT-DAG: Decl[EnumElement]/CurrNominal: foo[#MyEnum#];
79+
// MYENUM_DOT-DAG: Decl[EnumElement]/CurrNominal: bar[#MyEnum#];
80+
// MYENUM_DOT-DAG: Decl[TypeAlias]/CurrNominal: RawValue[#String#];
81+
// MYENUM_DOT-DAG: Decl[Constructor]/CurrNominal: init({#rawValue: String#})[#MyEnum?#];
82+
// MYENUM_DOT-DAG: Decl[InstanceMethod]/Super: hash({#(self): MyEnum#})[#(into: inout Hasher) -> Void#];
83+
// MYENUM_DOT: End completions
84+
}
85+
86+
func testHasWrappedValue(value: HasWrapped) {
87+
value.#^HASWRAPPED_DOT^#
88+
// HASWRAPPED_DOT: Begin completions, 3 items
89+
// HASWRAPPED_DOT: Keyword[self]/CurrNominal: self[#HasWrapped#];
90+
// HASWRAPPED_DOT: Decl[InstanceVar]/CurrNominal: wrapped[#Int#];
91+
// HASWRAPPED_DOT: Decl[InstanceVar]/CurrNominal: $wrapped[#Wrap<Int>#];
92+
// HASWRAPPED_DOT: End completions
93+
}

0 commit comments

Comments
 (0)