Skip to content

Commit 8d4447c

Browse files
committed
[code-completion] Add completion for keypath dynamic member lookup
Looks into the root type of the keypath to find additional members. This does not currently map the type of the completion to the subscript's return type. rdar://49029126
1 parent effab8c commit 8d4447c

File tree

5 files changed

+321
-5
lines changed

5 files changed

+321
-5
lines changed

include/swift/Sema/IDETypeChecking.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,18 @@ namespace swift {
227227
/// Sometimes for diagnostics we want to work on the original argument list as
228228
/// written by the user; this performs the reverse transformation.
229229
OriginalArgumentList getOriginalArgumentList(Expr *expr);
230+
231+
/// Return true if the specified type or a super-class/super-protocol has the
232+
/// @dynamicMemberLookup attribute on it.
233+
bool hasDynamicMemberLookupAttribute(Type type);
234+
235+
/// Returns the root type of the keypath type in a keypath dynamic member
236+
/// lookup subscript, or \c None if it cannot be determined.
237+
///
238+
/// \param subscript The potential keypath dynamic member lookup subscript.
239+
/// \param DC The DeclContext from which the subscript is being referenced.
240+
Optional<Type> getRootTypeOfKeypathDynamicMember(SubscriptDecl *subscript,
241+
const DeclContext *DC);
230242
}
231243

232244
#endif

lib/Sema/CSSimplify.cpp

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "swift/AST/ProtocolConformance.h"
2525
#include "swift/Basic/StringExtras.h"
2626
#include "swift/ClangImporter/ClangModule.h"
27+
#include "swift/Sema/IDETypeChecking.h"
2728
#include "llvm/ADT/SetVector.h"
2829
#include "llvm/Support/Compiler.h"
2930

@@ -3563,6 +3564,12 @@ static bool hasDynamicMemberLookupAttribute(Type type,
35633564
return result;
35643565
}
35653566

