Skip to content

[CodeCompletion] Duplicate existential requirements when restated #16811

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
118 changes: 104 additions & 14 deletions lib/AST/LookupVisibleDecls.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@

#include "NameLookupImpl.h"
#include "swift/AST/ASTContext.h"
#include "swift/AST/GenericSignature.h"
#include "swift/AST/GenericSignatureBuilder.h"
#include "swift/AST/Initializer.h"
#include "swift/AST/LazyResolver.h"
#include "swift/AST/NameLookup.h"
#include "swift/AST/ProtocolConformance.h"
#include "swift/AST/SubstitutionMap.h"
#include "swift/Basic/SourceManager.h"
#include "swift/Basic/STLExtras.h"
#include "swift/Sema/IDETypeChecking.h"
Expand Down Expand Up @@ -113,7 +113,7 @@ struct LookupState {
return Result;
}
};
} // unnamed namespace
} // end anonymous namespace

static bool areTypeDeclsVisibleInLookupMode(LookupState LS) {
// Nested type declarations can be accessed only with unqualified lookup or
Expand Down Expand Up @@ -473,19 +473,107 @@ lookupVisibleMemberDeclsImpl(Type BaseTy, VisibleDeclConsumer &Consumer,
GenericSignatureBuilder *GSB,
VisitedSet &Visited);

static void lookupVisibleProtocolMemberDecls(
Type BaseTy, ProtocolType *PT, VisibleDeclConsumer &Consumer,
const DeclContext *CurrDC, LookupState LS, DeclVisibilityKind Reason,
LazyResolver *TypeResolver, GenericSignatureBuilder *GSB,
VisitedSet &Visited) {
// Filters out restated declarations from a protocol hierarchy
// or equivalent requirements from protocol composition types.
class RestateFilteringConsumer : public VisibleDeclConsumer {
LazyResolver *resolver;

using FoundDecl = std::pair<ValueDecl*, DeclVisibilityKind>;
using NameAndType = std::pair<DeclName, CanType>;

llvm::DenseMap<DeclName, FoundDecl> foundVars;
llvm::DenseMap<NameAndType, FoundDecl> foundFuncs;
llvm::MapVector<ValueDecl*, DeclVisibilityKind> declsToReport;

template <typename K>
void addDecl(llvm::DenseMap<K, FoundDecl> &Map, K Key, FoundDecl FD) {
// Add the declaration if we haven't found an equivalent yet, otherwise
// replace the equivalent if the found decl has a higher access level.
auto existingDecl = Map.find(Key);

if ((existingDecl == Map.end()) ||
(Map[Key].first->getFormalAccess() < FD.first->getFormalAccess())) {
if (existingDecl != Map.end())
declsToReport.erase({existingDecl->getSecond().first});
Map[Key] = FD;
declsToReport.insert(FD);
}
}

CanType stripSelfRequirementsIfNeeded(ValueDecl *VD,
GenericFunctionType *GFT) const {
// Preserve the generic signature if this is a subscript, which are uncurried,
// or if we have generic params other than Self. Otherwise, use
// the resultType of the curried function type.
// When we keep the generic signature, we remove the requirements
// from Self to make sure they don't prevent us from recognizing restatements.
auto params = GFT->getGenericParams();
if (params.size() == 1 && !isa<SubscriptDecl>(VD)) {
return GFT->getResult()->getCanonicalType();
}
auto Self = VD->getDeclContext()->getSelfInterfaceType();
SmallVector<Requirement, 4> newReqs;
for (auto req: GFT->getRequirements()) {
if (!Self->isEqual(req.getFirstType()))
newReqs.push_back(req);
}
auto newSig = GenericSignature::get(params, newReqs, false);

return GenericFunctionType::get(newSig, GFT->getInput(),
GFT->getResult(), GFT->getExtInfo())
->getCanonicalType();
}

public:
RestateFilteringConsumer(Type baseTy, const DeclContext *DC,
LazyResolver *resolver)
: resolver(resolver) {
assert(DC && baseTy && !baseTy->hasLValueType());
}

void foundDecl(ValueDecl *VD, DeclVisibilityKind Reason) override {
assert(VD);
// If this isn't a protocol context, don't look further into the decl.
if (!isa<ProtocolDecl>(VD->getDeclContext())) {
declsToReport.insert({VD, Reason});
return;
}
if (resolver)
resolver->resolveDeclSignature(VD);

if (!VD->hasInterfaceType()) {
declsToReport.insert({VD, Reason});
return;
}
if (auto GFT = VD->getInterfaceType()->getAs<GenericFunctionType>()) {
auto type = stripSelfRequirementsIfNeeded(VD, GFT);
addDecl(foundFuncs, {VD->getFullName(), type}, {VD, Reason});
return;
}
addDecl(foundVars, VD->getFullName(), {VD, Reason});
}

void feedResultsToConsumer(VisibleDeclConsumer &Consumer) const {
for (const auto entry: declsToReport)
Consumer.foundDecl(entry.first, entry.second);
}
};

static void
lookupVisibleProtocolMemberDecls(Type BaseTy, ProtocolType *PT,
VisibleDeclConsumer &Consumer,
const DeclContext *CurrDC, LookupState LS,
DeclVisibilityKind Reason,
LazyResolver *TypeResolver,
GenericSignatureBuilder *GSB,
VisitedSet &Visited) {
if (!Visited.insert(PT->getDecl()).second)
return;

for (auto Proto : PT->getDecl()->getInheritedProtocols())
lookupVisibleProtocolMemberDecls(BaseTy, Proto->getDeclaredType(), Consumer, CurrDC,
LS, getReasonForSuper(Reason), TypeResolver,
GSB, Visited);

LS, getReasonForSuper(Reason), TypeResolver,
GSB, Visited);
lookupTypeMembers(BaseTy, PT, Consumer, CurrDC, LS, Reason, TypeResolver);
}

Expand Down Expand Up @@ -834,7 +922,7 @@ class OverrideFilteringConsumer : public VisibleDeclConsumer {
DeclsToReport.insert(FoundDeclTy(VD, Reason));
}
};
} // unnamed namespace
} // end anonymous namespace

