Skip to content

Commit c8aba81

Browse files
committed
[CodeCompletion] Move solver-based unresolved member completion to its own file
1 parent 471b24f commit c8aba81

6 files changed

+267
-211
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
//===--- UnresolvedMemberCompletion.h -------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 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_UNRESOLVEDMEMBERCOMPLETION_H
14+
#define SWIFT_IDE_UNRESOLVEDMEMBERCOMPLETION_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+
/// Used to collect and store information needed to perform unresolved member
24+
/// completion (\c CompletionKind::UnresolvedMember ) from the solutions
25+
/// formed during expression type-checking.
26+
class UnresolvedMemberTypeCheckCompletionCallback
27+
: public TypeCheckCompletionCallback {
28+
public:
29+
struct ExprResult {
30+
Type ExpectedTy;
31+
bool IsImplicitSingleExpressionReturn;
32+
};
33+
34+
private:
35+
CodeCompletionExpr *CompletionExpr;
36+
SmallVector<ExprResult, 4> ExprResults;
37+
SmallVector<Type, 1> EnumPatternTypes;
38+
bool GotCallback = false;
39+
40+
public:
41+
UnresolvedMemberTypeCheckCompletionCallback(
42+
CodeCompletionExpr *CompletionExpr)
43+
: CompletionExpr(CompletionExpr) {}
44+
45+
ArrayRef<ExprResult> getExprResults() const { return ExprResults; }
46+
47+
/// If we are completing in a pattern matching position, the types of all
48+
/// enums for whose cases are valid as an \c EnumElementPattern.
49+
ArrayRef<Type> getEnumPatternTypes() const { return EnumPatternTypes; }
50+
51+
/// True if at least one solution was passed via the \c sawSolution
52+
/// callback.
53+
bool gotCallback() const { return GotCallback; }
54+
55+
/// Typecheck the code completion expression in its outermost expression
56+
/// context, calling \c sawSolution for each solution formed.
57+
void fallbackTypeCheck(DeclContext *DC);
58+
59+
void sawSolution(const constraints::Solution &solution) override;
60+
};
61+
62+
void deliverUnresolvedMemberResults(
63+
ArrayRef<UnresolvedMemberTypeCheckCompletionCallback::ExprResult> Results,
64+
ArrayRef<Type> EnumPatternTypes, DeclContext *DC, SourceLoc DotLoc,
65+
ide::CodeCompletionContext &CompletionCtx,
66+
CodeCompletionConsumer &Consumer);
67+
68+
} // end namespace ide
69+
} // end namespace swift
70+
71+
#endif // SWIFT_IDE_UNRESOLVEDMEMBERCOMPLETION_H

include/swift/Sema/CodeCompletionTypeChecking.h

-37
Original file line numberDiff line numberDiff line change
@@ -44,43 +44,6 @@ namespace swift {
4444
};
4545

4646

