Skip to content

Commit 56ea334

Browse files
committed
[CodeCompletion] Migrate ForEachSequence and PostfixExprBeginning to solver-based
1 parent 1ef2c59 commit 56ea334

14 files changed

+148
-120
lines changed

include/swift/IDE/TypeCheckCompletionCallback.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,13 @@ class TypeCheckCompletionCallback {
6767

6868
// MARK: - Utility functions for subclasses of TypeCheckCompletionCallback
6969

70-
Type getTypeForCompletion(const constraints::Solution &S, Expr *E);
70+
Type getTypeForCompletion(const constraints::Solution &S, ASTNode Node);
71+
72+
/// If \p E occurs in a pattern matching position, returns the type that it is
73+
/// being pattern-matched against.
74+
/// If that type is an enum, it allows us to suggest the enum cases for the code
75+
/// completion expression \p E.
76+
Type getPatternMatchType(const constraints::Solution &S, Expr *E);
7177

7278
/// Whether the given completion expression is the only expression in its
7379
/// containing closure or function body and its value is implicitly returned.

lib/AST/Type.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4308,11 +4308,11 @@ operator()(CanType dependentType, Type conformingReplacementType,
43084308
ProtocolConformanceRef MakeAbstractConformanceForGenericType::
43094309
operator()(CanType dependentType, Type conformingReplacementType,
43104310
ProtocolDecl *conformedProtocol) const {
4311-
assert((conformingReplacementType->is<ErrorType>()
4312-
|| conformingReplacementType->is<SubstitutableType>()
4313-
|| conformingReplacementType->is<DependentMemberType>()
4314-
|| conformingReplacementType->is<TypeVariableType>())
4315-
&& "replacement requires looking up a concrete conformance");
4311+
assert((conformingReplacementType->is<ErrorType>() ||
4312+
conformingReplacementType->is<SubstitutableType>() ||
4313+
conformingReplacementType->is<DependentMemberType>() ||
4314+
conformingReplacementType->hasTypeVariable()) &&
4315+
"replacement requires looking up a concrete conformance");
43164316
// A class-constrained archetype might conform to the protocol
43174317
// concretely.
43184318
if (auto *archetypeType = conformingReplacementType->getAs<ArchetypeType>()) {

lib/IDE/CodeCompletion.cpp

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1396,7 +1396,9 @@ bool CodeCompletionCallbacksImpl::trySolverCompletion(bool MaybeFuncBody) {
13961396
CurDeclContext, CompletionContext, Consumer);
13971397
return true;
13981398
}
1399-
case CompletionKind::StmtOrExpr: {
1399+
case CompletionKind::StmtOrExpr:
1400+
case CompletionKind::ForEachSequence:
1401+
case CompletionKind::PostfixExprBeginning: {
14001402
assert(CodeCompleteTokenExpr);
14011403
assert(CurDeclContext);
14021404

@@ -1539,18 +1541,11 @@ void CodeCompletionCallbacksImpl::doneParsing() {
15391541
case CompletionKind::KeyPathExprSwift:
15401542
case CompletionKind::CallArg:
15411543
case CompletionKind::StmtOrExpr:
1544+
case CompletionKind::ForEachSequence:
1545+
case CompletionKind::PostfixExprBeginning:
15421546
llvm_unreachable("should be already handled");
15431547
return;
15441548

1545-
case CompletionKind::ForEachSequence:
1546-
case CompletionKind::PostfixExprBeginning: {
1547-
ExprContextInfo ContextInfo(CurDeclContext, CodeCompleteTokenExpr);
1548-
Lookup.setExpectedTypes(ContextInfo.getPossibleTypes(),
1549-
ContextInfo.isImplicitSingleExpressionReturn());
1550-
DoPostfixExprBeginning();
1551-
break;
1552-
}
1553-
15541549
case CompletionKind::PostfixExpr: {
15551550
Lookup.setHaveLeadingSpace(HasSpace);
15561551
if (isDynamicLookup(*ExprType))

lib/IDE/ExprCompletion.cpp

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,11 @@ void ExprTypeCheckCompletionCallback::sawSolutionImpl(
3838

3939
Results.push_back(
4040
{ExpectedTy, ImplicitReturn, IsAsync, SolutionSpecificVarTypes});
41+
42+
if (auto PatternMatchType = getPatternMatchType(S, CompletionExpr)) {
43+
Results.push_back(
44+
{PatternMatchType, ImplicitReturn, IsAsync, SolutionSpecificVarTypes});
45+
}
4146
}
4247

4348
void ExprTypeCheckCompletionCallback::deliverResults(
@@ -46,9 +51,14 @@ void ExprTypeCheckCompletionCallback::deliverResults(
4651
ASTContext &Ctx = DC->getASTContext();
4752
CompletionLookup Lookup(CompletionCtx.getResultSink(), Ctx, DC,
4853
&CompletionCtx);
54+
Lookup.shouldCheckForDuplicates(Results.size() > 1);
4955

56+
SmallVector<Type, 2> ExpectedTypes;
57+
for (auto &Result : Results) {
58+
ExpectedTypes.push_back(Result.ExpectedType);
59+
}
5060
for (auto &Result : Results) {
51-
Lookup.setExpectedTypes(Result.ExpectedType,
61+
Lookup.setExpectedTypes(ExpectedTypes,
5262
Result.IsImplicitSingleExpressionReturn);
5363
Lookup.setCanCurrDeclContextHandleAsync(Result.IsInAsyncContext);
5464
Lookup.setSolutionSpecificVarTypes(Result.SolutionSpecificVarTypes);

lib/IDE/ExprContextAnalysis.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,10 @@ void swift::ide::typeCheckContextAt(DeclContext *DC, SourceLoc Loc) {
107107
auto *Param = AFD->getParameters()->get(defaultArg->getIndex());
108108
(void)Param->getTypeCheckedDefaultExpr();
109109
}
110+
if (auto *SD = dyn_cast<SubscriptDecl>(defaultArg->getParent())) {
111+
auto *Param = SD->getIndices()->get(defaultArg->getIndex());
112+
(void)Param->getTypeCheckedDefaultExpr();
113+
}
110114
}
111115
break;
112116

lib/IDE/TypeCheckCompletionCallback.cpp

Lines changed: 66 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,9 @@ void TypeCheckCompletionCallback::fallbackTypeCheck(DeclContext *DC) {
4040

4141
// MARK: - Utility functions for subclasses of TypeCheckCompletionCallback
4242

43-
Type swift::ide::getTypeForCompletion(const constraints::Solution &S, Expr *E) {
44-
if (!S.hasType(E)) {
43+
Type swift::ide::getTypeForCompletion(const constraints::Solution &S,
44+
ASTNode Node) {
45+
if (!S.hasType(Node)) {
4546
assert(false && "Expression wasn't type checked?");
4647
return nullptr;
4748
}
@@ -66,8 +67,8 @@ Type swift::ide::getTypeForCompletion(const constraints::Solution &S, Expr *E) {
6667
// expression should still have a type of `[T]` instead of `[<<hole>>]`
6768
// because it helps to produce correct contextual member list based on
6869
// a conformance requirement associated with generic parameter `T`.
69-
if (isa<CodeCompletionExpr>(E)) {
70-
auto completionTy = S.getType(E).transform([&](Type type) -> Type {
70+
if (isExpr<CodeCompletionExpr>(Node)) {
71+
auto completionTy = S.getType(Node).transform([&](Type type) -> Type {
7172
if (auto *typeVar = type->getAs<TypeVariableType>())
7273
return S.getFixedType(typeVar);
7374
return type;
@@ -92,18 +93,77 @@ Type swift::ide::getTypeForCompletion(const constraints::Solution &S, Expr *E) {
9293
return type;
9394
}));
9495
} else {
95-
Result = S.getResolvedType(E);
96+
Result = S.getResolvedType(Node);
9697
}
9798

9899
if (!Result || Result->is<UnresolvedType>()) {
99-
Result = CS.getContextualType(E, /*forConstraint=*/false);
100+
Result = CS.getContextualType(Node, /*forConstraint=*/false);
100101
}
101102
if (Result && Result->is<UnresolvedType>()) {
102103
Result = Type();
103104
}
104105
return Result;
105106
}
106107

108+
/// If the code completion expression \p E occurs in a pattern matching
109+
/// position, we have an AST that looks like this.
110+
/// \code
111+
/// (binary_expr implicit type='$T3'
112+
/// (overloaded_decl_ref_expr function_ref=compound decls=[
113+
/// Swift.(file).~=,
114+
/// Swift.(file).Optional extension.~=])
115+
/// (argument_list implicit
116+
/// (argument
117+
/// (code_completion_expr implicit type='$T1'))
118+
/// (argument
119+
/// (declref_expr implicit decl=swift_ide_test.(file).foo(x:).$match))))
120+
/// \endcode
121+
/// If the code completion expression occurs in such an AST, return the
122+
/// declaration of the \c $match variable, otherwise return \c nullptr.
123+
static VarDecl *getMatchVarIfInPatternMatch(Expr *E, ConstraintSystem &CS) {
124+
auto &Context = CS.getASTContext();
125+
126+
auto *Binary = dyn_cast_or_null<BinaryExpr>(CS.getParentExpr(E));
127+
if (!Binary || !Binary->isImplicit() || Binary->getLHS() != E) {
128+
return nullptr;
129+
}
130+
131+
auto CalledOperator = Binary->getFn();
132+
if (!isPatternMatchingOperator(CalledOperator)) {
133+
return nullptr;
134+
}
135+
136+
auto MatchArg = dyn_cast_or_null<DeclRefExpr>(Binary->getRHS());
137+
if (!MatchArg || !MatchArg->isImplicit()) {
138+
return nullptr;
139+
}
140+
141+
auto MatchVar = MatchArg->getDecl();
142+
if (MatchVar && MatchVar->isImplicit() &&
143+
MatchVar->getBaseName() == Context.Id_PatternMatchVar) {
144+
return dyn_cast<VarDecl>(MatchVar);
145+
} else {
146+
return nullptr;
147+
}
148+
}
149+
150+
Type swift::ide::getPatternMatchType(const constraints::Solution &S, Expr *E) {
151+
if (auto MatchVar = getMatchVarIfInPatternMatch(E, S.getConstraintSystem())) {
152+
Type MatchVarType;
153+
// If the MatchVar has an explicit type, it's not part of the solution. But
154+
// we can look it up in the constraint system directly.
155+
if (auto T = S.getConstraintSystem().getVarType(MatchVar)) {
156+
MatchVarType = T;
157+
} else {
158+
MatchVarType = getTypeForCompletion(S, MatchVar);
159+
}
160+
if (MatchVarType) {
161+
return MatchVarType;
162+
}
163+
}
164+
return nullptr;
165+
}
166+
107167
bool swift::ide::isImplicitSingleExpressionReturn(ConstraintSystem &CS,
108168
Expr *CompletionExpr) {
109169
Expr *ParentExpr = CS.getParentExpr(CompletionExpr);

lib/IDE/UnresolvedMemberCompletion.cpp

Lines changed: 8 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -21,65 +21,6 @@ using namespace swift;
2121
using namespace swift::constraints;
2222
using namespace swift::ide;
2323

24-
/// If the code completion variable occurs in a pattern matching position, we
25-
/// have an AST that looks like this.
26-
/// \code
27-
/// (binary_expr implicit type='$T3'
28-
/// (overloaded_decl_ref_expr function_ref=compound decls=[
29-
/// Swift.(file).~=,
30-
/// Swift.(file).Optional extension.~=])
31-
/// (argument_list implicit
32-
/// (argument
33-
/// (code_completion_expr implicit type='$T1'))
34-
/// (argument
35-
/// (declref_expr implicit decl=swift_ide_test.(file).foo(x:).$match))))
36-
/// \endcode
37-
/// If the code completion expression occurs in such an AST, return the
38-
/// declaration of the \c $match variable, otherwise return \c nullptr.
39-
static VarDecl *getMatchVarIfInPatternMatch(CodeCompletionExpr *CompletionExpr,
40-
ConstraintSystem &CS) {
41-
auto &Context = CS.getASTContext();
42-
43-
auto *Binary = dyn_cast_or_null<BinaryExpr>(CS.getParentExpr(CompletionExpr));
44-
if (!Binary || !Binary->isImplicit() || Binary->getLHS() != CompletionExpr) {
45-
return nullptr;
46-
}
47-
48-
auto CalledOperator = Binary->getFn();
49-
if (!CalledOperator || !CalledOperator->isImplicit()) {
50-
return nullptr;
51-
}
52-
// The reference to the ~= operator might be an OverloadedDeclRefExpr or a
53-
// DeclRefExpr, depending on how many ~= operators are viable.
54-
if (auto Overloaded =
55-
dyn_cast_or_null<OverloadedDeclRefExpr>(CalledOperator)) {
56-
if (!llvm::all_of(Overloaded->getDecls(), [&Context](ValueDecl *D) {
57-
return D->getBaseName() == Context.Id_MatchOperator;
58-
})) {
59-
return nullptr;
60-
}
61-
} else if (auto Ref = dyn_cast_or_null<DeclRefExpr>(CalledOperator)) {
62-
if (Ref->getDecl()->getBaseName() != Context.Id_MatchOperator) {
63-
return nullptr;
64-
}
65-
} else {
66-
return nullptr;
67-
}
68-
69-
auto MatchArg = dyn_cast_or_null<DeclRefExpr>(Binary->getRHS());
70-
if (!MatchArg || !MatchArg->isImplicit()) {
71-
return nullptr;
72-
}
73-
74-
auto MatchVar = MatchArg->getDecl();
75-
if (MatchVar && MatchVar->isImplicit() &&
76-
MatchVar->getBaseName() == Context.Id_PatternMatchVar) {
77-
return dyn_cast<VarDecl>(MatchVar);
78-
} else {
79-
return nullptr;
80-
}
81-
}
82-
8324
void UnresolvedMemberTypeCheckCompletionCallback::sawSolutionImpl(
8425
const constraints::Solution &S) {
8526
auto &CS = S.getConstraintSystem();
@@ -102,24 +43,14 @@ void UnresolvedMemberTypeCheckCompletionCallback::sawSolutionImpl(
10243
}
10344
}
10445

105-
if (auto MatchVar = getMatchVarIfInPatternMatch(CompletionExpr, CS)) {
106-
Type MatchVarType;
107-
// If the MatchVar has an explicit type, it's not part of the solution. But
108-
// we can look it up in the constraint system directly.
109-
if (auto T = S.getConstraintSystem().getVarType(MatchVar)) {
110-
MatchVarType = T;
111-
} else {
112-
MatchVarType = S.getResolvedType(MatchVar);
113-
}
114-
if (MatchVarType && !MatchVarType->is<UnresolvedType>()) {
115-
auto IsEqual = [&](const Result &R) {
116-
return R.ExpectedTy->isEqual(MatchVarType);
117-
};
118-
if (!llvm::any_of(EnumPatternTypes, IsEqual)) {
119-
EnumPatternTypes.push_back({MatchVarType,
120-
/*IsImplicitSingleExpressionReturn=*/false,
121-
IsAsync});
122-
}
46+
if (auto PatternType = getPatternMatchType(S, CompletionExpr)) {
47+
auto IsEqual = [&](const Result &R) {
48+
return R.ExpectedTy->isEqual(PatternType);
49+
};
50+
if (!llvm::any_of(EnumPatternTypes, IsEqual)) {
51+
EnumPatternTypes.push_back({PatternType,
52+
/*IsImplicitSingleExpressionReturn=*/false,
53+
IsAsync});
12354
}
12455
}
12556
}

lib/Sema/TypeCheckConstraints.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -433,7 +433,11 @@ TypeChecker::typeCheckExpression(SolutionApplicationTarget &target,
433433
Type TypeChecker::typeCheckParameterDefault(Expr *&defaultValue,
434434
DeclContext *DC, Type paramType,
435435
bool isAutoClosure) {
436-
assert(paramType && !paramType->hasError());
436+
// During normal type checking we don't type check the parameter default if
437+
// the param has an error type. For code completion, we also type check the
438+
// parameter default because it might contain the code completion token.
439+
assert(paramType &&
440+
(!paramType->hasError() || DC->getASTContext().CompletionCallback));
437441

438442
auto &ctx = DC->getASTContext();
439443

lib/Sema/TypeCheckDeclPrimary.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1039,7 +1039,10 @@ Expr *DefaultArgumentExprRequest::evaluate(Evaluator &evaluator,
10391039
auto *initExpr = param->getStructuralDefaultExpr();
10401040
assert(initExpr);
10411041

1042-
if (paramTy->hasError())
1042+
// If the param has an error type, there's no point type checking the default
1043+
// expression, unless we are type checking for code completion, in which case
1044+
// the default expression might contain the code completion token.
1045+
if (paramTy->hasError() && !ctx.CompletionCallback)
10431046
return new (ctx) ErrorExpr(initExpr->getSourceRange(), ErrorType::get(ctx));
10441047

10451048
auto *dc = param->getDefaultArgumentInitContext();

test/IDE/complete_associated_types.swift

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -341,9 +341,7 @@ struct A<T>: MyProto {
341341
}
342342
}
343343
// BROKEN_CONFORMANCE_UNASSIGNABLE: Begin completions
344-
// BROKEN_CONFORMANCE_UNASSIGNABLE-NOT: TypeRelation
345344
// BROKEN_CONFORMANCE_UNASSIGNABLE: Decl[InstanceMethod]/Super: first()[#MyProto.Element#]; name=first()
346-
// BROKEN_CONFORMANCE_UNASSIGNABLE-NOT: TypeRelation
347345
// BROKEN_CONFORMANCE_UNASSIGNABLE: End completions
348346

349347
// BROKEN_CONFORMANCE_ASSIGNABLE: Begin completions

test/IDE/complete_exception.swift

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -88,21 +88,21 @@ func test002() {
8888
throw #^THROW1^#
8989

9090
// THROW1: Begin completions
91-
// THROW1-DAG: Decl[Enum]/CurrModule: Error4[#Error4#]; name=Error4{{$}}
92-
// THROW1-DAG: Decl[Class]/CurrModule: Error3[#Error3#]; name=Error3{{$}}
93-
// THROW1-DAG: Decl[Class]/CurrModule: Error2[#Error2#]; name=Error2{{$}}
94-
// THROW1-DAG: Decl[Class]/CurrModule: Error1[#Error1#]; name=Error1{{$}}
95-
// THROW1-DAG: Decl[Protocol]/CurrModule/Flair[RareType]: ErrorPro1[#ErrorPro1#]; name=ErrorPro1{{$}}
96-
// THROW1-DAG: Decl[FreeFunction]/CurrModule: getError1()[#Error1#]{{; name=.+$}}
97-
// THROW1-DAG: Decl[FreeFunction]/CurrModule: getNSError()[#NSError#]{{; name=.+$}}
91+
// THROW1-DAG: Decl[Enum]/CurrModule/TypeRelation[Convertible]: Error4[#Error4#]; name=Error4{{$}}
92+
// THROW1-DAG: Decl[Class]/CurrModule/TypeRelation[Convertible]: Error3[#Error3#]; name=Error3{{$}}
93+
// THROW1-DAG: Decl[Class]/CurrModule/TypeRelation[Convertible]: Error2[#Error2#]; name=Error2{{$}}
94+
// THROW1-DAG: Decl[Class]/CurrModule/TypeRelation[Convertible]: Error1[#Error1#]; name=Error1{{$}}
95+
// THROW1-DAG: Decl[Protocol]/CurrModule/Flair[RareType]/TypeRelation[Convertible]: ErrorPro1[#ErrorPro1#]; name=ErrorPro1{{$}}
96+
// THROW1-DAG: Decl[FreeFunction]/CurrModule/TypeRelation[Convertible]: getError1()[#Error1#]{{; name=.+$}}
97+
// THROW1-DAG: Decl[FreeFunction]/CurrModule/TypeRelation[Convertible]: getNSError()[#NSError#]{{; name=.+$}}
9898

9999
// If we could prove that there is no way to get to an Error value by
100100
// starting from these, we could remove them. But that may be infeasible in
101101
// the presence of overloaded operators.
102102
// THROW1-DAG: Decl[Class]/CurrModule: NoneError1[#NoneError1#]; name=NoneError1{{$}}
103103
// THROW1-LOCAL: Decl[LocalVar]/Local: text[#String#]; name=text{{$}}
104-
// THROW1-LOCAL: Decl[LocalVar]/Local: e1[#Error1#]; name=e1{{$}}
105-
// THROW1-LOCAL: Decl[LocalVar]/Local: e2[#Error2#]; name=e2{{$}}
104+
// THROW1-LOCAL: Decl[LocalVar]/Local/TypeRelation[Convertible]: e1[#Error1#]; name=e1{{$}}
105+
// THROW1-LOCAL: Decl[LocalVar]/Local/TypeRelation[Convertible]: e2[#Error2#]; name=e2{{$}}
106106
}
107107

108108
func test003() {

test/IDE/complete_expr_postfix_begin.swift

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -435,9 +435,14 @@ func testInForEach9(arg: Int) {
435435
}
436436
func testInForEach10(arg: Int) {
437437
let local = 2
438-
for index in [1:2, #^IN_FOR_EACH_10?check=IN_FOR_EACH_1^#] {}
438+
for index in [1:2, #^IN_FOR_EACH_10^#] {}
439439
let after = 4
440440
}
441+
// IN_FOR_EACH_10-NOT: Decl[LocalVar]
442+
// IN_FOR_EACH_10: Decl[LocalVar]/Local/TypeRelation[Identical]: local[#Int#];
443+
// IN_FOR_EACH_10-NOT: after
444+
// IN_FOR_EACH_10: Decl[LocalVar]/Local/TypeRelation[Identical]: arg[#Int#];
445+
// IN_FOR_EACH_10-NOT: Decl[LocalVar]
441446
func testInForEach11(arg: Int) {
442447
let local = 2
443448
for index in [1:2, #^IN_FOR_EACH_11?check=IN_FOR_EACH_2^#:] {}

test/IDE/complete_operator_operand.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@ func test_binary1(c1: C1, c2: C2) {
1515
// BINARY_RHS_1: Begin completion
1616
// BINARY_RHS_1-NOT: Decl[LocalVar]/Local/TypeRelation[Identical]: c1[#C1#]; name=c1
1717
// BINARY_RHS_1-DAG: Decl[LocalVar]/Local/TypeRelation[Identical]: c2[#C2#]; name=c2
18+
}
1819

20+
func test_binary2(c1: C1, c2: C2) {
1921
_ = c2 <--> #^BINARY_RHS_2^#
2022
// BINARY_RHS_2: Begin completion
2123
// BINARY_RHS_2-NOT: Decl[LocalVar]/Local/TypeRelation[Identical]: c2[#C2#]; name=c2

0 commit comments

Comments
 (0)