@@ -280,6 +280,10 @@ void getSwiftDocKeyword(const Decl* D, CommandWordsPairs &Words) {
280280
281281typedef llvm::function_ref<bool (ValueDecl*, DeclVisibilityKind)> DeclFilter;
282282DeclFilter DefaultFilter = [] (ValueDecl* VD, DeclVisibilityKind Kind) {return true ;};
283+ DeclFilter KeyPathFilter = [](ValueDecl* decl, DeclVisibilityKind) -> bool {
284+ return isa<TypeDecl>(decl) ||
285+ (isa<VarDecl>(decl) && decl->getDeclContext ()->isTypeContext ());
286+ };
283287
284288std::string swift::ide::removeCodeCompletionTokens (
285289 StringRef Input, StringRef TokenName, unsigned *CompletionOffset) {
@@ -1281,16 +1285,24 @@ class CodeCompletionCallbacksImpl : public CodeCompletionCallbacks {
12811285
12821286 Optional<Type> getTypeOfParsedExpr () {
12831287 assert (ParsedExpr && " should have an expression" );
1288+
1289+ // Figure out the kind of type-check we'll be performing.
1290+ auto CheckKind = CompletionTypeCheckKind::Normal;
1291+ if (Kind == CompletionKind::KeyPathExpr ||
1292+ Kind == CompletionKind::KeyPathExprDot)
1293+ CheckKind = CompletionTypeCheckKind::ObjCKeyPath;
1294+
12841295 // If we've already successfully type-checked the expression for some
12851296 // reason, just return the type.
12861297 // FIXME: if it's ErrorType but we've already typechecked we shouldn't
12871298 // typecheck again. rdar://21466394
1288- if (ParsedExpr->getType () && !ParsedExpr->getType ()->is <ErrorType>())
1299+ if (CheckKind == CompletionTypeCheckKind::Normal &&
1300+ ParsedExpr->getType () && !ParsedExpr->getType ()->is <ErrorType>())
12891301 return ParsedExpr->getType ();
12901302
12911303 Expr *ModifiedExpr = ParsedExpr;
12921304 if (auto T = getTypeOfCompletionContextExpr (P.Context , CurDeclContext,
1293- ModifiedExpr)) {
1305+ CheckKind, ModifiedExpr)) {
12941306 // FIXME: even though we don't apply the solution, the type checker may
12951307 // modify the original expression. We should understand what effect that
12961308 // may have on code completion.
@@ -1323,6 +1335,7 @@ class CodeCompletionCallbacksImpl : public CodeCompletionCallbacks {
13231335 void completePostfixExprParen (Expr *E, Expr *CodeCompletionE) override ;
13241336 void completeExprSuper (SuperRefExpr *SRE) override ;
13251337 void completeExprSuperDot (SuperRefExpr *SRE) override ;
1338+ void completeExprKeyPath (ObjCKeyPathExpr *KPE, bool HasDot) override ;
13261339
13271340 void completeTypeSimpleBeginning () override ;
13281341 void completeTypeIdentifierWithDot (IdentTypeRepr *ITR) override ;
@@ -1525,6 +1538,7 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
15251538 bool HaveLParen = false ;
15261539 bool HaveRParen = false ;
15271540 bool IsSuperRefExpr = false ;
1541+ bool IsKeyPathExpr = false ;
15281542 bool IsDynamicLookup = false ;
15291543 bool PreferFunctionReferencesToCalls = false ;
15301544 bool HaveLeadingSpace = false ;
@@ -1677,6 +1691,10 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
16771691 IsSuperRefExpr = true ;
16781692 }
16791693
1694+ void setIsKeyPathExpr () {
1695+ IsKeyPathExpr = true ;
1696+ }
1697+
16801698 void setIsDynamicLookup () {
16811699 IsDynamicLookup = true ;
16821700 }
@@ -2147,6 +2165,25 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
21472165 Builder.addRightParen ();
21482166 }
21492167
2168+ void addPoundKeyPath (bool needPound) {
2169+ // #keyPath is only available when the Objective-C runtime is.
2170+ if (!Ctx.LangOpts .EnableObjCInterop ) return ;
2171+
2172+ CodeCompletionResultBuilder Builder (
2173+ Sink,
2174+ CodeCompletionResult::ResultKind::Keyword,
2175+ SemanticContextKind::ExpressionSpecific,
2176+ ExpectedTypes);
2177+ if (needPound)
2178+ Builder.addTextChunk (" #keyPath" );
2179+ else
2180+ Builder.addTextChunk (" keyPath" );
2181+ Builder.addLeftParen ();
2182+ Builder.addSimpleTypedParameter (" @objc property sequence" ,
2183+ /* isVarArg=*/ false );
2184+ Builder.addRightParen ();
2185+ }
2186+
21502187 void addFunctionCallPattern (const AnyFunctionType *AFT,
21512188 const AbstractFunctionDecl *AFD = nullptr ) {
21522189 foundFunction (AFT);
@@ -2596,6 +2633,9 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
25962633 if (D->getName ().isEditorPlaceholder ())
25972634 return ;
25982635
2636+ if (IsKeyPathExpr && !KeyPathFilter (D, Reason))
2637+ return ;
2638+
25992639 if (!D->hasType ())
26002640 TypeResolver->resolveDeclSignature (D);
26012641 else if (isa<TypeAliasDecl>(D)) {
@@ -3046,7 +3086,9 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
30463086
30473087 if (auto T = getTypeOfCompletionContextExpr (
30483088 CurrDeclContext->getASTContext (),
3049- const_cast <DeclContext *>(CurrDeclContext), tempExpr))
3089+ const_cast <DeclContext *>(CurrDeclContext),
3090+ CompletionTypeCheckKind::Normal,
3091+ tempExpr))
30503092 addPostfixOperatorCompletion (op, *T);
30513093 }
30523094
@@ -3371,7 +3413,8 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
33713413 void getValueCompletionsInDeclContext (SourceLoc Loc,
33723414 DeclFilter Filter = DefaultFilter,
33733415 bool IncludeTopLevel = false ,
3374- bool RequestCache = true ) {
3416+ bool RequestCache = true ,
3417+ bool LiteralCompletions = true ) {
33753418 Kind = LookupKind::ValueInDeclContext;
33763419 NeedLeadingDot = false ;
33773420 FilteredDeclConsumer Consumer (*this , Filter);
@@ -3397,19 +3440,33 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
33973440 }
33983441 }
33993442
3400- addValueLiteralCompletions ();
3443+ if (LiteralCompletions)
3444+ addValueLiteralCompletions ();
34013445
3402- // If the expected type is ObjectiveC.Selector, add #selector.
3446+ // If the expected type is ObjectiveC.Selector, add #selector. If
3447+ // it's String, add #keyPath.
34033448 if (Ctx.LangOpts .EnableObjCInterop ) {
3449+ bool addedSelector = false ;
3450+ bool addedKeyPath = false ;
34043451 for (auto T : ExpectedTypes) {
34053452 T = T->lookThroughAllAnyOptionalTypes ();
34063453 if (auto structDecl = T->getStructOrBoundGenericStruct ()) {
3407- if (structDecl->getName () == Ctx.Id_Selector &&
3454+ if (!addedSelector &&
3455+ structDecl->getName () == Ctx.Id_Selector &&
34083456 structDecl->getParentModule ()->getName () == Ctx.Id_ObjectiveC ) {
34093457 addPoundSelector (/* needPound=*/ true );
3410- break ;
3458+ if (addedKeyPath) break ;
3459+ addedSelector = true ;
3460+ continue ;
34113461 }
34123462 }
3463+
3464+ if (!addedKeyPath && T->getAnyNominal () == Ctx.getStringDecl ()) {
3465+ addPoundKeyPath (/* needPound=*/ true );
3466+ if (addedSelector) break ;
3467+ addedKeyPath = true ;
3468+ continue ;
3469+ }
34133470 }
34143471 }
34153472 }
@@ -4192,6 +4249,13 @@ void CodeCompletionCallbacksImpl::completeExprSuperDot(SuperRefExpr *SRE) {
41924249 CurDeclContext = P.CurDeclContext ;
41934250}
41944251
4252+ void CodeCompletionCallbacksImpl::completeExprKeyPath (ObjCKeyPathExpr *KPE,
4253+ bool HasDot) {
4254+ Kind = HasDot ? CompletionKind::KeyPathExprDot : CompletionKind::KeyPathExpr;
4255+ ParsedExpr = KPE;
4256+ CurDeclContext = P.CurDeclContext ;
4257+ }
4258+
41954259void CodeCompletionCallbacksImpl::completePoundAvailablePlatform () {
41964260 Kind = CompletionKind::PoundAvailablePlatform;
41974261 CurDeclContext = P.CurDeclContext ;
@@ -4447,6 +4511,8 @@ void CodeCompletionCallbacksImpl::addKeywords(CodeCompletionResultSink &Sink) {
44474511 case CompletionKind::CallArg:
44484512 case CompletionKind::AfterPound:
44494513 case CompletionKind::GenericParams:
4514+ case CompletionKind::KeyPathExpr:
4515+ case CompletionKind::KeyPathExprDot:
44504516 break ;
44514517
44524518 case CompletionKind::StmtOrExpr:
@@ -4722,7 +4788,9 @@ void CodeCompletionCallbacksImpl::doneParsing() {
47224788 if (ParsedExpr) {
47234789 ExprType = getTypeOfParsedExpr ();
47244790 if (!ExprType && Kind != CompletionKind::PostfixExprParen &&
4725- Kind != CompletionKind::CallArg)
4791+ Kind != CompletionKind::CallArg &&
4792+ Kind != CompletionKind::KeyPathExpr &&
4793+ Kind != CompletionKind::KeyPathExprDot)
47264794 return ;
47274795 if (ExprType)
47284796 ParsedExpr->setType (*ExprType);
@@ -4856,6 +4924,27 @@ void CodeCompletionCallbacksImpl::doneParsing() {
48564924 break ;
48574925 }
48584926
4927+ case CompletionKind::KeyPathExprDot:
4928+ Lookup.setHaveDot (SourceLoc ());
4929+ SWIFT_FALLTHROUGH;
4930+
4931+ case CompletionKind::KeyPathExpr: {
4932+ Lookup.setIsKeyPathExpr ();
4933+ Lookup.includeInstanceMembers ();
4934+
4935+ if (ExprType) {
4936+ if (isDynamicLookup (*ExprType))
4937+ Lookup.setIsDynamicLookup ();
4938+
4939+ Lookup.getValueExprCompletions (*ExprType);
4940+ } else {
4941+ SourceLoc Loc = P.Context .SourceMgr .getCodeCompletionLoc ();
4942+ Lookup.getValueCompletionsInDeclContext (Loc, KeyPathFilter,
4943+ false , true , false );
4944+ }
4945+ break ;
4946+ }
4947+
48594948 case CompletionKind::TypeSimpleBeginning: {
48604949 Lookup.getTypeCompletionsInDeclContext (
48614950 P.Context .SourceMgr .getCodeCompletionLoc ());
@@ -4965,6 +5054,7 @@ void CodeCompletionCallbacksImpl::doneParsing() {
49655054 case CompletionKind::AfterPound: {
49665055 Lookup.addPoundAvailable (ParentStmtKind);
49675056 Lookup.addPoundSelector (/* needPound=*/ false );
5057+ Lookup.addPoundKeyPath (/* needPound=*/ false );
49685058 break ;
49695059 }
49705060
0 commit comments