Skip to content

Commit 81b37cc

Browse files
authored
Merge pull request #32762 from rintaro/ide-completion-rdar65081358
[CodeCompletion] Remove redundant entries from possible callee analysis
2 parents a54aeb2 + a778f51 commit 81b37cc

File tree

2 files changed

+124
-16
lines changed

2 files changed

+124
-16
lines changed

lib/IDE/ExprContextAnalysis.cpp

Lines changed: 33 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -388,6 +388,7 @@ static void collectPossibleCalleesByQualifiedLookup(
388388
decls))
389389
return;
390390

391+
llvm::DenseMap<std::pair<char, CanType>, size_t> known;
391392
auto *baseNominal = baseInstanceTy->getAnyNominal();
392393
for (auto *VD : decls) {
393394
if ((!isa<AbstractFunctionDecl>(VD) && !isa<SubscriptDecl>(VD)) ||
@@ -420,27 +421,43 @@ static void collectPossibleCalleesByQualifiedLookup(
420421
DC.getParentModule(), VD,
421422
VD->getInnermostDeclContext()->getGenericEnvironmentOfContext());
422423
auto fnType = declaredMemberType.subst(subs);
423-
if (!fnType)
424+
if (!fnType || !fnType->is<AnyFunctionType>())
424425
continue;
425426

426-
if (fnType->is<AnyFunctionType>()) {
427-
// If we are calling to typealias type,
428-
if (isa<SugarType>(baseInstanceTy.getPointer())) {
429-
auto canBaseTy = baseInstanceTy->getCanonicalType();
430-
fnType = fnType.transform([&](Type t) -> Type {
431-
if (t->getCanonicalType()->isEqual(canBaseTy))
432-
return baseInstanceTy;
433-
return t;
434-
});
435-
}
436-
auto semanticContext = SemanticContextKind::CurrentNominal;
437-
if (baseNominal &&
438-
VD->getDeclContext()->getSelfNominalTypeDecl() != baseNominal)
439-
semanticContext = SemanticContextKind::Super;
427+
// If we are calling on a type alias type, replace the canonicalized type
428+
// in the function type with the type alias.
429+
if (isa<SugarType>(baseInstanceTy.getPointer())) {
430+
auto canBaseTy = baseInstanceTy->getCanonicalType();
431+
fnType = fnType.transform([&](Type t) -> Type {
432+
if (t->getCanonicalType()->isEqual(canBaseTy))
433+
return baseInstanceTy;
434+
return t;
435+
});
436+
}
440437

441-
candidates.emplace_back(fnType->castTo<AnyFunctionType>(), VD,
438+
auto semanticContext = SemanticContextKind::CurrentNominal;
439+
if (baseNominal &&
440+
VD->getDeclContext()->getSelfNominalTypeDecl() != baseNominal)
441+
semanticContext = SemanticContextKind::Super;
442+
443+
FunctionTypeAndDecl entry(fnType->castTo<AnyFunctionType>(), VD,
442444
semanticContext);
445+
// Remember the index of the entry.
446+
auto knownResult = known.insert(
447+
{{VD->isStatic(), fnType->getCanonicalType()}, candidates.size()});
448+
if (knownResult.second) {
449+
candidates.push_back(entry);
450+
continue;
443451
}
452+
453+
auto idx = knownResult.first->second;
454+
if (AvailableAttr::isUnavailable(candidates[idx].Decl) &&
455+
!AvailableAttr::isUnavailable(VD)) {
456+
// Replace the previously found "unavailable" with the "available" one.
457+
candidates[idx] = entry;
458+
}
459+
460+
// Otherwise, skip redundant results.
444461
}
445462
}
446463

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
// RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=INITIALIZER | %FileCheck %s --check-prefix=INITIALIZER
2+
// RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=METHOD | %FileCheck %s --check-prefix=METHOD
3+
// RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=AVAILABILITY | %FileCheck %s --check-prefix=AVAILABILITY
4+
// RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=STATIC | %FileCheck %s --check-prefix=STATIC
5+
6+
protocol MyProtocol {
7+
init(init1: Int)
8+
init(init2: Int)
9+
10+
func method(method1: Int)
11+
func method(method2: Int)
12+
}
13+
14+
extension MyProtocol {
15+
init(init2: Int) { self.init(init1: init2) }
16+
init(init3: Int) { self.init(init1: init3) }
17+
18+
func method(method2: Int) {}
19+
func method(method3: Int) {}
20+
}
21+
22+
class Base {
23+
init(init4: Int) { }
24+
func method(method4: Int) {}
25+
}
26+
27+
class MyClass: Base, MyProtocol {
28+
29+
required init(init1: Int) { super.init(init4: init1) }
30+
required init(init2: Int) { super.init(init4: init1) }
31+
init(init3: Int) { super.init(init4: init1) }
32+
override init(init4: Int) { super.init(init4: init1) }
33+
34+
func method(method1: Int)
35+
func method(method2: Int) {}
36+
func method(method3: Int) {}
37+
override func method(method4: Int) {}
38+
}
39+
40+
func testConstructer() {
41+
MyClass(#^INITIALIZER^#)
42+
// INITIALIZER: Begin completions, 4 items
43+
// INITIALIZER-DAG: Decl[Constructor]/CurrNominal: ['(']{#init1: Int#}[')'][#MyClass#];
44+
// INITIALIZER-DAG: Decl[Constructor]/CurrNominal: ['(']{#init2: Int#}[')'][#MyClass#];
45+
// INITIALIZER-DAG: Decl[Constructor]/CurrNominal: ['(']{#init3: Int#}[')'][#MyClass#];
46+
// INITIALIZER-DAG: Decl[Constructor]/CurrNominal: ['(']{#init4: Int#}[')'][#MyClass#];
47+
// INITIALIZER: End completions
48+
}
49+
50+
func testMethod(obj: MyClass) {
51+
obj.method(#^METHOD^#)
52+
// METHOD: Begin completions, 4 items
53+
// METHOD-DAG: Decl[InstanceMethod]/CurrNominal: ['(']{#method1: Int#}[')'][#Void#];
54+
// METHOD-DAG: Decl[InstanceMethod]/CurrNominal: ['(']{#method2: Int#}[')'][#Void#];
55+
// METHOD-DAG: Decl[InstanceMethod]/CurrNominal: ['(']{#method3: Int#}[')'][#Void#];
56+
// METHOD-DAG: Decl[InstanceMethod]/CurrNominal: ['(']{#method4: Int#}[')'][#Void#];
57+
// METHOD: End completions
58+
}
59+
60+
protocol HasUnavailable {}
61+
extension HasUnavailable {
62+
func method(method1: Int) {}
63+
64+
@available(*, unavailable)
65+
func method(method2: Int) {}
66+
}
67+
struct MyStruct: HasUnavailable {
68+
@available(*, unavailable)
69+
func method(method1: Int) {}
70+
71+
func method(method2: Int) {}
72+
}
73+
func testUnavailable(val: MyStruct) {
74+
val.method(#^AVAILABILITY^#)
75+
// AVAILABILITY: Begin completions, 2 items
76+
// AVAILABILITY-DAG: Decl[InstanceMethod]/CurrNominal: ['(']{#method2: Int#}[')'][#Void#];
77+
// AVAILABILITY-DAG: Decl[InstanceMethod]/Super: ['(']{#method1: Int#}[')'][#Void#];
78+
// AVAILABILITY: End completions
79+
}
80+
81+
struct TestStatic {
82+
static func method(_ self: TestStatic) -> () -> Void {}
83+
func method() -> Void {}
84+
}
85+
func testStaticFunc() {
86+
TestStatic.method(#^STATIC^#)
87+
// STATIC: Begin completions
88+
// STATIC-DAG: Decl[StaticMethod]/CurrNominal: ['(']{#(self): TestStatic#}[')'][#() -> Void#];
89+
// STATIC-DAG: Decl[InstanceMethod]/CurrNominal: ['(']{#(self): TestStatic#}[')'][#() -> Void#];
90+
// STATIC: End completions
91+
}

0 commit comments

Comments
 (0)