Skip to content

Commit f5a216f

Browse files
authored
Merge pull request #23411 from benlangmuir/cc-1-close
[code-completion] Add type context for single-expression closures
2 parents 1b3c4ad + b512ab2 commit f5a216f

12 files changed

+331
-85
lines changed

include/swift/Parse/CodeCompletionCallbacks.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ class CodeCompletionCallbacks {
123123
virtual void completeDotExpr(Expr *E, SourceLoc DotLoc) {};
124124

125125
/// Complete the beginning of a statement or expression.
126-
virtual void completeStmtOrExpr() {};
126+
virtual void completeStmtOrExpr(CodeCompletionExpr *E) {};
127127

128128
/// Complete the beginning of expr-postfix -- no tokens provided
129129
/// by user.

lib/IDE/CodeCompletion.cpp

Lines changed: 70 additions & 56 deletions
Large diffs are not rendered by default.

lib/IDE/CodeCompletionResultBuilder.h

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,26 @@ class ModuleDecl;
3131

3232
namespace ide {
3333

34+
/// The expected contextual type(s) for code-completion.
35+
struct ExpectedTypeContext {
36+
/// Possible types of the code completion expression.
37+
llvm::SmallVector<Type, 4> possibleTypes;
38+
39+
/// Whether the `ExpectedTypes` comes from a single-expression body, e.g.
40+
/// `foo({ here })`.
41+
///
42+
/// Since the input may be incomplete, we take into account that the types are
43+
/// only a hint.
44+
bool isSingleExpressionBody = false;
45+
46+
bool empty() const { return possibleTypes.empty(); }
47+
48+
ExpectedTypeContext() = default;
49+
ExpectedTypeContext(ArrayRef<Type> types, bool isSingleExpressionBody)
50+
: possibleTypes(types.begin(), types.end()),
51+
isSingleExpressionBody(isSingleExpressionBody) {}
52+
};
53+
3454
class CodeCompletionResultBuilder {
3555
CodeCompletionResultSink &Sink;
3656
CodeCompletionResult::ResultKind Kind;
@@ -43,7 +63,7 @@ class CodeCompletionResultBuilder {
4363
SmallVector<CodeCompletionString::Chunk, 4> Chunks;
4464
llvm::PointerUnion<const ModuleDecl *, const clang::Module *>
4565
CurrentModule;
46-
ArrayRef<Type> ExpectedDeclTypes;
66+
ExpectedTypeContext declTypeContext;
4767
CodeCompletionResult::ExpectedTypeRelation ExpectedTypeRelation =
4868
CodeCompletionResult::Unrelated;
4969
bool Cancelled = false;
@@ -78,9 +98,9 @@ class CodeCompletionResultBuilder {
7898
CodeCompletionResultBuilder(CodeCompletionResultSink &Sink,
7999
CodeCompletionResult::ResultKind Kind,
80100
SemanticContextKind SemanticContext,
81-
ArrayRef<Type> ExpectedDeclTypes)
101+
const ExpectedTypeContext &declTypeContext)
82102
: Sink(Sink), Kind(Kind), SemanticContext(SemanticContext),
83-
ExpectedDeclTypes(ExpectedDeclTypes) {}
103+
declTypeContext(declTypeContext) {}
84104

85105
~CodeCompletionResultBuilder() {
86106
finishResult();

lib/IDE/ExprContextAnalysis.cpp

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -512,6 +512,7 @@ class ExprContextAnalyzer {
512512
SmallVectorImpl<Type> &PossibleTypes;
513513
SmallVectorImpl<StringRef> &PossibleNames;
514514
SmallVectorImpl<FunctionTypeAndDecl> &PossibleCallees;
515+
bool &singleExpressionBody;
515516

516517
void recordPossibleType(Type ty) {
517518
if (!ty || ty->is<ErrorType>())
@@ -618,6 +619,13 @@ class ExprContextAnalyzer {
618619
}
619620
break;
620621
}
622+
case ExprKind::Closure: {
623+
auto *CE = cast<ClosureExpr>(Parent);
624+
assert(isSingleExpressionBodyForCodeCompletion(CE->getBody()));
625+
singleExpressionBody = true;
626+
recordPossibleType(getReturnTypeFromContext(CE));
627+
break;
628+
}
621629
default:
622630
llvm_unreachable("Unhandled expression kind.");
623631
}
@@ -707,14 +715,20 @@ class ExprContextAnalyzer {
707715
}
708716
}
709717

718+
static bool isSingleExpressionBodyForCodeCompletion(BraceStmt *body) {
719+
return body->getNumElements() == 1 && body->getElements()[0].is<Expr *>();
720+
}
721+
710722
public:
711723
ExprContextAnalyzer(DeclContext *DC, Expr *ParsedExpr,
712724
SmallVectorImpl<Type> &PossibleTypes,
713725
SmallVectorImpl<StringRef> &PossibleNames,
714-
SmallVectorImpl<FunctionTypeAndDecl> &PossibleCallees)
726+
SmallVectorImpl<FunctionTypeAndDecl> &PossibleCallees,
727+
bool &singleExpressionBody)
715728
: DC(DC), ParsedExpr(ParsedExpr), SM(DC->getASTContext().SourceMgr),
716729
Context(DC->getASTContext()), PossibleTypes(PossibleTypes),
717-
PossibleNames(PossibleNames), PossibleCallees(PossibleCallees) {}
730+
PossibleNames(PossibleNames), PossibleCallees(PossibleCallees),
731+
singleExpressionBody(singleExpressionBody) {}
718732

719733
void Analyze() {
720734
// We cannot analyze without target.
@@ -737,6 +751,15 @@ class ExprContextAnalyzer {
737751
(!isa<CallExpr>(ParentE) && !isa<SubscriptExpr>(ParentE) &&
738752
!isa<BinaryExpr>(ParentE) && !isa<TupleShuffleExpr>(ParentE));
739753
}
754+
case ExprKind::Closure: {
755+
// Note: we cannot use hasSingleExpressionBody, because we explicitly
756+
// do not use the single-expression-body when there is code-completion
757+
// in the expression in order to avoid a base expression affecting
758+
// the type. However, now that we've typechecked, we will take the
759+
// context type into account.
760+
return isSingleExpressionBodyForCodeCompletion(
761+
cast<ClosureExpr>(E)->getBody());
762+
}
740763
default:
741764
return false;
742765
}
@@ -795,6 +818,6 @@ class ExprContextAnalyzer {
795818

796819
ExprContextInfo::ExprContextInfo(DeclContext *DC, Expr *TargetExpr) {
797820
ExprContextAnalyzer Analyzer(DC, TargetExpr, PossibleTypes, PossibleNames,
798-
PossibleCallees);
821+
PossibleCallees, singleExpressionBody);
799822
Analyzer.Analyze();
800823
}

lib/IDE/ExprContextAnalysis.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,21 @@ class ExprContextInfo {
5050
SmallVector<Type, 2> PossibleTypes;
5151
SmallVector<StringRef, 2> PossibleNames;
5252
SmallVector<FunctionTypeAndDecl, 2> PossibleCallees;
53+
bool singleExpressionBody = false;
5354

5455
public:
5556
ExprContextInfo(DeclContext *DC, Expr *TargetExpr);
5657

5758
// Returns a list of possible context types.
5859
ArrayRef<Type> getPossibleTypes() const { return PossibleTypes; }
5960

61+
/// Whether the type context comes from a single-expression body, e.g.
62+
/// `foo({ here })`.
63+
///
64+
/// If the input may be incomplete, such as in code-completion, take into
65+
/// account that the types returned by `getPossibleTypes()` are only a hint.
66+
bool isSingleExpressionBody() const { return singleExpressionBody; }
67+
6068
// Returns a list of possible argument label names.
6169
// Valid only if \c getKind() is \c CallArgument.
6270
ArrayRef<StringRef> getPossibleNames() const { return PossibleNames; }

lib/Parse/ParseStmt.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,8 +144,10 @@ ParserStatus Parser::parseExprOrStmt(ASTNode &Result) {
144144
CodeCompletion->setExprBeginning(getParserPosition());
145145

146146
if (Tok.is(tok::code_complete)) {
147+
auto *CCE = new (Context) CodeCompletionExpr(Tok.getLoc());
148+
Result = CCE;
147149
if (CodeCompletion)
148-
CodeCompletion->completeStmtOrExpr();
150+
CodeCompletion->completeStmtOrExpr(CCE);
149151
SyntaxParsingContext ErrorCtxt(SyntaxContext, SyntaxContextKind::Stmt);
150152
consumeToken(tok::code_complete);
151153
return makeParserCodeCompletionStatus();

lib/Sema/CSDiagnostics.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1976,7 +1976,8 @@ bool MissingArgumentsFailure::diagnoseAsError() {
19761976
auto path = locator->getPath();
19771977

19781978
// TODO: Currently this is only intended to diagnose contextual failures.
1979-
if (!(path.back().getKind() == ConstraintLocator::ApplyArgToParam ||
1979+
if (path.empty() ||
1980+
!(path.back().getKind() == ConstraintLocator::ApplyArgToParam ||
19801981
path.back().getKind() == ConstraintLocator::ContextualType))
19811982
return false;
19821983

test/IDE/complete_at_top_level.swift

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

test/IDE/complete_in_closures.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ struct FooStruct {
7171
// FOO_OBJECT_DOT: Begin completions
7272
// FOO_OBJECT_DOT-NEXT: Keyword[self]/CurrNominal: self[#FooStruct#]; name=self
7373
// FOO_OBJECT_DOT-NEXT: Decl[InstanceVar]/CurrNominal: instanceVar[#Int#]{{; name=.+$}}
74-
// FOO_OBJECT_DOT-NEXT: Decl[InstanceMethod]/CurrNominal: instanceFunc0()[#Void#]{{; name=.+$}}
74+
// FOO_OBJECT_DOT-NEXT: Decl[InstanceMethod]/CurrNominal{{(/TypeRelation\[Identical\])?}}: instanceFunc0()[#Void#]{{; name=.+$}}
7575
// FOO_OBJECT_DOT-NEXT: End completions
7676

7777
// WITH_GLOBAL_DECLS: Begin completions
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TestSingleExprClosureRet | %FileCheck %s -check-prefix=TestSingleExprClosureRet
2+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TestSingleExprClosureRetVoid | %FileCheck %s -check-prefix=TestSingleExprClosureRetVoid
3+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TestSingleExprClosureRetUnresolved | %FileCheck %s -check-prefix=TestSingleExprClosureRetUnresolved
4+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TestSingleExprClosure | %FileCheck %s -check-prefix=TestSingleExprClosure
5+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TestSingleExprClosureVoid | %FileCheck %s -check-prefix=TestSingleExprClosureRetVoid
6+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TestSingleExprClosureUnresolved | %FileCheck %s -check-prefix=TestSingleExprClosureRetUnresolved
7+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TestSingleExprClosureCall | %FileCheck %s -check-prefix=TestSingleExprClosure
8+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TestSingleExprClosureGlobal | %FileCheck %s -check-prefix=TestSingleExprClosureGlobal
9+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TestNonSingleExprClosureGlobal | %FileCheck %s -check-prefix=TestNonSingleExprClosureGlobal
10+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TestNonSingleExprClosureUnresolved | %FileCheck %s -check-prefix=TestNonSingleExprClosureUnresolved
11+
12+
struct TestSingleExprClosureRet {
13+
func void() -> Void {}
14+
func str() -> String { return "" }
15+
func int() -> Int { return 0 }
16+
17+
func test() -> Int {
18+
return { () in
19+
return self.#^TestSingleExprClosureRet^#
20+
}()
21+
}
22+
23+
// TestSingleExprClosureRet: Begin completions
24+
// TestSingleExprClosureRet-DAG: Decl[InstanceMethod]/CurrNominal: str()[#String#];
25+
// TestSingleExprClosureRet-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Identical]: int()[#Int#];
26+
// TestSingleExprClosureRet-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended/TypeRelation[Invalid]: void()[#Void#];
27+
// TestSingleExprClosureRet: End completions
28+
}
29+
30+
struct TestSingleExprClosureRetVoid {
31+
func void() -> Void {}
32+
func str() -> String { return "" }
33+
func int() -> Int { return 0 }
34+
35+
func test() {
36+
return { () in
37+
return self.#^TestSingleExprClosureRetVoid^#
38+
}()
39+
}
40+
41+
// TestSingleExprClosureRetVoid: Begin completions
42+
// TestSingleExprClosureRetVoid-DAG: Decl[InstanceMethod]/CurrNominal: str()[#String#];
43+
// TestSingleExprClosureRetVoid-DAG: Decl[InstanceMethod]/CurrNominal: int()[#Int#];
44+
// TestSingleExprClosureRetVoid-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Identical]: void()[#Void#];
45+
// TestSingleExprClosureRetVoid: End completions
46+
}
47+
48+
struct TestSingleExprClosureRetUnresolved {
49+
enum MyEnum { case myEnum }
50+
enum NotMine { case notMine }
51+
func mine() -> MyEnum { return .myEnum }
52+
func notMine() -> NotMine { return .notMine }
53+
54+
func test() -> MyEnum {
55+
return { () in
56+
return .#^TestSingleExprClosureRetUnresolved^#
57+
}()
58+
}
59+
60+
// TestSingleExprClosureRetUnresolved: Begin completions
61+
// TestSingleExprClosureRetUnresolved-NOT: notMine
62+
// TestSingleExprClosureRetUnresolved: Decl[EnumElement]/ExprSpecific: myEnum[#TestSingleExprClosure{{(Ret)?}}Unresolved.MyEnum#];
63+
// TestSingleExprClosureRetUnresolved-NOT: notMine
64+
// TestSingleExprClosureRetUnresolved: End completions
65+
}
66+
67+
struct TestSingleExprClosure {
68+
func void() -> Void {}
69+
func str() -> String { return "" }
70+
func int() -> Int { return 0 }
71+
72+
func test() -> Int {
73+
return { () in
74+
self.#^TestSingleExprClosure^#
75+
}()
76+
}
77+
// TestSingleExprClosure: Begin completions
78+
// TestSingleExprClosure-DAG: Decl[InstanceMethod]/CurrNominal: str()[#String#];
79+
// TestSingleExprClosure-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Identical]: int()[#Int#];
80+
// NOTE: this differs from the one using a return keyword.
81+
// TestSingleExprClosure-DAG: Decl[InstanceMethod]/CurrNominal: void()[#Void#];
82+
// TestSingleExprClosure: End completions
83+
}
84+
85+
struct TestSingleExprClosureVoid {
86+
func void() -> Void {}
87+
func str() -> String { return "" }
88+
func int() -> Int { return 0 }
89+
90+
func test() {
91+
return { () in
92+
self.#^TestSingleExprClosureVoid^#
93+
}()
94+
}
95+
}
96+
97+
struct TestSingleExprClosureUnresolved {
98+
enum MyEnum { case myEnum }
99+
enum NotMine { case notMine }
100+
func mine() -> MyEnum { return .myEnum }
101+
func notMine() -> NotMine { return .notMine }
102+
103+
func test() -> MyEnum {
104+
return { () in
105+
.#^TestSingleExprClosureUnresolved^#
106+
}()
107+
}
108+
}
109+
110+
struct TestSingleExprClosureCall {
111+
func void() -> Void {}
112+
func str() -> String { return "" }
113+
func int() -> Int { return 0 }
114+
115+
func take(_: () -> Int) {}
116+
117+
func test() {
118+
take {
119+
self.#^TestSingleExprClosureCall^#
120+
}
121+
}
122+
}
123+
124+
struct TestSingleExprClosureGlobal {
125+
func void() -> Void {}
126+
func str() -> String { return "" }
127+
func int() -> Int { return 0 }
128+
129+
func test() -> Int {
130+
return { () in
131+
#^TestSingleExprClosureGlobal^#
132+
}()
133+
}
134+
// TestSingleExprClosureGlobal: Begin completions
135+
// TestSingleExprClosureGlobal-DAG: Decl[InstanceMethod]/OutNominal: str()[#String#];
136+
// TestSingleExprClosureGlobal-DAG: Decl[InstanceMethod]/OutNominal/TypeRelation[Identical]: int()[#Int#];
137+
// TestSingleExprClosureGlobal-DAG: Decl[InstanceMethod]/OutNominal: void()[#Void#];
138+
// TestSingleExprClosureGlobal: End completions
139+
}
140+
141+
struct TestNonSingleExprClosureGlobal {
142+
func void() -> Void {}
143+
func str() -> String { return "" }
144+
func int() -> Int { return 0 }
145+
146+
func test() -> Int {
147+
return { () in
148+
#^TestNonSingleExprClosureGlobal^#
149+
return 42
150+
}()
151+
}
152+
// TestNonSingleExprClosureGlobal: Begin completions
153+
// TestNonSingleExprClosureGlobal-DAG: Decl[InstanceMethod]/OutNominal: str()[#String#];
154+
// TestNonSingleExprClosureGlobal-DAG: Decl[InstanceMethod]/OutNominal: int()[#Int#];
155+
// TestNonSingleExprClosureGlobal-DAG: Decl[InstanceMethod]/OutNominal: void()[#Void#];
156+
// TestNonSingleExprClosureGlobal: End completions
157+
}
158+
159+
struct TestNonSingleExprClosureUnresolved {
160+
enum MyEnum { case myEnum }
161+
enum NotMine { case notMine }
162+
func mine() -> MyEnum { return .myEnum }
163+
func notMine() -> NotMine { return .notMine }
164+
165+
func test() -> Int {
166+
return { () in
167+
.#^TestNonSingleExprClosureUnresolved^#
168+
return 42
169+
}()
170+
}
171+
// TestNonSingleExprClosureUnresolved-NOT: myEnum
172+
// TestNonSingleExprClosureUnresolved-NOT: notMine
173+
}

test/IDE/complete_unresolved_members.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=UNRESOLVED_20 | %FileCheck %s -check-prefix=UNRESOLVED_3
2929
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=UNRESOLVED_21 | %FileCheck %s -check-prefix=UNRESOLVED_1
3030
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=UNRESOLVED_22 | %FileCheck %s -check-prefix=UNRESOLVED_1
31+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=UNRESOLVED_22_noreturn | %FileCheck %s -check-prefix=UNRESOLVED_1
3132
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=UNRESOLVED_23 | %FileCheck %s -check-prefix=UNRESOLVED_1
3233
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=UNRESOLVED_24 | %FileCheck %s -check-prefix=UNRESOLVED_3
3334
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=UNRESOLVED_25 | %FileCheck %s -check-prefix=UNRESOLVED_3
@@ -303,6 +304,10 @@ var c1 = {() -> SomeOptions1 in
303304
return .#^UNRESOLVED_22^#
304305
}
305306

307+
var c1_noreturn = {() -> SomeOptions1 in
308+
.#^UNRESOLVED_22_noreturn^#
309+
}
310+
306311
class C6 {
307312
func f1() -> SomeOptions1 {
308313
return .#^UNRESOLVED_23^#

0 commit comments

Comments
 (0)