From ecc5b90aa5fb832188ab9d8d50da6f9ef6ec2eb0 Mon Sep 17 00:00:00 2001 From: Luciano Almeida Date: Mon, 31 Aug 2020 21:52:20 -0300 Subject: [PATCH] [CodeCompletion] Do not erase dot for code completion key path optiona root unwrapped members --- lib/IDE/CodeCompletion.cpp | 8 ++- ...omplete_swift_key_path_optional_root.swift | 55 +++++++++++++++++++ 2 files changed, 61 insertions(+), 2 deletions(-) create mode 100644 test/IDE/complete_swift_key_path_optional_root.swift diff --git a/lib/IDE/CodeCompletion.cpp b/lib/IDE/CodeCompletion.cpp index 4d14814a55103..8488e6079ea3b 100644 --- a/lib/IDE/CodeCompletion.cpp +++ b/lib/IDE/CodeCompletion.cpp @@ -3725,8 +3725,12 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { llvm::SaveAndRestore ChangeNeedOptionalUnwrap(NeedOptionalUnwrap, true); if (DotLoc.isValid()) { + // Let's not erase the dot if the completion is after a swift key path + // root because \A?.?.member is the correct way to access wrapped type + // member from an optional key path root. + auto loc = IsAfterSwiftKeyPathRoot ? DotLoc.getAdvancedLoc(1) : DotLoc; NumBytesToEraseForOptionalUnwrap = Ctx.SourceMgr.getByteDistance( - DotLoc, Ctx.SourceMgr.getCodeCompletionLoc()); + loc, Ctx.SourceMgr.getCodeCompletionLoc()); } else { NumBytesToEraseForOptionalUnwrap = 0; } @@ -5930,7 +5934,7 @@ void CodeCompletionCallbacksImpl::doneParsing() { if (!OnRoot && KPE->getComponents().back().getKind() == KeyPathExpr::Component::Kind::OptionalWrap) { // KeyPath expr with '?' (e.g. '\Ty.[0].prop?.another'). - // Althogh expected type is optional, we should unwrap it because it's + // Although expected type is optional, we should unwrap it because it's // unwrapped. baseType = baseType->getOptionalObjectType(); } diff --git a/test/IDE/complete_swift_key_path_optional_root.swift b/test/IDE/complete_swift_key_path_optional_root.swift new file mode 100644 index 0000000000000..c80822c601c4f --- /dev/null +++ b/test/IDE/complete_swift_key_path_optional_root.swift @@ -0,0 +1,55 @@ +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TYPE_INFER_DOT_OPTIONAL | %FileCheck %s -check-prefix=PERSONTYPE-INFER-DOT-OPT +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TYPE_DOT_OPTIONAL | %FileCheck %s -check-prefix=PERSONTYPE-DOT-OPT +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TYPE_DOT_OPTIONAL_SPACE | %FileCheck %s -check-prefix=PERSONTYPE-DOT-OPT-SPACE + +class Person { + var name: String + var friends: [Person] = [] + var bestFriend: Person? = nil + var itself: Person { return self } + init(name: String) { + self.name = name + } + func getName() -> String { return name } + subscript(_ index: Int) -> Int { get { return 1} } +} + +extension Optional { + var optMember: String { "member" } +} + +let _ : KeyPath = \.#^TYPE_INFER_DOT_OPTIONAL^# +// PERSONTYPE-INFER-DOT-OPT: Begin completions, 9 items +// PERSONTYPE-INFER-DOT-OPT-NEXT: Decl[InstanceVar]/CurrNominal: ?.name[#String#]; name=name +// PERSONTYPE-INFER-DOT-OPT-NEXT: Decl[InstanceVar]/CurrNominal: ?.friends[#[Person]#]; name=friends +// PERSONTYPE-INFER-DOT-OPT-NEXT: Decl[InstanceVar]/CurrNominal: ?.bestFriend[#Person?#]; name=bestFriend +// PERSONTYPE-INFER-DOT-OPT-NEXT: Decl[InstanceVar]/CurrNominal: ?.itself[#Person#]; name=itself +// PERSONTYPE-INFER-DOT-OPT-NEXT: Decl[Subscript]/CurrNominal: ?[{#(index): Int#}][#Int#]; name=[index: Int] +// PERSONTYPE-INFER-DOT-OPT-NEXT: Decl[InstanceVar]/CurrNominal/IsSystem: unsafelyUnwrapped[#Person#]; name=unsafelyUnwrapped +// PERSONTYPE-INFER-DOT-OPT-NEXT: Decl[InstanceVar]/CurrNominal: optMember[#String#]; name=optMember +// PERSONTYPE-INFER-DOT-OPT-NEXT: Decl[InstanceVar]/CurrNominal/IsSystem: debugDescription[#String#]; name=debugDescription +// PERSONTYPE-INFER-DOT-OPT-NEXT: Decl[InstanceVar]/CurrNominal/IsSystem: customMirror[#Mirror#]; name=customMirror + +let _ : KeyPath = \Person?.#^TYPE_DOT_OPTIONAL^# +// PERSONTYPE-DOT-OPT: Begin completions, 9 items +// PERSONTYPE-DOT-OPT-NEXT: Decl[InstanceVar]/CurrNominal: ?.name[#String#]; name=name +// PERSONTYPE-DOT-OPT-NEXT: Decl[InstanceVar]/CurrNominal: ?.friends[#[Person]#]; name=friends +// PERSONTYPE-DOT-OPT-NEXT: Decl[InstanceVar]/CurrNominal: ?.bestFriend[#Person?#]; name=bestFriend +// PERSONTYPE-DOT-OPT-NEXT: Decl[InstanceVar]/CurrNominal: ?.itself[#Person#]; name=itself +// PERSONTYPE-DOT-OPT-NEXT: Decl[Subscript]/CurrNominal: ?[{#(index): Int#}][#Int#]; name=[index: Int] +// PERSONTYPE-DOT-OPT-NEXT: Decl[InstanceVar]/CurrNominal/IsSystem: unsafelyUnwrapped[#Person#]; name=unsafelyUnwrapped +// PERSONTYPE-DOT-OPT-NEXT: Decl[InstanceVar]/CurrNominal: optMember[#String#]; name=optMember +// PERSONTYPE-DOT-OPT-NEXT: Decl[InstanceVar]/CurrNominal/IsSystem: debugDescription[#String#]; name=debugDescription +// PERSONTYPE-DOT-OPT-NEXT: Decl[InstanceVar]/CurrNominal/IsSystem: customMirror[#Mirror#]; name=customMirror + +let _ : KeyPath = \Person?. #^TYPE_DOT_OPTIONAL_SPACE^# +// PERSONTYPE-DOT-OPT-SPACE: Begin completions, 9 items +// PERSONTYPE-DOT-OPT-SPACE-NEXT: Decl[InstanceVar]/CurrNominal/Erase[1]: ?.name[#String#]; name=name +// PERSONTYPE-DOT-OPT-SPACE-NEXT: Decl[InstanceVar]/CurrNominal/Erase[1]: ?.friends[#[Person]#]; name=friends +// PERSONTYPE-DOT-OPT-SPACE-NEXT: Decl[InstanceVar]/CurrNominal/Erase[1]: ?.bestFriend[#Person?#]; name=bestFriend +// PERSONTYPE-DOT-OPT-SPACE-NEXT: Decl[InstanceVar]/CurrNominal/Erase[1]: ?.itself[#Person#]; name=itself +// PERSONTYPE-DOT-OPT-SPACE-NEXT: Decl[Subscript]/CurrNominal/Erase[1]: ?[{#(index): Int#}][#Int#]; name=[index: Int] +// PERSONTYPE-DOT-OPT-SPACE-NEXT: Decl[InstanceVar]/CurrNominal/IsSystem: unsafelyUnwrapped[#Person#]; name=unsafelyUnwrapped +// PERSONTYPE-DOT-OPT-SPACE-NEXT: Decl[InstanceVar]/CurrNominal: optMember[#String#]; name=optMember +// PERSONTYPE-DOT-OPT-SPACE-NEXT: Decl[InstanceVar]/CurrNominal/IsSystem: debugDescription[#String#]; name=debugDescription +// PERSONTYPE-DOT-OPT-SPACE-NEXT: Decl[InstanceVar]/CurrNominal/IsSystem: customMirror[#Mirror#]; name=customMirror