Skip to content

Commit 1eb05b7

Browse files
author
Nathan Hawes
authored
Merge pull request #34733 from nathawes/migrate-unresolved-completion
[CodeCompletion][Sema][Parse] Migrate unresolved member completion to the solver-based completion implementation
2 parents 638d5e7 + 240a262 commit 1eb05b7

24 files changed

+528
-316
lines changed

include/swift/Sema/CodeCompletionTypeChecking.h

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@
1818
#ifndef SWIFT_SEMA_CODECOMPLETIONTYPECHECKING_H
1919
#define SWIFT_SEMA_CODECOMPLETIONTYPECHECKING_H
2020

21+
#include "swift/Basic/LLVM.h"
22+
#include "swift/AST/Type.h"
23+
#include "llvm/ADT/DenseMap.h"
24+
#include "llvm/ADT/SmallVector.h"
25+
2126
namespace swift {
2227
class Decl;
2328
class DeclContext;
@@ -32,7 +37,7 @@ namespace swift {
3237
class TypeCheckCompletionCallback {
3338
public:
3439
/// Called for each solution produced while type-checking an expression
35-
/// containing a code completion expression.
40+
/// that the code completion expression participates in.
3641
virtual void sawSolution(const constraints::Solution &solution) = 0;
3742
virtual ~TypeCheckCompletionCallback() {}
3843
};
@@ -49,7 +54,7 @@ namespace swift {
4954
SmallVector<Type, 4> ExpectedTypes;
5055
bool ExpectsNonVoid;
5156
bool BaseIsStaticMetaType;
52-
bool IsSingleExpressionBody;
57+
bool IsImplicitSingleExpressionReturn;
5358
};
5459

5560
private:
@@ -78,6 +83,39 @@ namespace swift {
7883

7984
void sawSolution(const constraints::Solution &solution) override;
8085
};
86+
87+
/// Used to collect and store information needed to perform unresolved member
88+
/// completion (\c CompletionKind::UnresolvedMember ) from the solutions
89+
/// formed during expression type-checking.
90+
class UnresolvedMemberTypeCheckCompletionCallback: public TypeCheckCompletionCallback {
91+
public:
92+
struct Result {
93+
Type ExpectedTy;
94+
bool IsImplicitSingleExpressionReturn;
95+
};
96+
97+
private:
98+
CodeCompletionExpr *CompletionExpr;
99+
SmallVector<Result, 4> Results;
100+
bool GotCallback = false;
101+
102+
public:
103+
UnresolvedMemberTypeCheckCompletionCallback(CodeCompletionExpr *CompletionExpr)
104+
: CompletionExpr(CompletionExpr) {}
105+
106+
ArrayRef<Result> getResults() const { return Results; }
107+
108+
/// True if at least one solution was passed via the \c sawSolution
109+
/// callback.
110+
bool gotCallback() const { return GotCallback; }
111+
112+
/// Typecheck the code completion expression in its outermost expression
113+
/// context, calling \c sawSolution for each solution formed.
114+
void fallbackTypeCheck(DeclContext *DC);
115+
116+
void sawSolution(const constraints::Solution &solution) override;
117+
};
118+
81119
}
82120

83121
#endif

include/swift/Sema/ConstraintSystem.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2845,6 +2845,10 @@ class ConstraintSystem {
28452845
return TypeVariables.count(typeVar) > 0;
28462846
}
28472847

2848+
/// Whether the given expression's source range contains the code
2849+
/// completion location.
2850+
bool containsCodeCompletionLoc(Expr *expr) const;
2851+
28482852
void setClosureType(const ClosureExpr *closure, FunctionType *type) {
28492853
assert(closure);
28502854
assert(type && "Expected non-null type");

lib/IDE/CodeCompletion.cpp

Lines changed: 86 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1171,14 +1171,14 @@ calculateMaxTypeRelation(Type Ty, const ExpectedTypeContext &typeContext,
11711171
//
11721172
// { ... -> Int in x } // x must be Int
11731173
// { ... -> () in return x } // x must be Void
1174-
if (typeContext.isSingleExpressionBody && expectedTy->isVoid())
1174+
if (typeContext.isImplicitSingleExpressionReturn && expectedTy->isVoid())
11751175
continue;
11761176

11771177
Result = std::max(Result, calculateTypeRelation(Ty, expectedTy, DC));
11781178

11791179
// Map invalid -> unrelated when in a single-expression body, since the
11801180
// input may be incomplete.
1181-
if (typeContext.isSingleExpressionBody &&
1181+
if (typeContext.isImplicitSingleExpressionReturn &&
11821182
Result == CodeCompletionResult::ExpectedTypeRelation::Invalid)
11831183
Result = CodeCompletionResult::ExpectedTypeRelation::Unrelated;
11841184
}
@@ -1958,9 +1958,11 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
19581958
IsStaticMetatype = value;
19591959
}
19601960

1961-
void setExpectedTypes(ArrayRef<Type> Types, bool isSingleExpressionBody,
1961+
void setExpectedTypes(ArrayRef<Type> Types,
1962+
bool isImplicitSingleExpressionReturn,
19621963
bool preferNonVoid = false) {
1963-
expectedTypeContext.isSingleExpressionBody = isSingleExpressionBody;
1964+
expectedTypeContext.isImplicitSingleExpressionReturn =
1965+
isImplicitSingleExpressionReturn;
19641966
expectedTypeContext.preferNonVoid = preferNonVoid;
19651967
expectedTypeContext.possibleTypes.clear();
19661968
expectedTypeContext.possibleTypes.reserve(Types.size());
@@ -1976,7 +1978,7 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
19761978
CodeCompletionContext::TypeContextKind typeContextKind() const {
19771979
if (expectedTypeContext.empty() && !expectedTypeContext.preferNonVoid) {
19781980
return CodeCompletionContext::TypeContextKind::None;
1979-
} else if (expectedTypeContext.isSingleExpressionBody) {
1981+
} else if (expectedTypeContext.isImplicitSingleExpressionReturn) {
19801982
return CodeCompletionContext::TypeContextKind::SingleExpressionBody;
19811983
} else {
19821984
return CodeCompletionContext::TypeContextKind::Required;
@@ -4340,6 +4342,16 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
43404342
if (!T->mayHaveMembers())
43414343
return;
43424344

4345+
if (auto objT = T->getOptionalObjectType()) {
4346+
// Add 'nil' keyword with erasing '.' instruction.
4347+
unsigned bytesToErase = 0;
4348+
auto &SM = CurrDeclContext->getASTContext().SourceMgr;
4349+
if (DotLoc.isValid())
4350+
bytesToErase = SM.getByteDistance(DotLoc, SM.getCodeCompletionLoc());
4351+
addKeyword("nil", T, SemanticContextKind::None,
4352+
CodeCompletionKeywordKind::kw_nil, bytesToErase);
4353+
}
4354+
43434355
// We can only say .foo where foo is a static member of the contextual
43444356
// type and has the same type (or if the member is a function, then the
43454357
// same result type) as the contextual type.
@@ -4378,14 +4390,6 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
43784390
objT = objT->lookThroughAllOptionalTypes();
43794391
if (seenTypes.insert(objT->getCanonicalType()).second)
43804392
getUnresolvedMemberCompletions(objT);
4381-
4382-
// Add 'nil' keyword with erasing '.' instruction.
4383-
unsigned bytesToErase = 0;
4384-
auto &SM = CurrDeclContext->getASTContext().SourceMgr;
4385-
if (DotLoc.isValid())
4386-
bytesToErase = SM.getByteDistance(DotLoc, SM.getCodeCompletionLoc());
4387-
addKeyword("nil", T, SemanticContextKind::None,
4388-
CodeCompletionKeywordKind::kw_nil, bytesToErase);
43894393
}
43904394
getUnresolvedMemberCompletions(T);
43914395
}
@@ -6083,6 +6087,45 @@ static void deliverCompletionResults(CodeCompletionContext &CompletionContext,
60836087
DCForModules);
60846088
}
60856089

6090+
void deliverUnresolvedMemberResults(
6091+
ArrayRef<UnresolvedMemberTypeCheckCompletionCallback::Result> Results,
6092+
DeclContext *DC, SourceLoc DotLoc,
6093+
ide::CodeCompletionContext &CompletionCtx,
6094+
CodeCompletionConsumer &Consumer) {
6095+
ASTContext &Ctx = DC->getASTContext();
6096+
CompletionLookup Lookup(CompletionCtx.getResultSink(), Ctx, DC,
6097+
&CompletionCtx);
6098+
6099+
assert(DotLoc.isValid());
6100+
Lookup.setHaveDot(DotLoc);
6101+
Lookup.shouldCheckForDuplicates(Results.size() > 1);
6102+
6103+
// Get the canonical versions of the top-level types
6104+
SmallPtrSet<CanType, 4> originalTypes;
6105+
for (auto &Result: Results)
6106+
originalTypes.insert(Result.ExpectedTy->getCanonicalType());
6107+
6108+
for (auto &Result: Results) {
6109+
Lookup.setExpectedTypes({Result.ExpectedTy},
6110+
Result.IsImplicitSingleExpressionReturn,
6111+
/*expectsNonVoid*/true);
6112+
Lookup.setIdealExpectedType(Result.ExpectedTy);
6113+
6114+
// For optional types, also get members of the unwrapped type if it's not
6115+
// already equivalent to one of the top-level types. Handling it via the top
6116+
// level type and not here ensures we give the correct type relation
6117+
// (identical, rather than convertible).
6118+
if (Result.ExpectedTy->getOptionalObjectType()) {
6119+
Type Unwrapped = Result.ExpectedTy->lookThroughAllOptionalTypes();
6120+
if (originalTypes.insert(Unwrapped->getCanonicalType()).second)
6121+
Lookup.getUnresolvedMemberCompletions(Unwrapped);
6122+
}
6123+
Lookup.getUnresolvedMemberCompletions(Result.ExpectedTy);
6124+
}
6125+
SourceFile *SF = DC->getParentSourceFile();
6126+
deliverCompletionResults(CompletionCtx, Lookup, *SF, Consumer);
6127+
}
6128+
60866129
void deliverDotExprResults(
60876130
ArrayRef<DotExprTypeCheckCompletionCallback::Result> Results,
60886131
Expr *BaseExpr, DeclContext *DC, SourceLoc DotLoc, bool IsInSelector,
@@ -6113,7 +6156,7 @@ void deliverDotExprResults(
61136156
Lookup.setIsStaticMetatype(Result.BaseIsStaticMetaType);
61146157
Lookup.getPostfixKeywordCompletions(Result.BaseTy, BaseExpr);
61156158
Lookup.setExpectedTypes(Result.ExpectedTypes,
6116-
Result.IsSingleExpressionBody,
6159+
Result.IsImplicitSingleExpressionReturn,
61176160
Result.ExpectsNonVoid);
61186161
if (isDynamicLookup(Result.BaseTy))
61196162
Lookup.setIsDynamicLookup();
@@ -6158,6 +6201,23 @@ bool CodeCompletionCallbacksImpl::trySolverCompletion(bool MaybeFuncBody) {
61586201
Consumer);
61596202
return true;
61606203
}
6204+
case CompletionKind::UnresolvedMember: {
6205+
assert(CodeCompleteTokenExpr);
6206+
assert(CurDeclContext);
6207+
6208+
UnresolvedMemberTypeCheckCompletionCallback Lookup(CodeCompleteTokenExpr);
6209+
llvm::SaveAndRestore<TypeCheckCompletionCallback*>
6210+
CompletionCollector(Context.CompletionCallback, &Lookup);
6211+
typeCheckContextAt(CurDeclContext, CompletionLoc);
6212+
6213+
if (!Lookup.gotCallback())
6214+
Lookup.fallbackTypeCheck(CurDeclContext);
6215+
6216+
addKeywords(CompletionContext.getResultSink(), MaybeFuncBody);
6217+
deliverUnresolvedMemberResults(Lookup.getResults(), CurDeclContext, DotLoc,
6218+
CompletionContext, Consumer);
6219+
return true;
6220+
}
61616221
default:
61626222
return false;
61636223
}
@@ -6277,6 +6337,7 @@ void CodeCompletionCallbacksImpl::doneParsing() {
62776337
switch (Kind) {
62786338
case CompletionKind::None:
62796339
case CompletionKind::DotExpr:
6340+
case CompletionKind::UnresolvedMember:
62806341
llvm_unreachable("should be already handled");
62816342
return;
62826343

@@ -6336,7 +6397,7 @@ void CodeCompletionCallbacksImpl::doneParsing() {
63366397
case CompletionKind::PostfixExprBeginning: {
63376398
ExprContextInfo ContextInfo(CurDeclContext, CodeCompleteTokenExpr);
63386399
Lookup.setExpectedTypes(ContextInfo.getPossibleTypes(),
6339-
ContextInfo.isSingleExpressionBody());
6400+
ContextInfo.isImplicitSingleExpressionReturn());
63406401
DoPostfixExprBeginning();
63416402
break;
63426403
}
@@ -6358,8 +6419,9 @@ void CodeCompletionCallbacksImpl::doneParsing() {
63586419

63596420
if (ShouldCompleteCallPatternAfterParen) {
63606421
ExprContextInfo ParentContextInfo(CurDeclContext, ParsedExpr);
6361-
Lookup.setExpectedTypes(ParentContextInfo.getPossibleTypes(),
6362-
ParentContextInfo.isSingleExpressionBody());
6422+
Lookup.setExpectedTypes(
6423+
ParentContextInfo.getPossibleTypes(),
6424+
ParentContextInfo.isImplicitSingleExpressionReturn());
63636425
if (!ContextInfo.getPossibleCallees().empty()) {
63646426
for (auto &typeAndDecl : ContextInfo.getPossibleCallees())
63656427
Lookup.tryFunctionCallCompletions(typeAndDecl.Type, typeAndDecl.Decl,
@@ -6377,7 +6439,7 @@ void CodeCompletionCallbacksImpl::doneParsing() {
63776439
(Lookup.FoundFunctionCalls &&
63786440
Lookup.FoundFunctionsWithoutFirstKeyword)) {
63796441
Lookup.setExpectedTypes(ContextInfo.getPossibleTypes(),
6380-
ContextInfo.isSingleExpressionBody());
6442+
ContextInfo.isImplicitSingleExpressionReturn());
63816443
Lookup.setHaveLParen(false);
63826444
DoPostfixExprBeginning();
63836445
}
@@ -6426,7 +6488,7 @@ void CodeCompletionCallbacksImpl::doneParsing() {
64266488
case CompletionKind::CaseStmtBeginning: {
64276489
ExprContextInfo ContextInfo(CurDeclContext, CodeCompleteTokenExpr);
64286490
Lookup.setExpectedTypes(ContextInfo.getPossibleTypes(),
6429-
ContextInfo.isSingleExpressionBody());
6491+
ContextInfo.isImplicitSingleExpressionReturn());
64306492
Lookup.setIdealExpectedType(CodeCompleteTokenExpr->getType());
64316493
Lookup.getUnresolvedMemberCompletions(ContextInfo.getPossibleTypes());
64326494
DoPostfixExprBeginning();
@@ -6445,7 +6507,7 @@ void CodeCompletionCallbacksImpl::doneParsing() {
64456507
if (isa<AccessorDecl>(ParsedDecl)) {
64466508
ExprContextInfo ContextInfo(CurDeclContext, CodeCompleteTokenExpr);
64476509
Lookup.setExpectedTypes(ContextInfo.getPossibleTypes(),
6448-
ContextInfo.isSingleExpressionBody());
6510+
ContextInfo.isImplicitSingleExpressionReturn());
64496511
DoPostfixExprBeginning();
64506512
}
64516513
break;
@@ -6478,15 +6540,6 @@ void CodeCompletionCallbacksImpl::doneParsing() {
64786540
Lookup.addImportModuleNames();
64796541
break;
64806542
}
6481-
case CompletionKind::UnresolvedMember: {
6482-
Lookup.setHaveDot(DotLoc);
6483-
ExprContextInfo ContextInfo(CurDeclContext, CodeCompleteTokenExpr);
6484-
Lookup.setExpectedTypes(ContextInfo.getPossibleTypes(),
6485-
ContextInfo.isSingleExpressionBody());
6486-
Lookup.setIdealExpectedType(CodeCompleteTokenExpr->getType());
6487-
Lookup.getUnresolvedMemberCompletions(ContextInfo.getPossibleTypes());
6488-
break;
6489-
}
64906543
case CompletionKind::CallArg: {
64916544
ExprContextInfo ContextInfo(CurDeclContext, CodeCompleteTokenExpr);
64926545

@@ -6528,7 +6581,7 @@ void CodeCompletionCallbacksImpl::doneParsing() {
65286581

65296582
if (shouldPerformGlobalCompletion) {
65306583
Lookup.setExpectedTypes(ContextInfo.getPossibleTypes(),
6531-
ContextInfo.isSingleExpressionBody());
6584+
ContextInfo.isImplicitSingleExpressionReturn());
65326585
DoPostfixExprBeginning();
65336586
}
65346587
break;
@@ -6647,7 +6700,7 @@ void CodeCompletionCallbacksImpl::doneParsing() {
66476700
SmallVector<Type, 2> possibleReturnTypes;
66486701
collectPossibleReturnTypesFromContext(CurDeclContext, possibleReturnTypes);
66496702
Lookup.setExpectedTypes(possibleReturnTypes,
6650-
/*isSingleExpressionBody*/ false);
6703+
/*isImplicitSingleExpressionReturn*/ false);
66516704
Lookup.getValueCompletionsInDeclContext(Loc);
66526705
break;
66536706
}
@@ -6658,7 +6711,7 @@ void CodeCompletionCallbacksImpl::doneParsing() {
66586711
if (FD->isCoroutine()) {
66596712
// TODO: handle multi-value yields.
66606713
Lookup.setExpectedTypes(FD->getStorage()->getValueInterfaceType(),
6661-
/*isSingleExpressionBody*/ false);
6714+
/*isImplicitSingleExpressionReturn*/ false);
66626715
}
66636716
}
66646717
Lookup.getValueCompletionsInDeclContext(Loc);
@@ -6668,7 +6721,7 @@ void CodeCompletionCallbacksImpl::doneParsing() {
66686721
case CompletionKind::AfterPoundExpr: {
66696722
ExprContextInfo ContextInfo(CurDeclContext, CodeCompleteTokenExpr);
66706723
Lookup.setExpectedTypes(ContextInfo.getPossibleTypes(),
6671-
ContextInfo.isSingleExpressionBody());
6724+
ContextInfo.isImplicitSingleExpressionReturn());
66726725

66736726
Lookup.addPoundAvailable(ParentStmtKind);
66746727
Lookup.addPoundLiteralCompletions(/*needPound=*/false);

lib/IDE/CodeCompletionResultBuilder.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,12 @@ struct ExpectedTypeContext {
4848
///
4949
/// Since the input may be incomplete, we take into account that the types are
5050
/// only a hint.
51-
bool isSingleExpressionBody = false;
51+
bool isImplicitSingleExpressionReturn = false;
5252
bool preferNonVoid = false;
5353

5454
bool empty() const { return possibleTypes.empty(); }
5555
bool requiresNonVoid() const {
56-
if (isSingleExpressionBody)
56+
if (isImplicitSingleExpressionReturn)
5757
return false;
5858
if (preferNonVoid)
5959
return true;
@@ -65,9 +65,9 @@ struct ExpectedTypeContext {
6565
}
6666

6767
ExpectedTypeContext() = default;
68-
ExpectedTypeContext(ArrayRef<Type> types, bool isSingleExpressionBody)
68+
ExpectedTypeContext(ArrayRef<Type> types, bool isImplicitSingleExprReturn)
6969
: possibleTypes(types.begin(), types.end()),
70-
isSingleExpressionBody(isSingleExpressionBody) {}
70+
isImplicitSingleExpressionReturn(isImplicitSingleExprReturn) {}
7171
};
7272

7373
class CodeCompletionResultBuilder {

0 commit comments

Comments
 (0)