3567+
// for IDETypeChecking
3568+
bool swift::hasDynamicMemberLookupAttribute(Type type) {
3569+
llvm::DenseMap<CanType, bool> DynamicMemberLookupCache;
3570+
return ::hasDynamicMemberLookupAttribute(type, DynamicMemberLookupCache);
3571+
}
3572+
35663573
static bool isKeyPathDynamicMemberLookup(ConstraintLocator *locator) {
35673574
auto path = locator ? locator->getPath() : None;
35683575
return !path.empty() && path.back().isKeyPathDynamicMember();
@@ -3937,8 +3944,8 @@ performMemberLookup(ConstraintKind constraintKind, DeclName memberName,
39373944
// as representing "dynamic lookup" unless it's a direct call
39383945
// to such subscript (in that case label is expected to match).
39393946
if (auto *subscript = dyn_cast<SubscriptDecl>(cand)) {
3940-
if (hasDynamicMemberLookupAttribute(instanceTy,
3941-
DynamicMemberLookupCache) &&
3947+
if (::hasDynamicMemberLookupAttribute(instanceTy,
3948+
DynamicMemberLookupCache) &&
39423949
isValidKeyPathDynamicMemberLookup(subscript, TC)) {
39433950
auto info =
39443951
getArgumentLabels(*this, ConstraintLocatorBuilder(memberLocator));
@@ -4030,7 +4037,8 @@ performMemberLookup(ConstraintKind constraintKind, DeclName memberName,
40304037
constraintKind == ConstraintKind::ValueMember &&
40314038
memberName.isSimpleName() && !memberName.isSpecial()) {
40324039
auto name = memberName.getBaseIdentifier();
4033-
if (hasDynamicMemberLookupAttribute(instanceTy, DynamicMemberLookupCache)) {
4040+
if (::hasDynamicMemberLookupAttribute(instanceTy,
4041+
DynamicMemberLookupCache)) {
40344042
auto &ctx = getASTContext();
40354043

40364044
// Recursively look up `subscript(dynamicMember:)` methods in this type.

lib/Sema/LookupVisibleDecls.cpp

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -621,6 +621,41 @@ static void lookupVisibleMemberDeclsImpl(
621621
} while (1);
622622
}
623623

624+
static void lookupVisibleDynamicMemberLookupDecls(
625+
Type baseType, VisibleDeclConsumer &consumer, const DeclContext *dc,
626+
LookupState LS, DeclVisibilityKind reason, LazyResolver *typeResolver,
627+
GenericSignatureBuilder *GSB, VisitedSet &visited) {
628+
629+
if (!hasDynamicMemberLookupAttribute(baseType))
630+
return;
631+
632+
auto &ctx = dc->getASTContext();
633+
634+
// Lookup the `subscript(dynamicMember:)` methods in this type.
635+
auto subscriptName =
636+
DeclName(ctx, DeclBaseName::createSubscript(), ctx.Id_dynamicMember);
637+
638+
SmallVector<ValueDecl *, 2> subscripts;
639+
dc->lookupQualified(baseType, subscriptName, NL_QualifiedDefault,
640+
typeResolver, subscripts);
641+
642+
for (ValueDecl *VD : subscripts) {
643+
auto *subscript = dyn_cast<SubscriptDecl>(VD);
644+
if (!subscript)
645+
continue;
646+
647+
auto rootType = getRootTypeOfKeypathDynamicMember(subscript, dc);
648+
if (!rootType)
649+
continue;
650+
651+
auto subs =
652+
baseType->getMemberSubstitutionMap(dc->getParentModule(), subscript);
653+
if (auto memberType = rootType->subst(subs))
654+
lookupVisibleMemberDeclsImpl(memberType, consumer, dc, LS, reason,
655+
typeResolver, GSB, visited);
656+
}
657+
}
658+
624659
namespace {
625660

626661
struct FoundDeclTy {
@@ -822,6 +857,18 @@ class OverrideFilteringConsumer : public VisibleDeclConsumer {
822857
DeclsToReport.insert(FoundDeclTy(VD, Reason));
823858
}
824859
};
860+
861+
struct ShadowedKeyPathMembers : public VisibleDeclConsumer {
862+
VisibleDeclConsumer &consumer;
863+
llvm::DenseSet<DeclBaseName> &seen;
864+
ShadowedKeyPathMembers(VisibleDeclConsumer &consumer, llvm::DenseSet<DeclBaseName> &knownMembers) : consumer(consumer), seen(knownMembers) {}
865+
void foundDecl(ValueDecl *VD, DeclVisibilityKind reason) override {
866+
// Dynamic lookup members are only visible if they are not shadowed by
867+
// non-dynamic members.
868+
if (seen.count(VD->getBaseName()) == 0)
869+
consumer.foundDecl(VD, reason);
870+
}
871+
};
825872
} // end anonymous namespace
826873

827874
/// Enumerate all members in \c BaseTy (including members of extensions,
@@ -839,6 +886,14 @@ static void lookupVisibleMemberDecls(
839886
lookupVisibleMemberDeclsImpl(BaseTy, overrideConsumer, CurrDC, LS, Reason,
840887
TypeResolver, GSB, Visited);
841888

889+
llvm::DenseSet<DeclBaseName> knownMembers;
890+
for (auto &kv : overrideConsumer.FoundDecls) {
891+
knownMembers.insert(kv.first);
892+
}
893+
ShadowedKeyPathMembers dynamicConsumer(overrideConsumer, knownMembers);
894+
lookupVisibleDynamicMemberLookupDecls(BaseTy, dynamicConsumer, CurrDC, LS,
895+
Reason, TypeResolver, GSB, Visited);
896+
842897
// Report the declarations we found to the real consumer.
843898
for (const auto &DeclAndReason : overrideConsumer.DeclsToReport)
844899
Consumer.foundDecl(DeclAndReason.D, DeclAndReason.Reason);

lib/Sema/TypeCheckAttr.cpp

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,21 @@
1414
//
1515
//===----------------------------------------------------------------------===//
1616

17-
#include "TypeChecker.h"
1817
#include "MiscDiagnostics.h"
1918
#include "TypeCheckType.h"
20-
#include "swift/AST/GenericSignatureBuilder.h"
19+
#include "TypeChecker.h"
2120
#include "swift/AST/ASTVisitor.h"
2221
#include "swift/AST/ClangModuleLoader.h"
2322
#include "swift/AST/DiagnosticsParse.h"
2423
#include "swift/AST/GenericEnvironment.h"
24+
#include "swift/AST/GenericSignatureBuilder.h"
2525
#include "swift/AST/NameLookup.h"
2626
#include "swift/AST/NameLookupRequests.h"
2727
#include "swift/AST/ParameterList.h"
2828
#include "swift/AST/TypeCheckRequests.h"
2929
#include "swift/AST/Types.h"
3030
#include "swift/Parse/Lexer.h"
31+
#include "swift/Sema/IDETypeChecking.h"
3132
#include "llvm/Support/Debug.h"
3233

3334
using namespace swift;
@@ -1032,6 +1033,25 @@ bool swift::isValidKeyPathDynamicMemberLookup(SubscriptDecl *decl,
10321033
return false;
10331034
}
10341035

1036+
Optional<Type>
1037+
swift::getRootTypeOfKeypathDynamicMember(SubscriptDecl *subscript,
1038+
const DeclContext *DC) {
1039+
auto &TC = TypeChecker::createForContext(DC->getASTContext());
1040+
1041+
if (!isValidKeyPathDynamicMemberLookup(subscript, TC))
1042+
return None;
1043+
1044+
const auto *param = subscript->getIndices()->get(0);
1045+
auto keyPathType = param->getType()->getAs<BoundGenericType>();
1046+
if (!keyPathType)
1047+
return None;
1048+
1049+
assert(!keyPathType->getGenericArgs().empty() &&
1050+
"invalid keypath dynamic member");
1051+
auto rootType = keyPathType->getGenericArgs()[0];
1052+
return rootType;
1053+
}
1054+
10351055
/// The @dynamicMemberLookup attribute is only allowed on types that have at
10361056
/// least one subscript member declared like this:
10371057
///

0 commit comments

Comments
 (0)