Skip to content

Commit 28fccd1

Browse files
committed
[CodeCompletion] Migrate postfix expr completion to solver-based
1 parent 3c805ee commit 28fccd1

34 files changed

+1297
-890
lines changed

include/swift/IDE/CompletionLookup.h

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -492,7 +492,14 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
492492

493493
void getPostfixKeywordCompletions(Type ExprType, Expr *ParsedExpr);
494494

495-
void getValueExprCompletions(Type ExprType, ValueDecl *VD = nullptr);
495+
/// Add code completion results after an expression of type \p ExprType.
496+
/// This includes members as well as call patterns if \p ExprType is a
497+
/// function type.
498+
/// If \p IsDeclUnapplied is \c true, we are completing after a refernce to
499+
/// \p VD that hasn't been called yet. Thus, \p VD has type \p ExprType and we
500+
/// can use \p VD to enrich call pattern completions of \p ExprType.
501+
void getValueExprCompletions(Type ExprType, ValueDecl *VD = nullptr,
502+
bool IsDeclUnapplied = false);
496503

497504
void collectOperators(SmallVectorImpl<OperatorDecl *> &results);
498505

@@ -502,7 +509,7 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
502509

503510
void tryPostfixOperator(Expr *expr, PostfixOperatorDecl *op);
504511

505-
void addAssignmentOperator(Type RHSType, Type resultType);
512+
void addAssignmentOperator(Type RHSType);
506513

507514
void addInfixOperatorCompletion(OperatorDecl *op, Type resultType,
508515
Type RHSType);

include/swift/IDE/DotExprCompletion.h

