From 5753754d0670e10310ec0d5958fa0d00c4473544 Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Thu, 21 May 2020 16:21:59 -0700 Subject: [PATCH] [CodeCompletion] Add keypath apply subscript after open bracket rdar://problem/61016147 (cherry picked from commit 38d8b135202a7030640757b56ff27e3c948de286) --- lib/IDE/CodeCompletion.cpp | 36 ++++++++++++++++++++----------- lib/IDE/ExprContextAnalysis.cpp | 23 +++++++++++++++++--- test/IDE/complete_subscript.swift | 25 +++++++++++++++------ 3 files changed, 63 insertions(+), 21 deletions(-) diff --git a/lib/IDE/CodeCompletion.cpp b/lib/IDE/CodeCompletion.cpp index e9d4f5279fbea..bd650df50a1af 100644 --- a/lib/IDE/CodeCompletion.cpp +++ b/lib/IDE/CodeCompletion.cpp @@ -2762,23 +2762,32 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { const AnyFunctionType *AFT, const SubscriptDecl *SD, const Optional SemanticContext = None) { foundFunction(AFT); - auto genericSig = - SD->getInnermostDeclContext()->getGenericSignatureOfContext(); - AFT = eraseArchetypes(const_cast(AFT), genericSig) - ->castTo(); + if (SD) { + auto genericSig = + SD->getInnermostDeclContext()->getGenericSignatureOfContext(); + AFT = eraseArchetypes(const_cast(AFT), genericSig) + ->castTo(); + } CommandWordsPairs Pairs; CodeCompletionResultBuilder Builder( - Sink, CodeCompletionResult::ResultKind::Declaration, + Sink, + SD ? CodeCompletionResult::ResultKind::Declaration + : CodeCompletionResult::ResultKind::Pattern, SemanticContext ? *SemanticContext : getSemanticContextKind(SD), expectedTypeContext); - Builder.setAssociatedDecl(SD); - setClangDeclKeywords(SD, Pairs, Builder); + if (SD) { + Builder.setAssociatedDecl(SD); + setClangDeclKeywords(SD, Pairs, Builder); + } if (!HaveLParen) Builder.addLeftBracket(); else Builder.addAnnotatedLeftBracket(); - addCallArgumentPatterns(Builder, AFT, SD->getIndices()); + ArrayRef declParams; + if (SD) + declParams = SD->getIndices()->getArray(); + addCallArgumentPatterns(Builder, AFT->getParams(), declParams); if (!HaveLParen) Builder.addRightBracket(); else @@ -6013,7 +6022,7 @@ void CodeCompletionCallbacksImpl::doneParsing() { Lookup.getUnresolvedMemberCompletions(ContextInfo.getPossibleTypes()); break; } - case CompletionKind::CallArg : { + case CompletionKind::CallArg: { ExprContextInfo ContextInfo(CurDeclContext, CodeCompleteTokenExpr); bool shouldPerformGlobalCompletion = true; @@ -6022,9 +6031,12 @@ void CodeCompletionCallbacksImpl::doneParsing() { !ContextInfo.getPossibleCallees().empty()) { Lookup.setHaveLParen(true); for (auto &typeAndDecl : ContextInfo.getPossibleCallees()) { - if (auto SD = dyn_cast_or_null(typeAndDecl.Decl)) { - Lookup.addSubscriptCallPattern(typeAndDecl.Type, SD, - typeAndDecl.SemanticContext); + auto apply = ContextInfo.getAnalyzedExpr(); + if (apply && isa(apply)) { + Lookup.addSubscriptCallPattern( + typeAndDecl.Type, + dyn_cast_or_null(typeAndDecl.Decl), + typeAndDecl.SemanticContext); } else { Lookup.addFunctionCallPattern( typeAndDecl.Type, diff --git a/lib/IDE/ExprContextAnalysis.cpp b/lib/IDE/ExprContextAnalysis.cpp index ff5034f10f527..22857c51466f2 100644 --- a/lib/IDE/ExprContextAnalysis.cpp +++ b/lib/IDE/ExprContextAnalysis.cpp @@ -299,8 +299,11 @@ class ExprParentFinder : public ASTWalker { static void collectPossibleCalleesByQualifiedLookup( DeclContext &DC, Type baseTy, DeclNameRef name, SmallVectorImpl &candidates) { - bool isOnMetaType = baseTy->is(); auto baseInstanceTy = baseTy->getMetatypeInstanceType(); + if (!baseInstanceTy->mayHaveMembers()) + return; + + bool isOnMetaType = baseTy->is(); SmallVector decls; if (!DC.lookupQualified(baseInstanceTy, @@ -389,8 +392,6 @@ static void collectPossibleCalleesByQualifiedLookup( baseTy = *baseTyOpt; } baseTy = baseTy->getWithoutSpecifierType(); - if (!baseTy->getMetatypeInstanceType()->mayHaveMembers()) - return; // Use metatype for lookup 'super.init' if it's inside constructors. if (isa(baseExpr) && isa(DC) && @@ -398,6 +399,22 @@ static void collectPossibleCalleesByQualifiedLookup( baseTy = MetatypeType::get(baseTy); collectPossibleCalleesByQualifiedLookup(DC, baseTy, name, candidates); + + // Add virtual 'subscript(keyPath: KeyPath) -> Value'. + if (name.getBaseName() == DeclBaseName::createSubscript() && + (baseTy->getAnyNominal() || baseTy->is() || + baseTy->is())) { + auto &Ctx = DC.getASTContext(); + + auto *kpDecl = Ctx.getKeyPathDecl(); + Type kpTy = kpDecl->mapTypeIntoContext(kpDecl->getDeclaredInterfaceType()); + Type kpValueTy = kpTy->castTo()->getGenericArgs()[1]; + kpTy = BoundGenericType::get(kpDecl, Type(), {baseTy, kpValueTy}); + + Type fnTy = FunctionType::get( + {AnyFunctionType::Param(kpTy, Ctx.Id_keyPath)}, kpValueTy); + candidates.emplace_back(fnTy->castTo(), nullptr); + } } /// For the given \c callExpr, collect possible callee types and declarations. diff --git a/test/IDE/complete_subscript.swift b/test/IDE/complete_subscript.swift index afd47cb1fb798..dfb74fafc1c0b 100644 --- a/test/IDE/complete_subscript.swift +++ b/test/IDE/complete_subscript.swift @@ -16,6 +16,7 @@ // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=SUPER_IN_STATICMETHOD | %FileCheck %s -check-prefix=SUPER_IN_STATICMETHOD // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=LABELED_SUBSCRIPT | %FileCheck %s -check-prefix=LABELED_SUBSCRIPT +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TUPLE | %FileCheck %s -check-prefix=TUPLE struct MyStruct { static subscript(x: Int, static defValue: T) -> MyStruct { @@ -62,6 +63,7 @@ func test1() { let _ = MyStruct()[#^INSTANCE_INT_BRACKET^# // INSTANCE_INT_BRACKET: Begin completions // INSTANCE_INT_BRACKET-DAG: Decl[Subscript]/CurrNominal: ['[']{#(x): Int#}, {#instance: Int#}[']'][#Int#]; +// INSTANCE_INT_BRACKET-DAG: Pattern/CurrModule: ['[']{#keyPath: KeyPath, Value>#}[']'][#Value#]; // INSTANCE_INT_BRACKET: End completions } func test2(value: MyStruct) { @@ -87,6 +89,7 @@ func test2(value: MyStruct) { let _ = value[#^INSTANCE_ARCHETYPE_BRACKET^# // INSTANCE_ARCHETYPE_BRACKET: Begin completions // INSTANCE_ARCHETYPE_BRACKET-DAG: Decl[Subscript]/CurrNominal: ['[']{#(x): Int#}, {#instance: U#}[']'][#Int#]; +// INSTANCE_ARCHETYPE_BRACKET-DAG: Pattern/CurrModule: ['[']{#keyPath: KeyPath, Value>#}[']'][#Value#]; // INSTANCE_ARCHETYPE_BRACKET: End completions let _ = MyStruct[42, #^METATYPE_LABEL^# @@ -110,14 +113,16 @@ class Derived: Base { func testInstance() { let _ = self[#^SELF_IN_INSTANCEMETHOD^#] -// SELF_IN_INSTANCEMETHOD: Begin completions, 2 items +// SELF_IN_INSTANCEMETHOD: Begin completions, 3 items // SELF_IN_INSTANCEMETHOD-DAG: Decl[Subscript]/CurrNominal: ['[']{#derivedInstance: Int#}[']'][#Int#]; // SELF_IN_INSTANCEMETHOD-DAG: Decl[Subscript]/Super: ['[']{#instance: Int#}[']'][#Int#]; +// SELF_IN_INSTANCEMETHOD-DAG: Pattern/CurrModule: ['[']{#keyPath: KeyPath#}[']'][#Value#]; // SELF_IN_INSTANCEMETHOD: End completions let _ = super[#^SUPER_IN_INSTANCEMETHOD^#] -// SUPER_IN_INSTANCEMETHOD: Begin completions, 1 items -// SUPER_IN_INSTANCEMETHOD-DAG: Decl[Subscript]/CurrNominal: ['[']{#instance: Int#}[']'][#Int#]; +// SUPER_IN_INSTANCEMETHOD: Begin completions, 2 items +// SUPER_IN_INSTANCEMETHOD-DAG: Decl[Subscript]/CurrNominal: ['[']{#instance: Int#}[']'][#Int#]; +// SUPER_IN_INSTANCEMETHOD-DAG: Pattern/CurrModule: ['[']{#keyPath: KeyPath#}[']'][#Value#]; // SUPER_IN_INSTANCEMETHOD: End completions } @@ -130,7 +135,7 @@ class Derived: Base { let _ = super[#^SUPER_IN_STATICMETHOD^#] // SUPER_IN_STATICMETHOD: Begin completions, 1 items -// SUPER_IN_STATICMETHOD-DAG: Decl[Subscript]/CurrNominal: ['[']{#static: Int#}[']'][#Int#]; +// SUPER_IN_STATICMETHOD-DAG: Decl[Subscript]/CurrNominal: ['[']{#static: Int#}[']'][#Int#]; // SUPER_IN_STATICMETHOD: End completions } } @@ -140,7 +145,15 @@ struct MyStruct1 { } func testSubscriptCallSig(val: MyStruct1) { val[#^LABELED_SUBSCRIPT^# -// LABELED_SUBSCRIPT: Begin completions, 1 items -// LABELED_SUBSCRIPT: Decl[Subscript]/CurrNominal: ['[']{#idx1: Int#}, {#idx2: Comparable#}[']'][#Int!#]; +// LABELED_SUBSCRIPT: Begin completions, 2 items +// LABELED_SUBSCRIPT-DAG: Decl[Subscript]/CurrNominal: ['[']{#idx1: Int#}, {#idx2: Comparable#}[']'][#Int!#]; +// LABELED_SUBSCRIPT-DAG: Pattern/CurrModule: ['[']{#keyPath: KeyPath, Value>#}[']'][#Value#]; // LABELED_SUBSCRIPT: End completions } + +func testSubcscriptTuple(val: (x: Int, String)) { + val[#^TUPLE^#] +// TUPLE: Begin completions, 1 items +// TUPLE-DAG: Pattern/CurrModule: ['[']{#keyPath: KeyPath<(x: Int, String), Value>#}[']'][#Value#]; +// TUPLE: End completions +}