From 7e3a5dc50be0d4ba565dac397f584d0d6ef133a7 Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Fri, 22 Mar 2019 14:50:58 -0700 Subject: [PATCH 1/2] [CodeCompletion] Don't provide 'init(nilLiteral:)' et al in optional context For unresolved member completion, member decls in `Optional` referenceable by implicit member expression are useless in most cases. - `init(_: <#Wrapped#>)` - `init(nilLiteral: ())` - `.some(<#Wrapped#>)` - `.none` Instead, provide `nil` with erasing `.` instruction. rdar://problem/47806831 --- lib/IDE/CodeCompletion.cpp | 21 ++++++++++- test/IDE/complete_unresolved_members.swift | 41 +++++++++++++++++----- 2 files changed, 53 insertions(+), 9 deletions(-) diff --git a/lib/IDE/CodeCompletion.cpp b/lib/IDE/CodeCompletion.cpp index 313f244e573bd..9f592be92ebdf 100644 --- a/lib/IDE/CodeCompletion.cpp +++ b/lib/IDE/CodeCompletion.cpp @@ -2737,7 +2737,8 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { void addKeyword(StringRef Name, Type TypeAnnotation = Type(), SemanticContextKind SK = SemanticContextKind::None, CodeCompletionKeywordKind KeyKind - = CodeCompletionKeywordKind::None) { + = CodeCompletionKeywordKind::None, + unsigned NumBytesToErase = 0) { CodeCompletionResultBuilder Builder( Sink, CodeCompletionResult::ResultKind::Keyword, SK, expectedTypeContext); @@ -2746,6 +2747,8 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { Builder.setKeywordKind(KeyKind); if (TypeAnnotation) addTypeAnnotation(Builder, TypeAnnotation); + if (NumBytesToErase > 0) + Builder.setNumBytesToErase(NumBytesToErase); } void addKeyword(StringRef Name, StringRef TypeAnnotation, @@ -3634,6 +3637,14 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { return false; } + // In optional context, ignore '.init()', 'init(nilLiteral:)', + // '.some()' and '.none'. They are useless in most cases. + if (T->getOptionalObjectType() && + VD->getModuleContext()->isStdlibModule() && + (isa(VD) || isa(VD))) { + return false; + } + // Enum element decls can always be referenced by implicit member // expression. if (isa(VD)) @@ -3704,6 +3715,14 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { // If this is optional type, perform completion for the object type. // i.e. 'let _: Enum??? = .enumMember' is legal. getUnresolvedMemberCompletions(objT->lookThroughAllOptionalTypes()); + + // Add 'nil' keyword with erasing '.' instruction. + unsigned bytesToErase = 0; + auto &SM = CurrDeclContext->getASTContext().SourceMgr; + if (DotLoc.isValid()) + bytesToErase = SM.getByteDistance(DotLoc, SM.getCodeCompletionLoc()); + addKeyword("nil", T, SemanticContextKind::ExpressionSpecific, + CodeCompletionKeywordKind::kw_nil, bytesToErase); } getUnresolvedMemberCompletions(T); } diff --git a/test/IDE/complete_unresolved_members.swift b/test/IDE/complete_unresolved_members.swift index 1015870ad16e3..b0270099db202 100644 --- a/test/IDE/complete_unresolved_members.swift +++ b/test/IDE/complete_unresolved_members.swift @@ -14,6 +14,7 @@ // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=UNRESOLVED_OPT_1 | %FileCheck %s -check-prefix=UNRESOLVED_3_OPT // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=UNRESOLVED_OPT_2 | %FileCheck %s -check-prefix=UNRESOLVED_3_OPT // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=UNRESOLVED_OPT_3 | %FileCheck %s -check-prefix=UNRESOLVED_3_OPTOPTOPT +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=UNRESOLVED_OPT_4 | %FileCheck %s -check-prefix=UNRESOLVED_OPT_4 // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=UNRESOLVED_12 | %FileCheck %s -check-prefix=UNRESOLVED_3 // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=UNRESOLVED_13 | %FileCheck %s -check-prefix=UNRESOLVED_3 @@ -252,18 +253,42 @@ class C4 { // UNRESOLVED_3_OPT: Begin completions // UNRESOLVED_3_OPT-DAG: Decl[EnumElement]/ExprSpecific: North[#SomeEnum1#]; // UNRESOLVED_3_OPT-DAG: Decl[EnumElement]/ExprSpecific: South[#SomeEnum1#]; -// UNRESOLVED_3_OPT-DAG: Decl[EnumElement]/ExprSpecific: none[#Optional#]; name=none -// UNRESOLVED_3_OPT-DAG: Decl[EnumElement]/ExprSpecific: some({#SomeEnum1#})[#Optional#]; -// UNRESOLVED_3_OPT-DAG: Decl[Constructor]/CurrNominal: init({#(some): SomeEnum1#})[#Optional#]; -// UNRESOLVED_3_OPT-DAG: Decl[Constructor]/CurrNominal: init({#nilLiteral: ()#})[#Optional#]; +// UNRESOLVED_3_OPT-DAG: Keyword[nil]/ExprSpecific/Erase[1]: nil[#SomeEnum1?#]; name=nil +// UNRESOLVED_3_OPT-NOT: none +// UNRESOLVED_3_OPT-NOT: some +// UNRESOLVED_3_OPT-NOT: init({#(some): +// UNRESOLVED_3_OPT-NOT: init({#nilLiteral: // UNRESOLVED_3_OPTOPTOPT: Begin completions // UNRESOLVED_3_OPTOPTOPT-DAG: Decl[EnumElement]/ExprSpecific: North[#SomeEnum1#]; // UNRESOLVED_3_OPTOPTOPT-DAG: Decl[EnumElement]/ExprSpecific: South[#SomeEnum1#]; -// UNRESOLVED_3_OPTOPTOPT-DAG: Decl[EnumElement]/ExprSpecific: none[#Optional#]; name=none -// UNRESOLVED_3_OPTOPTOPT-DAG: Decl[EnumElement]/ExprSpecific: some({#SomeEnum1??#})[#Optional#]; -// UNRESOLVED_3_OPTOPTOPT-DAG: Decl[Constructor]/CurrNominal: init({#(some): SomeEnum1??#})[#Optional#]; -// UNRESOLVED_3_OPTOPTOPT-DAG: Decl[Constructor]/CurrNominal: init({#nilLiteral: ()#})[#Optional#]; +// UNRESOLVED_3_OPTOPTOPT-DAG: Keyword[nil]/ExprSpecific/Erase[1]: nil[#SomeEnum1???#]; name=nil +// UNRESOLVED_3_OPTOPTOPT-NOT: none +// UNRESOLVED_3_OPTOPTOPT-NOT: some +// UNRESOLVED_3_OPTOPTOPT-NOT: init({#(some): +// UNRESOLVED_3_OPTOPTOPT-NOT: init({#nilLiteral: + +enum Somewhere { + case earth, mars +} +extension Optional where Wrapped == Somewhere { + init(str: String) { fatalError() } + static var nowhere: Self { return nil } +} +func testOptionalWithCustomExtension() { + var _: Somewhere? = .#^UNRESOLVED_OPT_4^# +// UNRESOLVED_OPT_4: Begin completions +// UNRESOLVED_OPT_4-DAG: Decl[EnumElement]/ExprSpecific: earth[#Somewhere#]; +// UNRESOLVED_OPT_4-DAG: Decl[EnumElement]/ExprSpecific: mars[#Somewhere#]; +// UNRESOLVED_OPT_4-DAG: Keyword[nil]/ExprSpecific/Erase[1]: nil[#Somewhere?#]; name=nil +// UNRESOLVED_OPT_4-DAG: Decl[Constructor]/CurrNominal: init({#str: String#})[#Optional#]; name=init(str: String) +// UNRESOLVED_OPT_4-DAG: Decl[StaticVar]/CurrNominal/TypeRelation[Identical]: nowhere[#Optional#]; name=nowhere +// UNRESOLVED_OPT_4-NOT: none +// UNRESOLVED_OPT_4-NOT: some +// UNRESOLVED_OPT_4-NOT: init({#(some): +// UNRESOLVED_OPT_4-NOT: init({#nilLiteral: +} + class C5 { func f1() { From b1efc21e084e459b998f3293f0d7d7d4bed421b5 Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Mon, 25 Mar 2019 15:57:42 -0700 Subject: [PATCH 2/2] [CodeCompletion] Don't ignore .some(Wrapped) and .none for now with "TODO: ignore them in expression context". They are useful in pattern context, so we should provide them in completion. --- lib/IDE/CodeCompletion.cpp | 11 ++++++----- test/IDE/complete_unresolved_members.swift | 21 +++++++++++---------- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/lib/IDE/CodeCompletion.cpp b/lib/IDE/CodeCompletion.cpp index 9f592be92ebdf..fe3c3b1c4087b 100644 --- a/lib/IDE/CodeCompletion.cpp +++ b/lib/IDE/CodeCompletion.cpp @@ -3637,12 +3637,13 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { return false; } - // In optional context, ignore '.init()', 'init(nilLiteral:)', - // '.some()' and '.none'. They are useless in most cases. if (T->getOptionalObjectType() && - VD->getModuleContext()->isStdlibModule() && - (isa(VD) || isa(VD))) { - return false; + VD->getModuleContext()->isStdlibModule()) { + // In optional context, ignore '.init()', 'init(nilLiteral:)', + if (isa(VD)) + return false; + // TODO: Ignore '.some()' and '.none' too *in expression + // context*. They are useful in pattern context though. } // Enum element decls can always be referenced by implicit member diff --git a/test/IDE/complete_unresolved_members.swift b/test/IDE/complete_unresolved_members.swift index b0270099db202..affa2a179eb66 100644 --- a/test/IDE/complete_unresolved_members.swift +++ b/test/IDE/complete_unresolved_members.swift @@ -242,7 +242,7 @@ class C4 { var _: SomeEnum1??? = .#^UNRESOLVED_OPT_3^# } } -// UNRESOLVED_3: Begin completions +// UNRESOLVED_3: Begin completions, 2 items // UNRESOLVED_3-DAG: Decl[EnumElement]/ExprSpecific: North[#SomeEnum1#]; name=North // UNRESOLVED_3-DAG: Decl[EnumElement]/ExprSpecific: South[#SomeEnum1#]; name=South // UNRESOLVED_3-NOT: SomeOptions1 @@ -250,21 +250,21 @@ class C4 { // UNRESOLVED_3-NOT: none // UNRESOLVED_3-NOT: some( -// UNRESOLVED_3_OPT: Begin completions +// UNRESOLVED_3_OPT: Begin completions, 5 items // UNRESOLVED_3_OPT-DAG: Decl[EnumElement]/ExprSpecific: North[#SomeEnum1#]; // UNRESOLVED_3_OPT-DAG: Decl[EnumElement]/ExprSpecific: South[#SomeEnum1#]; // UNRESOLVED_3_OPT-DAG: Keyword[nil]/ExprSpecific/Erase[1]: nil[#SomeEnum1?#]; name=nil -// UNRESOLVED_3_OPT-NOT: none -// UNRESOLVED_3_OPT-NOT: some +// UNRESOLVED_3_OPT-DAG: Decl[EnumElement]/ExprSpecific: none[#Optional#]; name=none +// UNRESOLVED_3_OPT-DAG: Decl[EnumElement]/ExprSpecific: some({#SomeEnum1#})[#Optional#]; // UNRESOLVED_3_OPT-NOT: init({#(some): // UNRESOLVED_3_OPT-NOT: init({#nilLiteral: -// UNRESOLVED_3_OPTOPTOPT: Begin completions +// UNRESOLVED_3_OPTOPTOPT: Begin completions, 5 items // UNRESOLVED_3_OPTOPTOPT-DAG: Decl[EnumElement]/ExprSpecific: North[#SomeEnum1#]; // UNRESOLVED_3_OPTOPTOPT-DAG: Decl[EnumElement]/ExprSpecific: South[#SomeEnum1#]; // UNRESOLVED_3_OPTOPTOPT-DAG: Keyword[nil]/ExprSpecific/Erase[1]: nil[#SomeEnum1???#]; name=nil -// UNRESOLVED_3_OPTOPTOPT-NOT: none -// UNRESOLVED_3_OPTOPTOPT-NOT: some +// UNRESOLVED_3_OPTOPTOPT-DAG: Decl[EnumElement]/ExprSpecific: none[#Optional#]; name=none +// UNRESOLVED_3_OPTOPTOPT-DAG: Decl[EnumElement]/ExprSpecific: some({#SomeEnum1??#})[#Optional#]; // UNRESOLVED_3_OPTOPTOPT-NOT: init({#(some): // UNRESOLVED_3_OPTOPTOPT-NOT: init({#nilLiteral: @@ -277,16 +277,17 @@ extension Optional where Wrapped == Somewhere { } func testOptionalWithCustomExtension() { var _: Somewhere? = .#^UNRESOLVED_OPT_4^# -// UNRESOLVED_OPT_4: Begin completions +// UNRESOLVED_OPT_4: Begin completions, 7 items // UNRESOLVED_OPT_4-DAG: Decl[EnumElement]/ExprSpecific: earth[#Somewhere#]; // UNRESOLVED_OPT_4-DAG: Decl[EnumElement]/ExprSpecific: mars[#Somewhere#]; // UNRESOLVED_OPT_4-DAG: Keyword[nil]/ExprSpecific/Erase[1]: nil[#Somewhere?#]; name=nil +// UNRESOLVED_OPT_4-DAG: Decl[EnumElement]/ExprSpecific: none[#Optional#]; name=none +// UNRESOLVED_OPT_4-DAG: Decl[EnumElement]/ExprSpecific: some({#Somewhere#})[#Optional#]; // UNRESOLVED_OPT_4-DAG: Decl[Constructor]/CurrNominal: init({#str: String#})[#Optional#]; name=init(str: String) // UNRESOLVED_OPT_4-DAG: Decl[StaticVar]/CurrNominal/TypeRelation[Identical]: nowhere[#Optional#]; name=nowhere -// UNRESOLVED_OPT_4-NOT: none -// UNRESOLVED_OPT_4-NOT: some // UNRESOLVED_OPT_4-NOT: init({#(some): // UNRESOLVED_OPT_4-NOT: init({#nilLiteral: +// UNRESOLVED_OPT_4: End completions }