Skip to content

Commit 81ce653

Browse files
authored
Merge pull request #23587 from benlangmuir/cc-omit-return
[code-completion] Add type context for single-expression function bodies
2 parents 4fa916b + a54cb9d commit 81ce653

14 files changed

+601
-63
lines changed

include/swift/IDE/CodeCompletion.h

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -806,7 +806,21 @@ class CodeCompletionContext {
806806
public:
807807
CodeCompletionCache &Cache;
808808
CompletionKind CodeCompletionKind = CompletionKind::None;
809-
bool HasExpectedTypeRelation = false;
809+
810+
enum class TypeContextKind {
811+
/// There is no known contextual type. All types are equally good.
812+
None,
813+
814+
/// There is a contextual type from a single-expression closure/function
815+
/// body. The context is a hint, and enables unresolved member completion,
816+
/// but should not hide any results.
817+
SingleExpressionBody,
818+
819+
/// There are known contextual types.
820+
Required,
821+
};
822+
823+
TypeContextKind typeContextKind = TypeContextKind::None;
810824

811825
/// Whether there may be members that can use implicit member syntax,
812826
/// e.g. `x = .foo`.

include/swift/Parse/CodeCompletionCallbacks.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ class CodeCompletionCallbacks {
183183
SmallVectorImpl<StringRef> &Keywords, SourceLoc introducerLoc) {};
184184

185185
/// Complete at the beginning of accessor in a accessor block.
186-
virtual void completeAccessorBeginning() {};
186+
virtual void completeAccessorBeginning(CodeCompletionExpr *E) {};
187187

188188
/// Complete the keyword in attribute, for instance, @available.
189189
virtual void completeDeclAttrKeyword(Decl *D, bool Sil, bool Param) {};

lib/IDE/CodeCompletion.cpp

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -930,6 +930,18 @@ calculateMaxTypeRelationForDecl(
930930
bool IsImplicitlyCurriedInstanceMethod = false) {
931931
auto Result = CodeCompletionResult::ExpectedTypeRelation::Unrelated;
932932
for (auto Type : typeContext.possibleTypes) {
933+
// Do not use Void type context for a single-expression body, since the
934+
// implicit return does not constrain the expression.
935+
//
936+
// { ... -> () in x } // x can be anything
937+
//
938+
// This behaves differently from explicit return, and from non-Void:
939+
//
940+
// { ... -> Int in x } // x must be Int
941+
// { ... -> () in return x } // x must be Void
942+
if (typeContext.isSingleExpressionBody && Type->isVoid())
943+
continue;
944+
933945
Result = std::max(Result, calculateTypeRelationForDecl(
934946
D, Type, IsImplicitlyCurriedInstanceMethod));
935947

@@ -1359,7 +1371,7 @@ class CodeCompletionCallbacksImpl : public CodeCompletionCallbacks {
13591371
void completeInPrecedenceGroup(SyntaxKind SK) override;
13601372
void completeNominalMemberBeginning(
13611373
SmallVectorImpl<StringRef> &Keywords, SourceLoc introducerLoc) override;
1362-
void completeAccessorBeginning() override;
1374+
void completeAccessorBeginning(CodeCompletionExpr *E) override;
13631375

13641376
void completePoundAvailablePlatform() override;
13651377
void completeImportDecl(std::vector<std::pair<Identifier, SourceLoc>> &Path) override;
@@ -1650,7 +1662,15 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
16501662
expectedTypeContext.possibleTypes.push_back(T);
16511663
}
16521664

1653-
bool hasExpectedTypes() const { return !expectedTypeContext.empty(); }
1665+
CodeCompletionContext::TypeContextKind typeContextKind() const {
1666+
if (expectedTypeContext.empty()) {
1667+
return CodeCompletionContext::TypeContextKind::None;
1668+
} else if (expectedTypeContext.isSingleExpressionBody) {
1669+
return CodeCompletionContext::TypeContextKind::SingleExpressionBody;
1670+
} else {
1671+
return CodeCompletionContext::TypeContextKind::Required;
1672+
}
1673+
}
16541674

16551675
bool needDot() const {
16561676
return NeedLeadingDot;
@@ -3559,9 +3579,7 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
35593579
builder.addSimpleNamedParameter("values");
35603580
builder.addRightParen();
35613581
for (auto T : expectedTypeContext.possibleTypes) {
3562-
if (!T)
3563-
continue;
3564-
if (T->is<TupleType>()) {
3582+
if (T && T->is<TupleType>() && !T->isVoid()) {
35653583
addTypeAnnotation(builder, T);
35663584
builder.setExpectedTypeRelation(CodeCompletionResult::Identical);
35673585
break;
@@ -4705,9 +4723,11 @@ void CodeCompletionCallbacksImpl::completeNominalMemberBeginning(
47054723
CurDeclContext = P.CurDeclContext;
47064724
}
47074725

4708-
void CodeCompletionCallbacksImpl::completeAccessorBeginning() {
4726+
void CodeCompletionCallbacksImpl::completeAccessorBeginning(
4727+
CodeCompletionExpr *E) {
47094728
Kind = CompletionKind::AccessorBeginning;
47104729
CurDeclContext = P.CurDeclContext;
4730+
CodeCompleteTokenExpr = E;
47114731
}
47124732

47134733
static bool isDynamicLookup(Type T) {
@@ -5301,8 +5321,12 @@ void CodeCompletionCallbacksImpl::doneParsing() {
53015321
}
53025322

53035323
case CompletionKind::AccessorBeginning: {
5304-
if (isa<AccessorDecl>(ParsedDecl))
5324+
if (isa<AccessorDecl>(ParsedDecl)) {
5325+
ExprContextInfo ContextInfo(CurDeclContext, CodeCompleteTokenExpr);
5326+
Lookup.setExpectedTypes(ContextInfo.getPossibleTypes(),
5327+
ContextInfo.isSingleExpressionBody());
53055328
DoPostfixExprBeginning();
5329+
}
53065330
break;
53075331
}
53085332

@@ -5491,7 +5515,7 @@ void CodeCompletionCallbacksImpl::doneParsing() {
54915515
Lookup.RequestedCachedResults.clear();
54925516
}
54935517

5494-
CompletionContext.HasExpectedTypeRelation = Lookup.hasExpectedTypes();
5518+
CompletionContext.typeContextKind = Lookup.typeContextKind();
54955519

54965520
deliverCompletionResults();
54975521
}

lib/IDE/ExprContextAnalysis.cpp

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -633,6 +633,12 @@ class ExprContextAnalyzer {
633633
break;
634634
}
635635
default:
636+
if (auto *AFD = dyn_cast<AbstractFunctionDecl>(D)) {
637+
assert(isSingleExpressionBodyForCodeCompletion(AFD->getBody()));
638+
singleExpressionBody = true;
639+
recordPossibleType(getReturnTypeFromContext(AFD));
640+
break;
641+
}
636642
llvm_unreachable("Unhandled decl kind.");
637643
}
638644
}
@@ -653,6 +659,14 @@ class ExprContextAnalyzer {
653659
}
654660
}
655661

662+
/// Whether the given \c BraceStmt, which must be the body of a function or
663+
/// closure, should be treated as a single-expression return for the purposes
664+
/// of code-completion.
665+
///
666+
/// We cannot use hasSingleExpressionBody, because we explicitly do not use
667+
/// the single-expression-body when there is code-completion in the expression
668+
/// in order to avoid a base expression affecting the type. However, now that
669+
/// we've typechecked, we will take the context type into account.
656670
static bool isSingleExpressionBodyForCodeCompletion(BraceStmt *body) {
657671
return body->getNumElements() == 1 && body->getElements()[0].is<Expr *>();
658672
}
@@ -698,15 +712,9 @@ class ExprContextAnalyzer {
698712
(!isa<CallExpr>(ParentE) && !isa<SubscriptExpr>(ParentE) &&
699713
!isa<BinaryExpr>(ParentE));
700714
}
701-
case ExprKind::Closure: {
702-
// Note: we cannot use hasSingleExpressionBody, because we explicitly
703-
// do not use the single-expression-body when there is code-completion
704-
// in the expression in order to avoid a base expression affecting
705-
// the type. However, now that we've typechecked, we will take the
706-
// context type into account.
715+
case ExprKind::Closure:
707716
return isSingleExpressionBodyForCodeCompletion(
708717
cast<ClosureExpr>(E)->getBody());
709-
}
710718
default:
711719
return false;
712720
}
@@ -727,6 +735,9 @@ class ExprContextAnalyzer {
727735
case DeclKind::PatternBinding:
728736
return true;
729737
default:
738+
if (auto *AFD = dyn_cast<AbstractFunctionDecl>(D))
739+
if (auto *body = AFD->getBody())
740+
return isSingleExpressionBodyForCodeCompletion(body);
730741
return false;
731742
}
732743
} else if (auto P = Node.getAsPattern()) {

lib/Parse/ParseDecl.cpp

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4515,20 +4515,30 @@ ParserStatus Parser::parseGetSet(ParseDeclOptions Flags,
45154515

45164516
if (Tok.is(tok::code_complete)) {
45174517
if (CodeCompletion) {
4518+
CodeCompletionExpr *CCE = nullptr;
45184519
if (IsFirstAccessor && !parsingLimitedSyntax) {
45194520
// If CC token is the first token after '{', it might be implicit
45204521
// getter. Set up dummy accessor as the decl context to populate
45214522
// 'self' decl.
4523+
4524+
// FIXME: if there is already code inside the body, we should fall
4525+
// through to parseImplicitGetter and handle the completion there so
4526+
// that we can differentiate a single-expression body from the first
4527+
// expression in a multi-statement body.
45224528
auto getter = createAccessorFunc(
45234529
accessors.LBLoc, /*ValueNamePattern*/ nullptr, GenericParams,
45244530
Indices, ElementTy, StaticLoc, Flags, AccessorKind::Get,
45254531
storage, this, /*AccessorKeywordLoc*/ SourceLoc());
4532+
CCE = new (Context) CodeCompletionExpr(Tok.getLoc());
4533+
getter->setBody(BraceStmt::create(Context, Tok.getLoc(),
4534+
ASTNode(CCE), Tok.getLoc(),
4535+
/*implicit*/ true));
45264536
accessors.add(getter);
45274537
CodeCompletion->setParsedDecl(getter);
45284538
} else {
45294539
CodeCompletion->setParsedDecl(storage);
45304540
}
4531-
CodeCompletion->completeAccessorBeginning();
4541+
CodeCompletion->completeAccessorBeginning(CCE);
45324542
}
45334543
consumeToken(tok::code_complete);
45344544
accessorHasCodeCompletion = true;

test/IDE/complete_accessor.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@
135135
// NO_OBSERVER-NOT: willSet
136136
// NO_OBSERVER-NOT: didSet
137137

138-
// WITH_GLOBAL: Decl[GlobalVar]/CurrModule: globalValue[#String#];
138+
// WITH_GLOBAL: Decl[GlobalVar]/CurrModule{{(/TypeRelation\[Identical\])?}}: globalValue[#String#];
139139
// NO_GLOBAL-NOT: globalValue;
140140

141141
// WITH_SELF: Decl[LocalVar]/Local: self[#{{.+}}#]; name=self

test/IDE/complete_at_top_level.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,7 @@ _ = {
299299
}()
300300
// TOP_LEVEL_CLOSURE_1: Begin completions
301301
// TOP_LEVEL_CLOSURE_1-DAG: Decl[Struct]/CurrModule: FooStruct[#FooStruct#]{{; name=.+$}}
302-
// TOP_LEVEL_CLOSURE_1-DAG: Decl[FreeFunction]/CurrModule/TypeRelation[Identical]: fooFunc1()[#Void#]{{; name=.+$}}
302+
// TOP_LEVEL_CLOSURE_1-DAG: Decl[FreeFunction]/CurrModule: fooFunc1()[#Void#]{{; name=.+$}}
303303
// TOP_LEVEL_CLOSURE_1-DAG: Decl[GlobalVar]/Local: fooObject[#FooStruct#]{{; name=.+$}}
304304
// TOP_LEVEL_CLOSURE_1: End completions
305305

test/IDE/complete_crashes.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ while true {
6767
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=GENERIC_PARAM_AND_ASSOC_TYPE | %FileCheck %s -check-prefix=GENERIC_PARAM_AND_ASSOC_TYPE
6868
struct CustomGenericCollection<Key> : ExpressibleByDictionaryLiteral {
6969
// GENERIC_PARAM_AND_ASSOC_TYPE: Begin completions
70-
// GENERIC_PARAM_AND_ASSOC_TYPE-DAG: Decl[InstanceVar]/CurrNominal: count[#Int#]; name=count
70+
// GENERIC_PARAM_AND_ASSOC_TYPE-DAG: Decl[InstanceVar]/CurrNominal/TypeRelation[Identical]: count[#Int#]; name=count
7171
// GENERIC_PARAM_AND_ASSOC_TYPE-DAG: Decl[GenericTypeParam]/Local: Key[#Key#]; name=Key
7272
// GENERIC_PARAM_AND_ASSOC_TYPE-DAG: Decl[TypeAlias]/CurrNominal: Value[#CustomGenericCollection<Key>.Value#]; name=Value
7373
// GENERIC_PARAM_AND_ASSOC_TYPE: End completions

test/IDE/complete_in_accessors.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ func returnsInt() -> Int {}
132132

133133
// WITH_GLOBAL_DECLS: Begin completions
134134
// WITH_GLOBAL_DECLS-DAG: Decl[Struct]/CurrModule: FooStruct[#FooStruct#]{{; name=.+$}}
135-
// WITH_GLOBAL_DECLS-DAG: Decl[FreeFunction]/CurrModule: returnsInt()[#Int#]{{; name=.+$}}
135+
// WITH_GLOBAL_DECLS-DAG: Decl[FreeFunction]/CurrModule{{(/TypeRelation\[Identical\])?}}: returnsInt()[#Int#]{{; name=.+$}}
136136
// WITH_GLOBAL_DECLS: End completions
137137

138138
// WITH_GLOBAL_DECLS1: Begin completions
@@ -142,7 +142,7 @@ func returnsInt() -> Int {}
142142

143143
// WITH_MEMBER_DECLS: Begin completions
144144
// WITH_MEMBER_DECLS-DAG: Decl[Struct]/CurrModule: FooStruct[#FooStruct#]{{; name=.+$}}
145-
// WITH_MEMBER_DECLS-DAG: Decl[FreeFunction]/CurrModule: returnsInt()[#Int#]{{; name=.+$}}
145+
// WITH_MEMBER_DECLS-DAG: Decl[FreeFunction]/CurrModule{{(/TypeRelation\[Identical\])?}}: returnsInt()[#Int#]{{; name=.+$}}
146146
// WITH_MEMBER_DECLS-DAG: Decl[LocalVar]/Local: self[#MemberAccessors#]{{; name=.+$}}
147147
// WITH_MEMBER_DECLS-DAG: Decl[InstanceVar]/CurrNominal: instanceVar[#Double#]{{; name=.+$}}
148148
// WITH_MEMBER_DECLS-DAG: Decl[InstanceMethod]/CurrNominal: instanceFunc({#(a): Int#})[#Float#]{{; name=.+$}}
@@ -159,8 +159,8 @@ func returnsInt() -> Int {}
159159

160160
// WITH_LOCAL_DECLS: Begin completions
161161
// WITH_LOCAL_DECLS-DAG: Decl[Struct]/CurrModule: FooStruct[#FooStruct#]{{; name=.+$}}
162-
// WITH_LOCAL_DECLS-DAG: Decl[FreeFunction]/CurrModule: returnsInt()[#Int#]{{; name=.+$}}
163-
// WITH_LOCAL_DECLS-DAG: Decl[LocalVar]/Local: functionParam[#Int#]{{; name=.+$}}
162+
// WITH_LOCAL_DECLS-DAG: Decl[FreeFunction]/CurrModule{{(/TypeRelation\[Identical\])?}}: returnsInt()[#Int#]{{; name=.+$}}
163+
// WITH_LOCAL_DECLS-DAG: Decl[LocalVar]/Local{{(/TypeRelation\[Identical\])?}}: functionParam[#Int#]{{; name=.+$}}
164164
// WITH_LOCAL_DECLS-DAG: Decl[FreeFunction]/Local: localFunc({#(a): Int#})[#Float#]{{; name=.+$}}
165165
// WITH_LOCAL_DECLS: End completions
166166

@@ -456,7 +456,7 @@ func accessorsInFunction(_ functionParam: Int) {
456456

457457
// ACCESSORS_IN_MEMBER_FUNC_2: Begin completions
458458
// ACCESSORS_IN_MEMBER_FUNC_2-DAG: Decl[LocalVar]/Local: self[#AccessorsInMemberFunction#]
459-
// ACCESSORS_IN_MEMBER_FUNC_2-DAG: Decl[LocalVar]/Local: functionParam[#Int#]
459+
// ACCESSORS_IN_MEMBER_FUNC_2-DAG: Decl[LocalVar]/Local{{(/TypeRelation\[Identical\])?}}: functionParam[#Int#]
460460
// ACCESSORS_IN_MEMBER_FUNC_2-DAG: Decl[InstanceVar]/OutNominal: instanceVar[#Double#]
461461
// ACCESSORS_IN_MEMBER_FUNC_2-DAG: Decl[InstanceMethod]/OutNominal: instanceFunc({#(a): Int#})[#Float#]
462462
// ACCESSORS_IN_MEMBER_FUNC_2: End completions

0 commit comments

Comments
 (0)