47-
/// Used to collect and store information needed to perform unresolved member
48-
/// completion (\c CompletionKind::UnresolvedMember ) from the solutions
49-
/// formed during expression type-checking.
50-
class UnresolvedMemberTypeCheckCompletionCallback: public TypeCheckCompletionCallback {
51-
public:
52-
struct ExprResult {
53-
Type ExpectedTy;
54-
bool IsImplicitSingleExpressionReturn;
55-
};
56-
57-
private:
58-
CodeCompletionExpr *CompletionExpr;
59-
SmallVector<ExprResult, 4> ExprResults;
60-
SmallVector<Type, 1> EnumPatternTypes;
61-
bool GotCallback = false;
62-
63-
public:
64-
UnresolvedMemberTypeCheckCompletionCallback(CodeCompletionExpr *CompletionExpr)
65-
: CompletionExpr(CompletionExpr) {}
66-
67-
ArrayRef<ExprResult> getExprResults() const { return ExprResults; }
68-
69-
/// If we are completing in a pattern matching position, the types of all
70-
/// enums for whose cases are valid as an \c EnumElementPattern.
71-
ArrayRef<Type> getEnumPatternTypes() const { return EnumPatternTypes; }
72-
73-
/// True if at least one solution was passed via the \c sawSolution
74-
/// callback.
75-
bool gotCallback() const { return GotCallback; }
76-
77-
/// Typecheck the code completion expression in its outermost expression
78-
/// context, calling \c sawSolution for each solution formed.
79-
void fallbackTypeCheck(DeclContext *DC);
80-
81-
void sawSolution(const constraints::Solution &solution) override;
82-
};
83-
8447
class KeyPathTypeCheckCompletionCallback
8548
: public TypeCheckCompletionCallback {
8649
public:

lib/IDE/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ add_swift_host_library(swiftIDE STATIC
2727
REPLCodeCompletion.cpp
2828
SwiftSourceDocInfo.cpp
2929
SyntaxModel.cpp
30+
UnresolvedMemberCompletion.cpp
3031
Utils.cpp
3132
IDETypeChecking.cpp
3233
ImportDepth.cpp

lib/IDE/CodeCompletion.cpp

+1-55
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
#include "swift/IDE/CompletionLookup.h"
4040
#include "swift/IDE/CompletionOverrideLookup.h"
4141
#include "swift/IDE/DotExprCompletion.h"
42+
#include "swift/IDE/UnresolvedMemberCompletion.h"
4243
#include "swift/IDE/Utils.h"
4344
#include "swift/Parse/CodeCompletionCallbacks.h"
4445
#include "swift/Sema/CodeCompletionTypeChecking.h"
@@ -1319,61 +1320,6 @@ void swift::ide::deliverCompletionResults(
13191320
Consumer.handleResultsAndModules(CompletionContext, RequestedModules, DC);
13201321
}
13211322

1322-
void deliverUnresolvedMemberResults(
1323-
ArrayRef<UnresolvedMemberTypeCheckCompletionCallback::ExprResult> Results,
1324-
ArrayRef<Type> EnumPatternTypes, DeclContext *DC, SourceLoc DotLoc,
1325-
ide::CodeCompletionContext &CompletionCtx,
1326-
CodeCompletionConsumer &Consumer) {
1327-
ASTContext &Ctx = DC->getASTContext();
1328-
CompletionLookup Lookup(CompletionCtx.getResultSink(), Ctx, DC,
1329-
&CompletionCtx);
1330-
1331-
assert(DotLoc.isValid());
1332-
Lookup.setHaveDot(DotLoc);
1333-
Lookup.shouldCheckForDuplicates(Results.size() + EnumPatternTypes.size() > 1);
1334-
1335-
// Get the canonical versions of the top-level types
1336-
SmallPtrSet<CanType, 4> originalTypes;
1337-
for (auto &Result: Results)
1338-
originalTypes.insert(Result.ExpectedTy->getCanonicalType());
1339-
1340-
for (auto &Result: Results) {
1341-
Lookup.setExpectedTypes({Result.ExpectedTy},
1342-
Result.IsImplicitSingleExpressionReturn,
1343-
/*expectsNonVoid*/true);
1344-
Lookup.setIdealExpectedType(Result.ExpectedTy);
1345-
1346-
// For optional types, also get members of the unwrapped type if it's not
1347-
// already equivalent to one of the top-level types. Handling it via the top
1348-
// level type and not here ensures we give the correct type relation
1349-
// (identical, rather than convertible).
1350-
if (Result.ExpectedTy->getOptionalObjectType()) {
1351-
Type Unwrapped = Result.ExpectedTy->lookThroughAllOptionalTypes();
1352-
if (originalTypes.insert(Unwrapped->getCanonicalType()).second)
1353-
Lookup.getUnresolvedMemberCompletions(Unwrapped);
1354-
}
1355-
Lookup.getUnresolvedMemberCompletions(Result.ExpectedTy);
1356-
}
1357-
1358-
// Offer completions when interpreting the pattern match as an
1359-
// EnumElementPattern.
1360-
for (auto &Ty : EnumPatternTypes) {
1361-
Lookup.setExpectedTypes({Ty}, /*IsImplicitSingleExpressionReturn=*/false,
1362-
/*expectsNonVoid=*/true);
1363-
Lookup.setIdealExpectedType(Ty);
1364-
1365-
// We can pattern match MyEnum against Optional<MyEnum>
1366-
if (Ty->getOptionalObjectType()) {
1367-
Type Unwrapped = Ty->lookThroughAllOptionalTypes();
1368-
Lookup.getEnumElementPatternCompletions(Unwrapped);
1369-
}
1370-
1371-
Lookup.getEnumElementPatternCompletions(Ty);
1372-
}
1373-
1374-
deliverCompletionResults(CompletionCtx, Lookup, DC, Consumer);
1375-
}
1376-
13771323
void deliverKeyPathResults(
13781324
ArrayRef<KeyPathTypeCheckCompletionCallback::Result> Results,
13791325
DeclContext *DC, SourceLoc DotLoc,
+194
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
//===--- UnresolvedMemberCodeCompletion.cpp -------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 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+
#include "swift/IDE/UnresolvedMemberCompletion.h"
14+
#include "swift/IDE/CodeCompletion.h"
15+
#include "swift/IDE/CompletionLookup.h"
16+
#include "swift/Sema/CompletionContextFinder.h"
17+
#include "swift/Sema/ConstraintSystem.h"
18+
#include "swift/Sema/IDETypeChecking.h"
19+
20+
using namespace swift;
21+
using namespace swift::constraints;
22+
using namespace swift::ide;
23+
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+
83+
void UnresolvedMemberTypeCheckCompletionCallback::sawSolution(
84+
const constraints::Solution &S) {
85+
GotCallback = true;
86+
87+
auto &CS = S.getConstraintSystem();
88+
Type ExpectedTy = getTypeForCompletion(S, CompletionExpr);
89+
// If the type couldn't be determined (e.g. because there isn't any context
90+
// to derive it from), let's not attempt to do a lookup since it wouldn't
91+
// produce any useful results anyway.
92+
if (ExpectedTy && !ExpectedTy->is<UnresolvedType>()) {
93+
// If ExpectedTy is a duplicate of any other result, ignore this solution.
94+
if (!llvm::any_of(ExprResults, [&](const ExprResult &R) {
95+
return R.ExpectedTy->isEqual(ExpectedTy);
96+
})) {
97+
bool SingleExprBody =
98+
isImplicitSingleExpressionReturn(CS, CompletionExpr);
99+
ExprResults.push_back({ExpectedTy, SingleExprBody});
100+
}
101+
}
102+
103+
if (auto MatchVar = getMatchVarIfInPatternMatch(CompletionExpr, CS)) {
104+
Type MatchVarType;
105+
// If the MatchVar has an explicit type, it's not part of the solution. But
106+
// we can look it up in the constraint system directly.
107+
if (auto T = S.getConstraintSystem().getVarType(MatchVar)) {
108+
MatchVarType = T;
109+
} else {
110+
MatchVarType = S.getResolvedType(MatchVar);
111+
}
112+
if (MatchVarType && !MatchVarType->is<UnresolvedType>()) {
113+
if (!llvm::any_of(EnumPatternTypes, [&](const Type &R) {
114+
return R->isEqual(MatchVarType);
115+
})) {
116+
EnumPatternTypes.push_back(MatchVarType);
117+
}
118+
}
119+
}
120+
}
121+
122+
void UnresolvedMemberTypeCheckCompletionCallback::fallbackTypeCheck(
123+
DeclContext *DC) {
124+
assert(!gotCallback());
125+
126+
CompletionContextFinder finder(DC);
127+
if (!finder.hasCompletionExpr())
128+
return;
129+
130+
auto fallback = finder.getFallbackCompletionExpr();
131+
if (!fallback)
132+
return;
133+
134+
SolutionApplicationTarget completionTarget(fallback->E, fallback->DC,
135+
CTP_Unused, Type(),
136+
/*isDiscared=*/true);
137+
typeCheckForCodeCompletion(completionTarget, /*needsPrecheck*/ true,
138+
[&](const Solution &S) { sawSolution(S); });
139+
}
140+
141+
void swift::ide::deliverUnresolvedMemberResults(
142+
ArrayRef<UnresolvedMemberTypeCheckCompletionCallback::ExprResult> Results,
143+
ArrayRef<Type> EnumPatternTypes, DeclContext *DC, SourceLoc DotLoc,
144+
ide::CodeCompletionContext &CompletionCtx,
145+
CodeCompletionConsumer &Consumer) {
146+
ASTContext &Ctx = DC->getASTContext();
147+
CompletionLookup Lookup(CompletionCtx.getResultSink(), Ctx, DC,
148+
&CompletionCtx);
149+
150+
assert(DotLoc.isValid());
151+
Lookup.setHaveDot(DotLoc);
152+
Lookup.shouldCheckForDuplicates(Results.size() + EnumPatternTypes.size() > 1);
153+
154+
// Get the canonical versions of the top-level types
155+
SmallPtrSet<CanType, 4> originalTypes;
156+
for (auto &Result : Results)
157+
originalTypes.insert(Result.ExpectedTy->getCanonicalType());
158+
159+
for (auto &Result : Results) {
160+
Lookup.setExpectedTypes({Result.ExpectedTy},
161+
Result.IsImplicitSingleExpressionReturn,
162+
/*expectsNonVoid*/ true);
163+
Lookup.setIdealExpectedType(Result.ExpectedTy);
164+
165+
// For optional types, also get members of the unwrapped type if it's not
166+
// already equivalent to one of the top-level types. Handling it via the top
167+
// level type and not here ensures we give the correct type relation
168+
// (identical, rather than convertible).
169+
if (Result.ExpectedTy->getOptionalObjectType()) {
170+
Type Unwrapped = Result.ExpectedTy->lookThroughAllOptionalTypes();
171+
if (originalTypes.insert(Unwrapped->getCanonicalType()).second)
172+
Lookup.getUnresolvedMemberCompletions(Unwrapped);
173+
}
174+
Lookup.getUnresolvedMemberCompletions(Result.ExpectedTy);
175+
}
176+
177+
// Offer completions when interpreting the pattern match as an
178+
// EnumElementPattern.
179+
for (auto &Ty : EnumPatternTypes) {
180+
Lookup.setExpectedTypes({Ty}, /*IsImplicitSingleExpressionReturn=*/false,
181+
/*expectsNonVoid=*/true);
182+
Lookup.setIdealExpectedType(Ty);
183+
184+
// We can pattern match MyEnum against Optional<MyEnum>
185+
if (Ty->getOptionalObjectType()) {
186+
Type Unwrapped = Ty->lookThroughAllOptionalTypes();
187+
Lookup.getEnumElementPatternCompletions(Unwrapped);
188+
}
189+
190+
Lookup.getEnumElementPatternCompletions(Ty);
191+
}
192+
193+
deliverCompletionResults(CompletionCtx, Lookup, DC, Consumer);
194+
}

0 commit comments

Comments
 (0)