Skip to content

Commit 458ce70

Browse files
authored
Merge pull request #41633 from ahoppen/pr/solver-based-global-completions
[CodeCompletion] Migrate expression completions to solver-based
2 parents 5c2028d + e2a62f1 commit 458ce70

17 files changed

+308
-5
lines changed

include/swift/IDE/CompletionLookup.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,12 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
113113
/// Expected types of the code completion expression.
114114
ExpectedTypeContext expectedTypeContext;
115115

116+
/// Variables whose type was determined while type checking the code
117+
/// completion expression and that are thus not recorded in the AST.
118+
/// This in particular applies to params of closures that contain the code
119+
/// completion token.
120+
llvm::SmallDenseMap<const VarDecl *, Type> SolutionSpecificVarTypes;
121+
116122
bool CanCurrDeclContextHandleAsync = false;
117123
bool HaveDot = false;
118124
bool IsUnwrappedOptional = false;
@@ -206,6 +212,11 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
206212
const DeclContext *CurrDeclContext,
207213
CodeCompletionContext *CompletionContext = nullptr);
208214

215+
void setSolutionSpecificVarTypes(
216+
llvm::SmallDenseMap<const VarDecl *, Type> SolutionSpecificVarTypes) {
217+
this->SolutionSpecificVarTypes = SolutionSpecificVarTypes;
218+
}
219+
209220
void setHaveDot(SourceLoc DotLoc) {
210221
HaveDot = true;
211222
this->DotLoc = DotLoc;
@@ -226,6 +237,10 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
226237

227238
void setIdealExpectedType(Type Ty) { expectedTypeContext.setIdealType(Ty); }
228239

240+
void setCanCurrDeclContextHandleAsync(bool CanCurrDeclContextHandleAsync) {
241+
this->CanCurrDeclContextHandleAsync = CanCurrDeclContextHandleAsync;
242+
}
243+
229244
const ExpectedTypeContext *getExpectedTypeContext() const {
230245
return &expectedTypeContext;
231246
}

include/swift/IDE/ExprCompletion.h

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
//===--- ExprCompletion.h -------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2022 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#ifndef SWIFT_IDE_EXPRCOMPLETION_H
14+
#define SWIFT_IDE_EXPRCOMPLETION_H
15+
16+
#include "swift/IDE/CodeCompletionConsumer.h"
17+
#include "swift/IDE/CodeCompletionContext.h"
18+
#include "swift/Sema/CodeCompletionTypeChecking.h"
19+
20+
namespace swift {
21+
namespace ide {
22+
23+
class ExprTypeCheckCompletionCallback : public TypeCheckCompletionCallback {
24+
public:
25+
struct Result {
26+
/// The contextual type that the code completion expression should produce.
27+
Type ExpectedType;
28+
29+
/// If the code completion expression is an implicit return in a
30+
/// single-expression closure.
31+
bool IsImplicitSingleExpressionReturn;
32+
33+
/// Whether the surrounding context is async and thus calling async
34+
/// functions is supported.
35+
bool IsInAsyncContext;
36+
37+
/// Types of variables that were determined in the solution that produced
38+
/// this result. This in particular includes parameters of closures that
39+
/// were type-checked with the code completion expression.
40+
llvm::SmallDenseMap<const VarDecl *, Type> SolutionSpecificVarTypes;
41+
};
42+
43+
private:
44+
CodeCompletionExpr *CompletionExpr;
45+
DeclContext *DC;
46+
47+
SmallVector<Result, 4> Results;
48+
49+
public:
50+
/// \param DC The decl context in which the \p CompletionExpr occurs.
51+
ExprTypeCheckCompletionCallback(CodeCompletionExpr *CompletionExpr,
52+
DeclContext *DC)
53+
: CompletionExpr(CompletionExpr), DC(DC) {}
54+
55+
void sawSolution(const constraints::Solution &solution) override;
56+
57+
/// \param CCLoc The location of the code completion token.
58+
void deliverResults(SourceLoc CCLoc,
59+
ide::CodeCompletionContext &CompletionCtx,
60+
CodeCompletionConsumer &Consumer);
61+
};
62+
63+
} // end namespace ide
64+
} // end namespace swift
65+
66+
#endif // SWIFT_IDE_EXPRCOMPLETION_H

include/swift/Sema/ConstraintSystem.h

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -978,6 +978,7 @@ class SolutionApplicationTargetsKey {
978978
tombstone,
979979
stmtCondElement,
980980
expr,
981+
closure,
981982
stmt,
982983
pattern,
983984
patternBindingEntry,
@@ -1020,6 +1021,11 @@ class SolutionApplicationTargetsKey {
10201021
storage.expr = expr;
10211022
}
10221023

1024+
SolutionApplicationTargetsKey(const ClosureExpr *closure) {
1025+
kind = Kind::closure;
1026+
storage.expr = closure;
1027+
}
1028+
10231029
SolutionApplicationTargetsKey(const Stmt *stmt) {
10241030
kind = Kind::stmt;
10251031
storage.stmt = stmt;
@@ -1056,6 +1062,7 @@ class SolutionApplicationTargetsKey {
10561062
return lhs.storage.stmtCondElement == rhs.storage.stmtCondElement;
10571063

10581064
case Kind::expr:
1065+
case Kind::closure:
10591066
return lhs.storage.expr == rhs.storage.expr;
10601067

10611068
case Kind::stmt:
@@ -1096,6 +1103,7 @@ class SolutionApplicationTargetsKey {
10961103
DenseMapInfo<void *>::getHashValue(storage.stmtCondElement));
10971104

10981105
case Kind::expr:
1106+
case Kind::closure:
10991107
return hash_combine(
11001108
DenseMapInfo<unsigned>::getHashValue(static_cast<unsigned>(kind)),
11011109
DenseMapInfo<void *>::getHashValue(storage.expr));
@@ -1623,6 +1631,7 @@ class SolutionApplicationTarget {
16231631
public:
16241632
enum class Kind {
16251633
expression,
1634+
closure,
16261635
function,
16271636
stmtCondition,
16281637
caseLabelItem,
@@ -1684,6 +1693,14 @@ class SolutionApplicationTarget {
16841693
};
16851694
} expression;
16861695

1696+
struct {
1697+
/// The closure expression being type-checked.
1698+
ClosureExpr *closure;
1699+
1700+
/// The type to which the expression should be converted.
1701+
Type convertType;
1702+
} closure;
1703+
16871704
struct {
16881705
AnyFunctionRef function;
16891706
BraceStmt *body;
@@ -1735,6 +1752,12 @@ class SolutionApplicationTarget {
17351752
setPattern(pattern);
17361753
}
17371754

1755+
SolutionApplicationTarget(ClosureExpr *closure, Type convertType) {
1756+
kind = Kind::closure;
1757+
this->closure.closure = closure;
1758+
this->closure.convertType = convertType;
1759+
}
1760+
17381761
SolutionApplicationTarget(AnyFunctionRef fn)
17391762
: SolutionApplicationTarget(fn, fn.getBody()) { }
17401763

@@ -1831,6 +1854,7 @@ class SolutionApplicationTarget {
18311854
case Kind::expression:
18321855
return expression.expression;
18331856

1857+
case Kind::closure:
18341858
case Kind::function:
18351859
case Kind::stmtCondition:
18361860
case Kind::caseLabelItem:
@@ -1846,6 +1870,9 @@ class SolutionApplicationTarget {
18461870
case Kind::expression:
18471871
return expression.dc;
18481872

1873+
case Kind::closure:
1874+
return closure.closure;
1875+
18491876
case Kind::function:
18501877
return function.function.getAsDeclContext();
18511878

@@ -1934,6 +1961,11 @@ class SolutionApplicationTarget {
19341961
return cast<ExprPattern>(expression.pattern);
19351962
}
19361963

1964+
Type getClosureContextualType() const {
1965+
assert(kind == Kind::closure);
1966+
return closure.convertType;
1967+
}
1968+
19371969
/// For a pattern initialization target, retrieve the contextual pattern.
19381970
ContextualPattern getContextualPattern() const;
19391971

@@ -2062,6 +2094,7 @@ class SolutionApplicationTarget {
20622094
Optional<AnyFunctionRef> getAsFunction() const {
20632095
switch (kind) {
20642096
case Kind::expression:
2097+
case Kind::closure:
20652098
case Kind::stmtCondition:
20662099
case Kind::caseLabelItem:
20672100
case Kind::patternBinding:
@@ -2077,6 +2110,7 @@ class SolutionApplicationTarget {
20772110
Optional<StmtCondition> getAsStmtCondition() const {
20782111
switch (kind) {
20792112
case Kind::expression:
2113+
case Kind::closure:
20802114
case Kind::function:
20812115
case Kind::caseLabelItem:
20822116
case Kind::patternBinding:
@@ -2092,6 +2126,7 @@ class SolutionApplicationTarget {
20922126
Optional<CaseLabelItem *> getAsCaseLabelItem() const {
20932127
switch (kind) {
20942128
case Kind::expression:
2129+
case Kind::closure:
20952130
case Kind::function:
20962131
case Kind::stmtCondition:
20972132
case Kind::patternBinding:
@@ -2107,6 +2142,7 @@ class SolutionApplicationTarget {
21072142
PatternBindingDecl *getAsPatternBinding() const {
21082143
switch (kind) {
21092144
case Kind::expression:
2145+
case Kind::closure:
21102146
case Kind::function:
21112147
case Kind::stmtCondition:
21122148
case Kind::caseLabelItem:
@@ -2122,6 +2158,7 @@ class SolutionApplicationTarget {
21222158
VarDecl *getAsUninitializedWrappedVar() const {
21232159
switch (kind) {
21242160
case Kind::expression:
2161+
case Kind::closure:
21252162
case Kind::function:
21262163
case Kind::stmtCondition:
21272164
case Kind::caseLabelItem:
@@ -2137,6 +2174,7 @@ class SolutionApplicationTarget {
21372174
Pattern *getAsUninitializedVar() const {
21382175
switch (kind) {
21392176
case Kind::expression:
2177+
case Kind::closure:
21402178
case Kind::function:
21412179
case Kind::stmtCondition:
21422180
case Kind::caseLabelItem:
@@ -2152,6 +2190,7 @@ class SolutionApplicationTarget {
21522190
Type getTypeOfUninitializedVar() const {
21532191
switch (kind) {
21542192
case Kind::expression:
2193+
case Kind::closure:
21552194
case Kind::function:
21562195
case Kind::stmtCondition:
21572196
case Kind::caseLabelItem:
@@ -2167,6 +2206,7 @@ class SolutionApplicationTarget {
21672206
PatternBindingDecl *getPatternBindingOfUninitializedVar() const {
21682207
switch (kind) {
21692208
case Kind::expression:
2209+
case Kind::closure:
21702210
case Kind::function:
21712211
case Kind::stmtCondition:
21722212
case Kind::caseLabelItem:
@@ -2182,6 +2222,7 @@ class SolutionApplicationTarget {
21822222
unsigned getIndexOfUninitializedVar() const {
21832223
switch (kind) {
21842224
case Kind::expression:
2225+
case Kind::closure:
21852226
case Kind::function:
21862227
case Kind::stmtCondition:
21872228
case Kind::caseLabelItem:
@@ -2210,6 +2251,9 @@ class SolutionApplicationTarget {
22102251
case Kind::expression:
22112252
return expression.expression->getSourceRange();
22122253

2254+
case Kind::closure:
2255+
return closure.closure->getSourceRange();
2256+
22132257
case Kind::function:
22142258
return function.body->getSourceRange();
22152259

@@ -2240,6 +2284,9 @@ class SolutionApplicationTarget {
22402284
case Kind::expression:
22412285
return expression.expression->getLoc();
22422286

2287+
case Kind::closure:
2288+
return closure.closure->getLoc();
2289+
22432290
case Kind::function:
22442291
return function.function.getLoc();
22452292

lib/IDE/ArgumentCompletion.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,9 @@ void ArgumentTypeCheckCompletionCallback::sawSolution(const Solution &S) {
106106
}
107107

108108
if (!ParentCall || ParentCall == CompletionExpr) {
109-
assert(false && "no containing call?");
109+
// We might not have a call that contains the code completion expression if
110+
// we type-checked the fallback code completion expression that only
111+
// contains the code completion token, but not the surrounding call.
110112
return;
111113
}
112114

lib/IDE/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ add_swift_host_library(swiftIDE STATIC
2020
ConformingMethodList.cpp
2121
DependencyChecking.cpp
2222
DotExprCompletion.cpp
23+
ExprCompletion.cpp
2324
ExprContextAnalysis.cpp
2425
Formatting.cpp
2526
FuzzyStringMatcher.cpp

lib/IDE/CodeCompletion.cpp

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
#include "swift/IDE/CompletionLookup.h"
4141
#include "swift/IDE/CompletionOverrideLookup.h"
4242
#include "swift/IDE/DotExprCompletion.h"
43+
#include "swift/IDE/ExprCompletion.h"
4344
#include "swift/IDE/KeyPathCompletion.h"
4445
#include "swift/IDE/UnresolvedMemberCompletion.h"
4546
#include "swift/IDE/Utils.h"
@@ -1392,6 +1393,26 @@ bool CodeCompletionCallbacksImpl::trySolverCompletion(bool MaybeFuncBody) {
13921393
CurDeclContext, CompletionContext, Consumer);
13931394
return true;
13941395
}
1396+
case CompletionKind::StmtOrExpr: {
1397+
assert(CodeCompleteTokenExpr);
1398+
assert(CurDeclContext);
1399+
1400+
ExprTypeCheckCompletionCallback Lookup(CodeCompleteTokenExpr,
1401+
CurDeclContext);
1402+
llvm::SaveAndRestore<TypeCheckCompletionCallback *> CompletionCollector(
1403+
Context.CompletionCallback, &Lookup);
1404+
typeCheckContextAt(CurDeclContext, CompletionLoc);
1405+
1406+
if (!Lookup.gotCallback()) {
1407+
Lookup.fallbackTypeCheck(CurDeclContext);
1408+
}
1409+
1410+
addKeywords(CompletionContext.getResultSink(), MaybeFuncBody);
1411+
1412+
SourceLoc CCLoc = P.Context.SourceMgr.getCodeCompletionLoc();
1413+
Lookup.deliverResults(CCLoc, CompletionContext, Consumer);
1414+
return true;
1415+
}
13951416
default:
13961417
return false;
13971418
}
@@ -1514,10 +1535,10 @@ void CodeCompletionCallbacksImpl::doneParsing() {
15141535
case CompletionKind::UnresolvedMember:
15151536
case CompletionKind::KeyPathExprSwift:
15161537
case CompletionKind::CallArg:
1538+
case CompletionKind::StmtOrExpr:
15171539
llvm_unreachable("should be already handled");
15181540
return;
15191541

1520-
case CompletionKind::StmtOrExpr:
15211542
case CompletionKind::ForEachSequence:
15221543
case CompletionKind::PostfixExprBeginning: {
15231544
ExprContextInfo ContextInfo(CurDeclContext, CodeCompleteTokenExpr);

lib/IDE/CompletionLookup.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -804,8 +804,13 @@ void CompletionLookup::addVarDeclRef(const VarDecl *VD,
804804
assert(!Name.empty() && "name should not be empty");
805805

806806
Type VarType;
807-
if (VD->hasInterfaceType())
807+
auto SolutionSpecificType = SolutionSpecificVarTypes.find(VD);
808+
if (SolutionSpecificType != SolutionSpecificVarTypes.end()) {
809+
assert(!VarType && "Type recorded in the AST and is also solution-specific?");
810+
VarType = SolutionSpecificType->second;
811+
} else if (VD->hasInterfaceType()) {
808812
VarType = getTypeOfMember(VD, dynamicLookupInfo);
813+
}
809814

810815
Optional<ContextualNotRecommendedReason> NotRecommended;
811816
// "not recommended" in its own getter.

0 commit comments

Comments
 (0)