/// \brief Enumerate all members in \c BaseTy (including members of extensions,
/// superclasses and implemented protocols), as seen from the context \c CurrDC.
Expand All @@ -846,13 +934,15 @@ static void lookupVisibleMemberDecls(
Type BaseTy, VisibleDeclConsumer &Consumer, const DeclContext *CurrDC,
LookupState LS, DeclVisibilityKind Reason, LazyResolver *TypeResolver,
GenericSignatureBuilder *GSB) {
OverrideFilteringConsumer ConsumerWrapper(BaseTy, CurrDC, TypeResolver);
OverrideFilteringConsumer overrideConsumer(BaseTy, CurrDC, TypeResolver);
RestateFilteringConsumer restateConsumer(BaseTy, CurrDC, TypeResolver);
VisitedSet Visited;
lookupVisibleMemberDeclsImpl(BaseTy, ConsumerWrapper, CurrDC, LS, Reason,
lookupVisibleMemberDeclsImpl(BaseTy, restateConsumer, CurrDC, LS, Reason,
TypeResolver, GSB, Visited);

// Report the declarations we found to the real consumer.
for (const auto &DeclAndReason : ConsumerWrapper.DeclsToReport)
restateConsumer.feedResultsToConsumer(overrideConsumer);
for (const auto &DeclAndReason : overrideConsumer.DeclsToReport)
Consumer.foundDecl(DeclAndReason.D, DeclAndReason.Reason);
}

Expand Down
21 changes: 9 additions & 12 deletions test/IDE/complete_override_access_control_protocol.swift
Original file line number Diff line number Diff line change
Expand Up @@ -168,16 +168,15 @@ public class TestPublicDE : ProtocolDPrivate, ProtocolEPublic {
#^TEST_PUBLIC_DE^#
}

// FIXME: Should be 2 items in the three checks below.
// TEST_PRIVATE_DE: Begin completions, 4 items
// TEST_PRIVATE_DE: Begin completions, 2 items
// TEST_PRIVATE_DE-DAG: Decl[InstanceMethod]/Super: func colliding() {|}{{; name=.+$}}
// TEST_PRIVATE_DE-DAG: Decl[InstanceMethod]/Super: func collidingGeneric<T>(x: T) {|}{{; name=.+$}}

// TEST_INTERNAL_DE: Begin completions, 4 items
// TEST_INTERNAL_DE: Begin completions, 2 items
// TEST_INTERNAL_DE-DAG: Decl[InstanceMethod]/Super: func colliding() {|}{{; name=.+$}}
// TEST_INTERNAL_DE-DAG: Decl[InstanceMethod]/Super: func collidingGeneric<T>(x: T) {|}{{; name=.+$}}

// TEST_PUBLIC_DE: Begin completions, 4 items
// TEST_PUBLIC_DE: Begin completions, 2 items
// TEST_PUBLIC_DE-DAG: Decl[InstanceMethod]/Super: public func colliding() {|}{{; name=.+$}}
// TEST_PUBLIC_DE-DAG: Decl[InstanceMethod]/Super: public func collidingGeneric<T>(x: T) {|}{{; name=.+$}}

Expand All @@ -195,16 +194,15 @@ public class TestPublicED : ProtocolEPublic, ProtocolDPrivate {
#^TEST_PUBLIC_ED^#
}

// FIXME: Should be 2 items in the three checks below.
// TEST_PRIVATE_ED: Begin completions, 4 items
// TEST_PRIVATE_ED: Begin completions, 2 items
// TEST_PRIVATE_ED-DAG: Decl[InstanceMethod]/Super: func colliding() {|}{{; name=.+$}}
// TEST_PRIVATE_ED-DAG: Decl[InstanceMethod]/Super: func collidingGeneric<T>(x: T) {|}{{; name=.+$}}

// TEST_INTERNAL_ED: Begin completions, 4 items
// TEST_INTERNAL_ED: Begin completions, 2 items
// TEST_INTERNAL_ED-DAG: Decl[InstanceMethod]/Super: func collidingGeneric<T>(x: T) {|}{{; name=.+$}}
// TEST_INTERNAL_ED-DAG: Decl[InstanceMethod]/Super: func colliding() {|}{{; name=.+$}}

// TEST_PUBLIC_ED: Begin completions, 4 items
// TEST_PUBLIC_ED: Begin completions, 2 items
// TEST_PUBLIC_ED-DAG: Decl[InstanceMethod]/Super: public func collidingGeneric<T>(x: T) {|}{{; name=.+$}}
// TEST_PUBLIC_ED-DAG: Decl[InstanceMethod]/Super: public func colliding() {|}{{; name=.+$}}

Expand All @@ -222,15 +220,14 @@ public class TestPublicEF : ProtocolEPublic, ProtocolFPublic {
#^TEST_PUBLIC_EF^#
}

// FIXME: Should be 2 items in the three checks below.
// TEST_PRIVATE_EF: Begin completions, 4 items
// TEST_PRIVATE_EF: Begin completions, 2 items
// TEST_PRIVATE_EF-DAG: Decl[InstanceMethod]/Super: func colliding() {|}{{; name=.+$}}
// TEST_PRIVATE_EF-DAG: Decl[InstanceMethod]/Super: func collidingGeneric<T>(x: T) {|}{{; name=.+$}}

// TEST_INTERNAL_EF: Begin completions, 4 items
// TEST_INTERNAL_EF: Begin completions, 2 items
// TEST_INTERNAL_EF-DAG: Decl[InstanceMethod]/Super: func colliding() {|}{{; name=.+$}}
// TEST_INTERNAL_EF-DAG: Decl[InstanceMethod]/Super: func collidingGeneric<T>(x: T) {|}{{; name=.+$}}

// TEST_PUBLIC_EF: Begin completions, 4 items
// TEST_PUBLIC_EF: Begin completions, 2 items
// TEST_PUBLIC_EF-DAG: Decl[InstanceMethod]/Super: public func colliding() {|}{{; name=.+$}}
// TEST_PUBLIC_EF-DAG: Decl[InstanceMethod]/Super: public func collidingGeneric<T>(x: T) {|}{{; name=.+$}}
132 changes: 132 additions & 0 deletions test/IDE/complete_value_expr.swift
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,20 @@
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=PROTOCOL_EXT_GENERICP1 | %FileCheck %s -check-prefix=PROTOCOL_EXT_GENERICP1
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=PROTOCOL_EXT_GENERICP2 | %FileCheck %s -check-prefix=PROTOCOL_EXT_GENERICP2
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=PROTOCOL_EXT_GENERICP3 | %FileCheck %s -check-prefix=PROTOCOL_EXT_GENERICP3
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=NODUP_RESTATED_REQ1 | %FileCheck %s -check-prefix=CHECK_NODUP_RESTATED_REQ
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=NODUP_RESTATED_REQ2 | %FileCheck %s -check-prefix=CHECK_NODUP_RESTATED_REQ
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=NODUP_RESTATED_REQ3 | %FileCheck %s -check-prefix=CHECK_NODUP_RESTATED_REQ
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=NODUP_RESTATED_REQ4 | %FileCheck %s -check-prefix=CHECK_NODUP_RESTATED_REQ
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=NODUP_RESTATED_REQ5 | %FileCheck %s -check-prefix=CHECK_NODUP_RESTATED_REQ
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=NODUP_RESTATED_REQ6 | %FileCheck %s -check-prefix=CHECK_NODUP_RESTATED_REQ
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=NODUP_RESTATED_REQ_TYPE1 | %FileCheck %s -check-prefix=CHECK_NODUP_RESTATED_REQ_TYPE
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=NODUP_RESTATED_REQ_TYPE2 | %FileCheck %s -check-prefix=CHECK_NODUP_RESTATED_REQ_TYPE
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=NODUP_RESTATED_REQ_TYPE3 | %FileCheck %s -check-prefix=CHECK_NODUP_RESTATED_REQ_TYPE
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=NODUP_RESTATED_REQ_NODOT1 | %FileCheck %s -check-prefix=CHECK_NODUP_RESTATED_REQ_NODOT
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=NODUP_RESTATED_REQ_NODOT2 | %FileCheck %s -check-prefix=CHECK_NODUP_RESTATED_REQ_NODOT
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=NODUP_RESTATED_REQ_NODOT3 | %FileCheck %s -check-prefix=CHECK_NODUP_RESTATED_REQ_NODOT
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=CHECK_PROT_OVERRIDES1 | %FileCheck %s -check-prefix=CHECK_PROT_OVERRIDES
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=CHECK_PROT_OVERRIDES2 | %FileCheck %s -check-prefix=CHECK_PROT_OVERRIDES
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=PROTOCOL_EXT_P4 | %FileCheck %s -check-prefix=PROTOCOL_EXT_P4
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=PROTOCOL_EXT_CONCRETE1 | %FileCheck %s -check-prefix=PROTOCOL_EXT_P4_P1
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=PROTOCOL_EXT_CONCRETE2 | %FileCheck %s -check-prefix=PROTOCOL_EXT_P4_P1
Expand Down Expand Up @@ -1521,6 +1535,124 @@ func testGenericConforming3<T: P3>(x: T) {
// PROTOCOL_EXT_GENERICP3-DAG: Decl[InstanceMethod]/Super: extP3()[#Void#]{{; name=.+$}}
// PROTOCOL_EXT_GENERICP3: End completions

protocol NoDupReq1 {
func foo()
func roo(arg1: Int)
subscript(arg: Bool) -> Bool {get}
var doo: Int {get}
associatedtype E
}
protocol NoDupReq2 {
func foo()
subscript(arg: Bool) -> Bool {get}
var doo: Int {get}
associatedtype E
}
protocol NoDupReq3 {
func foo()
func roo(arg2: Int)
subscript(arg: Bool) -> Bool {get}
var doo: Int {get}
associatedtype E
}

protocol NoDupReq4 {
func foo()
func roo(arg1: Int)
subscript(arg: Bool) -> Bool {get}
var doo: Int {get}
associatedtype E
}
protocol NoDupReq5: NoDupReq4 {
func foo()
subscript(arg: Bool) -> Bool {get}
var doo: Int {get}
associatedtype E
}
protocol NoDupReq6: NoDupReq5 {
func foo()
func roo(arg2: Int)
subscript(arg: Bool) -> Bool {get}
var doo: Int {get}
associatedtype E
}

typealias NoDupReq23 = NoDupReq2 & NoDupReq3

protocol Override {
func foo<T: NoDupReq1>(_ arg: T)
func foo<T: NoDupReq2>(_ arg: T)
}
protocol Override2 {
func foo<T: NoDupReq1>(_ arg: T)
}
protocol Override3: Override2 {
func foo<T: NoDupReq2>(_ arg: T)
}

func checkRestatementNoDup1(_ arg: NoDupReq1 & NoDupReq2 & NoDupReq3) {
arg.#^NODUP_RESTATED_REQ1^#
arg#^NODUP_RESTATED_REQ_NODOT1^#
}
func checkRestatementNoDup2(_ arg: NoDupReq6) {
arg.#^NODUP_RESTATED_REQ2^#
}
func checkRestatementNoDup3<T: NoDupReq6>(_ arg: T) {
arg.#^NODUP_RESTATED_REQ3^#
T.#^NODUP_RESTATED_REQ_TYPE1^#
arg#^NODUP_RESTATED_REQ_NODOT2^#
}
func checkRestatementNoDup4<T: NoDupReq1 & NoDupReq2 & NoDupReq3>(_ arg: T) {
arg.#^NODUP_RESTATED_REQ4^#
T.#^NODUP_RESTATED_REQ_TYPE2^#
}
func checkRestatementNoDup5<T: NoDupReq1 & NoDupReq23>(_ arg: T) {
arg.#^NODUP_RESTATED_REQ5^#
T.#^NODUP_RESTATED_REQ_TYPE3^#
}
func checkRestatementNoDup6(_ arg: NoDupReq1 & NoDupReq23) {
arg.#^NODUP_RESTATED_REQ6^#
arg#^NODUP_RESTATED_REQ_NODOT3^#
}
func checkOverrideInclusion1(_ arg: Override) {
arg.#^CHECK_PROT_OVERRIDES1^#
}
func checkOverrideInclusion2(_ arg: Override3) {
arg.#^CHECK_PROT_OVERRIDES2^#
}

// CHECK_NODUP_RESTATED_REQ: Begin completions
// CHECK_NODUP_RESTATED_REQ-DAG: Decl[InstanceMethod]/{{Super|CurrNominal}}: foo()[#Void#]; name=foo()
// CHECK_NODUP_RESTATED_REQ-DAG: Decl[InstanceMethod]/{{Super|CurrNominal}}: roo({#arg1: Int#})[#Void#]
// CHECK_NODUP_RESTATED_REQ-DAG: Decl[InstanceVar]/{{Super|CurrNominal}}: doo[#Int#]; name=doo
// CHECK_NODUP_RESTATED_REQ-DAG: Decl[InstanceMethod]/{{Super|CurrNominal}}: roo({#arg2: Int#})[#Void#]
// CHECK_NODUP_RESTATED_REQ-NOT: Decl[InstanceMethod]/{{Super|CurrNominal}}: foo()[#Void#]; name=foo()
// CHECK_NODUP_RESTATED_REQ-NOT: Decl[InstanceVar]/{{Super|CurrNominal}}: doo[#Int#]; name=doo
// CHECK_NODUP_RESTATED_REQ: End completions

// CHECK_NODUP_RESTATED_REQ_NODOT: Begin completions
// CHECK_NODUP_RESTATED_REQ_NODOT: Decl[InstanceMethod]/{{Super|CurrNominal}}: .foo()[#Void#]; name=foo()
// CHECK_NODUP_RESTATED_REQ_NODOT: Decl[InstanceMethod]/{{Super|CurrNominal}}: .roo({#arg1: Int#})[#Void#];
// CHECK_NODUP_RESTATED_REQ_NODOT: Decl[Subscript]/{{Super|CurrNominal}}: [{#Bool#}][#Bool#]; name=[Bool]
// CHECK_NODUP_RESTATED_REQ_NODOT: Decl[InstanceVar]/{{Super|CurrNominal}}: .doo[#Int#]; name=doo
// CHECK_NODUP_RESTATED_REQ_NODOT: Decl[InstanceMethod]/{{Super|CurrNominal}}: .roo({#arg2: Int#})[#Void#];
// CHECK_NODUP_RESTATED_REQ_NODOT-NOT: Decl[InstanceMethod]/{{Super|CurrNominal}}: .foo()[#Void#]; name=foo()
// CHECK_NODUP_RESTATED_REQ_NODOT-NOT: Decl[Subscript]/{{Super|CurrNominal}}: [{#Bool#}][#Bool#]; name=[Bool]
// CHECK_NODUP_RESTATED_REQ_NODOT-NOT: Decl[InstanceVar]/{{Super|CurrNominal}}: .doo[#Int#]; name=doo
// CHECK_NODUP_RESTATED_REQ_NODOT: End completions

// CHECK_NODUP_RESTATED_REQ_TYPE: Begin completions
// CHECK_NODUP_RESTATED_REQ_TYPE-DAG: Decl[InstanceMethod]/Super: foo({#self: [[ARG:.+]]#})[#() -> Void#]; name=foo([[ARG]])
// CHECK_NODUP_RESTATED_REQ_TYPE-DAG: Decl[InstanceMethod]/Super: roo({#self: [[ARG]]#})[#(arg1: Int) -> Void#]; name=roo([[ARG]])
// CHECK_NODUP_RESTATED_REQ_TYPE-DAG: Decl[AssociatedType]/Super: E; name=E
// CHECK_NODUP_RESTATED_REQ_TYPE-DAG: Decl[InstanceMethod]/Super: roo({#self: [[ARG]]#})[#(arg2: Int) -> Void#]; name=roo([[ARG]])
// CHECK_NODUP_RESTATED_REQ_TYPE-NOT: Decl[InstanceMethod]/Super: foo({#self: [[ARG:.+]]#})[#() -> Void#]; name=foo([[ARG]])
// CHECK_NODUP_RESTATED_REQ_TYPE-NOT: Decl[AssociatedType]/Super: E; name=E
// CHECK_NODUP_RESTATED_REQ_TYPE: End completions

// CHECK_PROT_OVERRIDES: Decl[InstanceMethod]/{{Super|CurrNominal}}: foo({#(arg): NoDupReq1#})[#Void#]; name=foo(arg: NoDupReq1)
// CHECK_PROT_OVERRIDES: Decl[InstanceMethod]/{{Super|CurrNominal}}: foo({#(arg): NoDupReq2#})[#Void#]; name=foo(arg: NoDupReq2)

struct OnlyMe {}
protocol P4 {
associatedtype T
Expand Down
Loading