Lines changed: 51 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,23 +25,60 @@ namespace ide {
2525
/// type-checking.
2626
class DotExprTypeCheckCompletionCallback : public TypeCheckCompletionCallback {
2727
struct Result {
28+
/// The type that we are completing on. Will never be null.
2829
Type BaseTy;
30+
31+
/// The we were able to determine it, the decl that we are completing on.
32+
/// Is \c nullptr if we are completing on an expression.
2933
ValueDecl *BaseDecl;
34+
35+
/// Whether \c BaseDecl refers to a function that has not been called yet.
36+
/// In such cases, we know that \p BaseTy is the type of \p BaseDecl and we
37+
/// can use \p BaseDecl for more detailed call pattern completions.
38+
bool IsBaseDeclUnapplied;
39+
40+
/// If the expression we are completing on statically refers to a metatype,
41+
/// that is if it's something like 'MyType'. In such cases we want to offer
42+
/// constructor call pattern completions and don't want to suggested
43+
/// operators that work on metatypes.
44+
bool BaseIsStaticMetaType;
45+
46+
/// The types that the completion is expected to produce.
3047
SmallVector<Type, 4> ExpectedTypes;
48+
49+
/// Whether results that produce 'Void' should be disfavored. This happens
50+
/// if the context is requiring a value. Once a completion produces 'Void',
51+
/// we know that we can't retrieve a value from it anymore.
3152
bool ExpectsNonVoid;
32-
bool BaseIsStaticMetaType;
53+
54+
/// If the code completion expression occurrs as a single statement in a
55+
/// single-expression closure. In such cases we don't want to disfavor
56+
/// results that produce 'Void' because the user might intend to make the
57+
/// closure a multi-statment closure, in which case this expression is no
58+
/// longer implicitly returned.
3359
bool IsImplicitSingleExpressionReturn;
3460

3561
/// Whether the surrounding context is async and thus calling async
3662
/// functions is supported.
3763
bool IsInAsyncContext;
64+
65+
/// Checks whether this result has the same \c BaseTy and \c BaseDecl as
66+
/// \p Other and if the two can thus be merged to be one value lookup in
67+
/// \c deliverResults.
68+
bool canBeMergedWith(const Result &Other) const;
69+
70+
/// Merge this result with \p Other. Assumes that they can be merged.
71+
void merge(const Result &Other);
3872
};
3973

4074
CodeCompletionExpr *CompletionExpr;
4175
DeclContext *DC;
4276

4377
SmallVector<Result, 4> Results;
44-
llvm::DenseMap<std::pair<Type, Decl *>, size_t> BaseToSolutionIdx;
78+
79+
/// Add a result to \c Results, merging it with an existing result, if
80+
/// possible.
81+
void addResult(const Result &Res);
4582

4683
void sawSolutionImpl(const constraints::Solution &solution) override;
4784

@@ -54,8 +91,18 @@ class DotExprTypeCheckCompletionCallback : public TypeCheckCompletionCallback {
5491
/// \c sawSolution for each solution formed.
5592
void fallbackTypeCheck(DeclContext *DC) override;
5693

57-
void deliverResults(Expr *BaseExpr, DeclContext *DC, SourceLoc DotLoc,
58-
bool IsInSelector, CodeCompletionContext &CompletionCtx,
94+
/// Deliver code completion results that were discoverd by \c sawSolution to
95+
/// \p Consumer.
96+
/// \param DotLoc If we are completing after a dot, the location of the dot,
97+
/// otherwise an invalid SourceLoc.
98+
/// \param IsInSelector Whether we are completing in an Objective-C selector.
99+
/// \param IncludeOperators If operators should be suggested. Assumes that
100+
/// \p DotLoc is invalid
101+
/// \param HasLeadingSpace Whether there is a space separating the exiting
102+
/// expression and the code completion token.
103+
void deliverResults(SourceLoc DotLoc, bool IsInSelector,
104+
bool IncludeOperators, bool HasLeadingSpace,
105+
CodeCompletionContext &CompletionCtx,
59106
CodeCompletionConsumer &Consumer);
60107
};
61108

include/swift/Parse/CodeCompletionCallbacks.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,8 +132,9 @@ class CodeCompletionCallbacks {
132132
/// -- no tokens provided by user.
133133
virtual void completeForEachSequenceBeginning(CodeCompletionExpr *E) {};
134134

135-
/// Complete a given expr-postfix.
136-
virtual void completePostfixExpr(Expr *E, bool hasSpace) {};
135+
/// Complete a expr-postfix. The \c CodeCompletionExpr has the expression it
136+
/// is completing after set as its base.
137+
virtual void completePostfixExpr(CodeCompletionExpr *E, bool hasSpace){};
137138

138139
/// Complete a given expr-postfix, given that there is a following
139140
/// left parenthesis.

include/swift/Sema/IDETypeChecking.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,10 @@
1919
#ifndef SWIFT_SEMA_IDETYPECHECKING_H
2020
#define SWIFT_SEMA_IDETYPECHECKING_H
2121

22+
#include "swift/AST/Expr.h"
2223
#include "swift/AST/Identifier.h"
23-
#include "swift/Basic/SourceLoc.h"
2424
#include "swift/Basic/OptionSet.h"
25+
#include "swift/Basic/SourceLoc.h"
2526
#include <memory>
2627
#include <tuple>
2728

@@ -163,6 +164,11 @@ namespace swift {
163164
constraints::SolutionApplicationTarget &target, bool needsPrecheck,
164165
llvm::function_ref<void(const constraints::Solution &)> callback);
165166

167+
/// Thunk around \c TypeChecker::resolveDeclRefExpr to make it
168+
/// available to \c swift::ide.
169+
Expr *resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE, DeclContext *Context,
170+
bool replaceInvalidRefsWithErrors);
171+
166172
LookupResult
167173
lookupSemanticMember(DeclContext *DC, Type ty, DeclName name);
168174

lib/IDE/CodeCompletion.cpp

Lines changed: 15 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,7 @@ class CodeCompletionCallbacksImpl : public CodeCompletionCallbacks {
231231
void completeStmtOrExpr(CodeCompletionExpr *E) override;
232232
void completePostfixExprBeginning(CodeCompletionExpr *E) override;
233233
void completeForEachSequenceBeginning(CodeCompletionExpr *E) override;
234-
void completePostfixExpr(Expr *E, bool hasSpace) override;
234+
void completePostfixExpr(CodeCompletionExpr *E, bool hasSpace) override;
235235
void completePostfixExprParen(Expr *E, Expr *CodeCompletionE) override;
236236
void completeExprKeyPath(KeyPathExpr *KPE, SourceLoc DotLoc) override;
237237

@@ -354,7 +354,8 @@ void CodeCompletionCallbacksImpl::completeForEachSequenceBeginning(
354354
CodeCompleteTokenExpr = E;
355355
}
356356

357-
void CodeCompletionCallbacksImpl::completePostfixExpr(Expr *E, bool hasSpace) {
357+
void CodeCompletionCallbacksImpl::completePostfixExpr(CodeCompletionExpr *E,
358+
bool hasSpace) {
358359
assert(P.Tok.is(tok::code_complete));
359360

360361
// Don't produce any results in an enum element.
@@ -368,7 +369,8 @@ void CodeCompletionCallbacksImpl::completePostfixExpr(Expr *E, bool hasSpace) {
368369
CompleteExprSelectorContext = ParseExprSelectorContext;
369370
}
370371

371-
ParsedExpr = E;
372+
ParsedExpr = E->getBase();
373+
CodeCompleteTokenExpr = E;
372374
CurDeclContext = P.CurDeclContext;
373375
}
374376

@@ -1331,6 +1333,7 @@ bool CodeCompletionCallbacksImpl::trySolverCompletion(bool MaybeFuncBody) {
13311333
: CurDeclContext->getASTContext().SourceMgr.getCodeCompletionLoc();
13321334

13331335
switch (Kind) {
1336+
case CompletionKind::PostfixExpr:
13341337
case CompletionKind::DotExpr: {
13351338
assert(CodeCompleteTokenExpr);
13361339
assert(CurDeclContext);
@@ -1351,9 +1354,10 @@ bool CodeCompletionCallbacksImpl::trySolverCompletion(bool MaybeFuncBody) {
13511354

13521355
addKeywords(CompletionContext.getResultSink(), MaybeFuncBody);
13531356

1354-
Expr *CheckedBase = CodeCompleteTokenExpr->getBase();
1355-
Lookup.deliverResults(CheckedBase, CurDeclContext, DotLoc,
1356-
isInsideObjCSelector(), CompletionContext, Consumer);
1357+
bool IncludeOperators = (Kind == CompletionKind::PostfixExpr);
1358+
1359+
Lookup.deliverResults(DotLoc, isInsideObjCSelector(), IncludeOperators,
1360+
HasSpace, CompletionContext, Consumer);
13571361
return true;
13581362
}
13591363
case CompletionKind::UnresolvedMember: {
@@ -1587,26 +1591,10 @@ void CodeCompletionCallbacksImpl::doneParsing() {
15871591
case CompletionKind::AfterPoundExpr:
15881592
case CompletionKind::AccessorBeginning:
15891593
case CompletionKind::CaseStmtBeginning:
1594+
case CompletionKind::PostfixExpr:
15901595
llvm_unreachable("should be already handled");
15911596
return;
15921597

1593-
case CompletionKind::PostfixExpr: {
1594-
Lookup.setHaveLeadingSpace(HasSpace);
1595-
if (isDynamicLookup(*ExprType))
1596-
Lookup.setIsDynamicLookup();
1597-
Lookup.getValueExprCompletions(*ExprType, ReferencedDecl.getDecl());
1598-
/// We set the type of ParsedExpr explicitly above. But we don't want an
1599-
/// unresolved type in our AST when we type check again for operator
1600-
/// completions. Remove the type of the ParsedExpr and see if we can come up
1601-
/// with something more useful based on the the full sequence expression.
1602-
if (ParsedExpr->getType()->is<UnresolvedType>()) {
1603-
ParsedExpr->setType(nullptr);
1604-
}
1605-
Lookup.getOperatorCompletions(ParsedExpr, leadingSequenceExprs);
1606-
Lookup.getPostfixKeywordCompletions(*ExprType, ParsedExpr);
1607-
break;
1608-
}
1609-
16101598
case CompletionKind::PostfixExprParen: {
16111599
Lookup.setHaveLParen(true);
16121600

@@ -1656,7 +1644,8 @@ void CodeCompletionCallbacksImpl::doneParsing() {
16561644
if (isDynamicLookup(*ExprType))
16571645
Lookup.setIsDynamicLookup();
16581646

1659-
Lookup.getValueExprCompletions(*ExprType, ReferencedDecl.getDecl());
1647+
Lookup.getValueExprCompletions(*ExprType, ReferencedDecl.getDecl(),
1648+
/*IncludeFunctionCallCompletions=*/true);
16601649
} else {
16611650
SourceLoc Loc = P.Context.SourceMgr.getCodeCompletionLoc();
16621651
Lookup.getValueCompletionsInDeclContext(Loc, KeyPathFilter,
@@ -1822,7 +1811,8 @@ void CodeCompletionCallbacksImpl::doneParsing() {
18221811

18231812
if (isDynamicLookup(resultTy))
18241813
Lookup.setIsDynamicLookup();
1825-
Lookup.getValueExprCompletions(resultTy, /*VD=*/nullptr);
1814+
Lookup.getValueExprCompletions(resultTy, /*VD=*/nullptr,
1815+
/*IncludeFunctionCallCompletions=*/true);
18261816
Lookup.getOperatorCompletions(analyzedExpr, leadingSequenceExprs);
18271817
Lookup.getPostfixKeywordCompletions(resultTy, analyzedExpr);
18281818
}

lib/IDE/CompletionLookup.cpp

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2134,6 +2134,8 @@ void CompletionLookup::getPostfixKeywordCompletions(Type ExprType,
21342134
if (IsSuperRefExpr)
21352135
return;
21362136

2137+
NeedLeadingDot = !HaveDot;
2138+
21372139
if (!ExprType->getAs<ModuleType>()) {
21382140
addKeyword(getTokenText(tok::kw_self), ExprType->getRValueType(),
21392141
SemanticContextKind::CurrentNominal,
@@ -2156,7 +2158,8 @@ void CompletionLookup::getPostfixKeywordCompletions(Type ExprType,
21562158
}
21572159
}
21582160

2159-
void CompletionLookup::getValueExprCompletions(Type ExprType, ValueDecl *VD) {
2161+
void CompletionLookup::getValueExprCompletions(Type ExprType, ValueDecl *VD,
2162+
bool IsDeclUnapplied) {
21602163
Kind = LookupKind::ValueExpr;
21612164
NeedLeadingDot = !HaveDot;
21622165

@@ -2189,7 +2192,7 @@ void CompletionLookup::getValueExprCompletions(Type ExprType, ValueDecl *VD) {
21892192

21902193
// Handle special cases
21912194
bool isIUO = VD && VD->isImplicitlyUnwrappedOptional();
2192-
if (tryFunctionCallCompletions(ExprType, VD))
2195+
if (tryFunctionCallCompletions(ExprType, IsDeclUnapplied ? VD : nullptr))
21932196
return;
21942197
if (tryModuleCompletions(ExprType))
21952198
return;
@@ -2252,7 +2255,7 @@ void CompletionLookup::tryPostfixOperator(Expr *expr, PostfixOperatorDecl *op) {
22522255
addPostfixOperatorCompletion(op, funcTy->getResult());
22532256
}
22542257

2255-
void CompletionLookup::addAssignmentOperator(Type RHSType, Type resultType) {
2258+
void CompletionLookup::addAssignmentOperator(Type RHSType) {
22562259
CodeCompletionResultBuilder builder(Sink,
22572260
CodeCompletionResultKind::BuiltinOperator,
22582261
SemanticContextKind::None);
@@ -2263,12 +2266,11 @@ void CompletionLookup::addAssignmentOperator(Type RHSType, Type resultType) {
22632266
builder.addWhitespace(" ");
22642267
builder.addEqual();
22652268
builder.addWhitespace(" ");
2266-
assert(RHSType && resultType);
2269+
assert(RHSType);
22672270
Type contextTy;
22682271
if (auto typeContext = CurrDeclContext->getInnermostTypeContext())
22692272
contextTy = typeContext->getDeclaredTypeInContext();
22702273
builder.addCallArgument(Identifier(), RHSType, contextTy);
2271-
addTypeAnnotation(builder, resultType);
22722274
}
22732275

22742276
void CompletionLookup::addInfixOperatorCompletion(OperatorDecl *op,
@@ -2387,8 +2389,7 @@ void CompletionLookup::getOperatorCompletions(
23872389

23882390
if (leadingSequence.empty() && LHS->getType() &&
23892391
LHS->getType()->hasLValueType()) {
2390-
addAssignmentOperator(LHS->getType()->getRValueType(),
2391-
CurrDeclContext->getASTContext().TheEmptyTupleType);
2392+
addAssignmentOperator(LHS->getType()->getRValueType());
23922393
}
23932394

23942395
// FIXME: unify this with the ?.member completions.

lib/IDE/ConformingMethodList.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ class ConformingMethodListCallbacks : public CodeCompletionCallbacks {
4646
// Only handle callbacks for suffix completions.
4747
// {
4848
void completeDotExpr(CodeCompletionExpr *E, SourceLoc DotLoc) override;
49-
void completePostfixExpr(Expr *E, bool hasSpace) override;
49+
void completePostfixExpr(CodeCompletionExpr *E, bool hasSpace) override;
5050
// }
5151

5252
void doneParsing() override;
@@ -58,10 +58,10 @@ void ConformingMethodListCallbacks::completeDotExpr(CodeCompletionExpr *E,
5858
ParsedExpr = E->getBase();
5959
}
6060

61-
void ConformingMethodListCallbacks::completePostfixExpr(Expr *E,
61+
void ConformingMethodListCallbacks::completePostfixExpr(CodeCompletionExpr *E,
6262
bool hasSpace) {
6363
CurDeclContext = P.CurDeclContext;
64-
ParsedExpr = E;
64+
ParsedExpr = E->getBase();
6565
}
6666

6767
void ConformingMethodListCallbacks::doneParsing() {

0 commit comments

Comments
 (0)