@@ -280,6 +280,10 @@ void getSwiftDocKeyword(const Decl* D, CommandWordsPairs &Words) {
280
280
281
281
typedef llvm::function_ref<bool (ValueDecl*, DeclVisibilityKind)> DeclFilter;
282
282
DeclFilter 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
+ };
283
287
284
288
std::string swift::ide::removeCodeCompletionTokens (
285
289
StringRef Input, StringRef TokenName, unsigned *CompletionOffset) {
@@ -1281,16 +1285,24 @@ class CodeCompletionCallbacksImpl : public CodeCompletionCallbacks {
1281
1285
1282
1286
Optional<Type> getTypeOfParsedExpr () {
1283
1287
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
+
1284
1295
// If we've already successfully type-checked the expression for some
1285
1296
// reason, just return the type.
1286
1297
// FIXME: if it's ErrorType but we've already typechecked we shouldn't
1287
1298
// typecheck again. rdar://21466394
1288
- if (ParsedExpr->getType () && !ParsedExpr->getType ()->is <ErrorType>())
1299
+ if (CheckKind == CompletionTypeCheckKind::Normal &&
1300
+ ParsedExpr->getType () && !ParsedExpr->getType ()->is <ErrorType>())
1289
1301
return ParsedExpr->getType ();
1290
1302
1291
1303
Expr *ModifiedExpr = ParsedExpr;
1292
1304
if (auto T = getTypeOfCompletionContextExpr (P.Context , CurDeclContext,
1293
- ModifiedExpr)) {
1305
+ CheckKind, ModifiedExpr)) {
1294
1306
// FIXME: even though we don't apply the solution, the type checker may
1295
1307
// modify the original expression. We should understand what effect that
1296
1308
// may have on code completion.
@@ -1323,6 +1335,7 @@ class CodeCompletionCallbacksImpl : public CodeCompletionCallbacks {
1323
1335
void completePostfixExprParen (Expr *E, Expr *CodeCompletionE) override ;
1324
1336
void completeExprSuper (SuperRefExpr *SRE) override ;
1325
1337
void completeExprSuperDot (SuperRefExpr *SRE) override ;
1338
+ void completeExprKeyPath (ObjCKeyPathExpr *KPE, bool HasDot) override ;
1326
1339
1327
1340
void completeTypeSimpleBeginning () override ;
1328
1341
void completeTypeIdentifierWithDot (IdentTypeRepr *ITR) override ;
@@ -1525,6 +1538,7 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
1525
1538
bool HaveLParen = false ;
1526
1539
bool HaveRParen = false ;
1527
1540
bool IsSuperRefExpr = false ;
1541
+ bool IsKeyPathExpr = false ;
1528
1542
bool IsDynamicLookup = false ;
1529
1543
bool PreferFunctionReferencesToCalls = false ;
1530
1544
bool HaveLeadingSpace = false ;
@@ -1677,6 +1691,10 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
1677
1691
IsSuperRefExpr = true ;
1678
1692
}
1679
1693
1694
+ void setIsKeyPathExpr () {
1695
+ IsKeyPathExpr = true ;
1696
+ }
1697
+
1680
1698
void setIsDynamicLookup () {
1681
1699
IsDynamicLookup = true ;
1682
1700
}
@@ -2147,6 +2165,25 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
2147
2165
Builder.addRightParen ();
2148
2166
}
2149
2167
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
+
2150
2187
void addFunctionCallPattern (const AnyFunctionType *AFT,
2151
2188
const AbstractFunctionDecl *AFD = nullptr ) {
2152
2189
foundFunction (AFT);
@@ -2596,6 +2633,9 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
2596
2633
if (D->getName ().isEditorPlaceholder ())
2597
2634
return ;
2598
2635
2636
+ if (IsKeyPathExpr && !KeyPathFilter (D, Reason))
2637
+ return ;
2638
+
2599
2639
if (!D->hasType ())
2600
2640
TypeResolver->resolveDeclSignature (D);
2601
2641
else if (isa<TypeAliasDecl>(D)) {
@@ -3046,7 +3086,9 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
3046
3086
3047
3087
if (auto T = getTypeOfCompletionContextExpr (
3048
3088
CurrDeclContext->getASTContext (),
3049
- const_cast <DeclContext *>(CurrDeclContext), tempExpr))
3089
+ const_cast <DeclContext *>(CurrDeclContext),
3090
+ CompletionTypeCheckKind::Normal,
3091
+ tempExpr))
3050
3092
addPostfixOperatorCompletion (op, *T);
3051
3093
}
3052
3094
@@ -3371,7 +3413,8 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
3371
3413
void getValueCompletionsInDeclContext (SourceLoc Loc,
3372
3414
DeclFilter Filter = DefaultFilter,
3373
3415
bool IncludeTopLevel = false ,
3374
- bool RequestCache = true ) {
3416
+ bool RequestCache = true ,
3417
+ bool LiteralCompletions = true ) {
3375
3418
Kind = LookupKind::ValueInDeclContext;
3376
3419
NeedLeadingDot = false ;
3377
3420
FilteredDeclConsumer Consumer (*this , Filter);
@@ -3397,19 +3440,33 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
3397
3440
}
3398
3441
}
3399
3442
3400
- addValueLiteralCompletions ();
3443
+ if (LiteralCompletions)
3444
+ addValueLiteralCompletions ();
3401
3445
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.
3403
3448
if (Ctx.LangOpts .EnableObjCInterop ) {
3449
+ bool addedSelector = false ;
3450
+ bool addedKeyPath = false ;
3404
3451
for (auto T : ExpectedTypes) {
3405
3452
T = T->lookThroughAllAnyOptionalTypes ();
3406
3453
if (auto structDecl = T->getStructOrBoundGenericStruct ()) {
3407
- if (structDecl->getName () == Ctx.Id_Selector &&
3454
+ if (!addedSelector &&
3455
+ structDecl->getName () == Ctx.Id_Selector &&
3408
3456
structDecl->getParentModule ()->getName () == Ctx.Id_ObjectiveC ) {
3409
3457
addPoundSelector (/* needPound=*/ true );
3410
- break ;
3458
+ if (addedKeyPath) break ;
3459
+ addedSelector = true ;
3460
+ continue ;
3411
3461
}
3412
3462
}
3463
+
3464
+ if (!addedKeyPath && T->getAnyNominal () == Ctx.getStringDecl ()) {
3465
+ addPoundKeyPath (/* needPound=*/ true );
3466
+ if (addedSelector) break ;
3467
+ addedKeyPath = true ;
3468
+ continue ;
3469
+ }
3413
3470
}
3414
3471
}
3415
3472
}
@@ -4192,6 +4249,13 @@ void CodeCompletionCallbacksImpl::completeExprSuperDot(SuperRefExpr *SRE) {
4192
4249
CurDeclContext = P.CurDeclContext ;
4193
4250
}
4194
4251
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
+
4195
4259
void CodeCompletionCallbacksImpl::completePoundAvailablePlatform () {
4196
4260
Kind = CompletionKind::PoundAvailablePlatform;
4197
4261
CurDeclContext = P.CurDeclContext ;
@@ -4447,6 +4511,8 @@ void CodeCompletionCallbacksImpl::addKeywords(CodeCompletionResultSink &Sink) {
4447
4511
case CompletionKind::CallArg:
4448
4512
case CompletionKind::AfterPound:
4449
4513
case CompletionKind::GenericParams:
4514
+ case CompletionKind::KeyPathExpr:
4515
+ case CompletionKind::KeyPathExprDot:
4450
4516
break ;
4451
4517
4452
4518
case CompletionKind::StmtOrExpr:
@@ -4722,7 +4788,9 @@ void CodeCompletionCallbacksImpl::doneParsing() {
4722
4788
if (ParsedExpr) {
4723
4789
ExprType = getTypeOfParsedExpr ();
4724
4790
if (!ExprType && Kind != CompletionKind::PostfixExprParen &&
4725
- Kind != CompletionKind::CallArg)
4791
+ Kind != CompletionKind::CallArg &&
4792
+ Kind != CompletionKind::KeyPathExpr &&
4793
+ Kind != CompletionKind::KeyPathExprDot)
4726
4794
return ;
4727
4795
if (ExprType)
4728
4796
ParsedExpr->setType (*ExprType);
@@ -4856,6 +4924,27 @@ void CodeCompletionCallbacksImpl::doneParsing() {
4856
4924
break ;
4857
4925
}
4858
4926
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
+
4859
4948
case CompletionKind::TypeSimpleBeginning: {
4860
4949
Lookup.getTypeCompletionsInDeclContext (
4861
4950
P.Context .SourceMgr .getCodeCompletionLoc ());
@@ -4965,6 +5054,7 @@ void CodeCompletionCallbacksImpl::doneParsing() {
4965
5054
case CompletionKind::AfterPound: {
4966
5055
Lookup.addPoundAvailable (ParentStmtKind);
4967
5056
Lookup.addPoundSelector (/* needPound=*/ false );
5057
+ Lookup.addPoundKeyPath (/* needPound=*/ false );
4968
5058
break ;
4969
5059
}
4970
5060
0 commit comments