Skip to content

Commit d0691e3

Browse files
authored
Merge pull request #42224 from rintaro/5.7-ide-completion-rdar90399603
[5.7][CodeCompletion] Update for SE-0345 shorthand optional binding
2 parents be60ff0 + bb7b9dd commit d0691e3

File tree

7 files changed

+143
-8
lines changed

7 files changed

+143
-8
lines changed

include/swift/IDE/CodeCompletionResult.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,7 @@ enum class CompletionKind : uint8_t {
223223
StmtLabel,
224224
ForEachPatternBeginning,
225225
TypeAttrBeginning,
226+
OptionalBinding,
226227
};
227228

228229
enum class CodeCompletionDiagnosticSeverity : uint8_t {

include/swift/IDE/CompletionLookup.h

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,15 @@
3333
namespace swift {
3434
namespace ide {
3535

36-
using DeclFilter = std::function<bool(ValueDecl *, DeclVisibilityKind)>;
36+
using DeclFilter =
37+
std::function<bool(ValueDecl *, DeclVisibilityKind, DynamicLookupInfo)>;
3738

3839
/// A filter that always returns \c true.
39-
bool DefaultFilter(ValueDecl *VD, DeclVisibilityKind Kind);
40+
bool DefaultFilter(ValueDecl *VD, DeclVisibilityKind Kind,
41+
DynamicLookupInfo dynamicLookupInfo);
4042

41-
bool KeyPathFilter(ValueDecl *decl, DeclVisibilityKind);
43+
bool KeyPathFilter(ValueDecl *decl, DeclVisibilityKind,
44+
DynamicLookupInfo dynamicLookupInfo);
4245

4346
/// Returns \c true only if the completion is happening for top-level
4447
/// declrarations. i.e.:
@@ -528,7 +531,7 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
528531
: Consumer(Consumer), Filter(Filter) {}
529532
void foundDecl(ValueDecl *VD, DeclVisibilityKind Kind,
530533
DynamicLookupInfo dynamicLookupInfo) override {
531-
if (Filter(VD, Kind))
534+
if (Filter(VD, Kind, dynamicLookupInfo))
532535
Consumer.foundDecl(VD, Kind, dynamicLookupInfo);
533536
}
534537
};
@@ -588,6 +591,8 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
588591
bool ResultsHaveLeadingDot);
589592

590593
void getStmtLabelCompletions(SourceLoc Loc, bool isContinue);
594+
595+
void getOptionalBindingCompletions(SourceLoc Loc);
591596
};
592597

593598
} // end namespace ide

include/swift/Parse/CodeCompletionCallbacks.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,8 @@ class CodeCompletionCallbacks {
236236

237237
virtual void completeTypeAttrBeginning() {};
238238

239+
virtual void completeOptionalBinding(){};
240+
239241
/// Signals that the AST for the all the delayed-parsed code was
240242
/// constructed. No \c complete*() callbacks will be done after this.
241243
virtual void doneParsing() = 0;

lib/IDE/CodeCompletion.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,7 @@ class CodeCompletionCallbacksImpl : public CodeCompletionCallbacks {
274274
void completeStmtLabel(StmtKind ParentKind) override;
275275
void completeForEachPatternBeginning(bool hasTry, bool hasAwait) override;
276276
void completeTypeAttrBeginning() override;
277+
void completeOptionalBinding() override;
277278

278279
void doneParsing() override;
279280

@@ -625,6 +626,11 @@ void CodeCompletionCallbacksImpl::completeForEachPatternBeginning(
625626
ParsedKeywords.emplace_back("await");
626627
}
627628

629+
void CodeCompletionCallbacksImpl::completeOptionalBinding() {
630+
CurDeclContext = P.CurDeclContext;
631+
Kind = CompletionKind::OptionalBinding;
632+
}
633+
628634
void CodeCompletionCallbacksImpl::completeTypeAttrBeginning() {
629635
CurDeclContext = P.CurDeclContext;
630636
Kind = CompletionKind::TypeAttrBeginning;
@@ -907,6 +913,7 @@ void CodeCompletionCallbacksImpl::addKeywords(CodeCompletionResultSink &Sink,
907913
case CompletionKind::PrecedenceGroup:
908914
case CompletionKind::StmtLabel:
909915
case CompletionKind::TypeAttrBeginning:
916+
case CompletionKind::OptionalBinding:
910917
break;
911918

912919
case CompletionKind::EffectsSpecifier: {
@@ -1886,6 +1893,12 @@ void CodeCompletionCallbacksImpl::doneParsing() {
18861893
break;
18871894

18881895
}
1896+
case CompletionKind::OptionalBinding: {
1897+
SourceLoc Loc = P.Context.SourceMgr.getCodeCompletionLoc();
1898+
Lookup.getOptionalBindingCompletions(Loc);
1899+
break;
1900+
}
1901+
18891902
case CompletionKind::AfterIfStmtElse:
18901903
case CompletionKind::CaseStmtKeyword:
18911904
case CompletionKind::EffectsSpecifier:

lib/IDE/CompletionLookup.cpp

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -119,11 +119,13 @@ static bool hasTrivialTrailingClosure(const FuncDecl *FD,
119119
}
120120
} // end anonymous namespace
121121

122-
bool swift::ide::DefaultFilter(ValueDecl *VD, DeclVisibilityKind Kind) {
122+
bool swift::ide::DefaultFilter(ValueDecl *VD, DeclVisibilityKind Kind,
123+
DynamicLookupInfo dynamicLookupInfo) {
123124
return true;
124125
}
125126

126-
bool swift::ide::KeyPathFilter(ValueDecl *decl, DeclVisibilityKind) {
127+
bool swift::ide::KeyPathFilter(ValueDecl *decl, DeclVisibilityKind,
128+
DynamicLookupInfo dynamicLookupInfo) {
127129
return isa<TypeDecl>(decl) ||
128130
(isa<VarDecl>(decl) && decl->getDeclContext()->isTypeContext());
129131
}
@@ -1799,7 +1801,7 @@ void CompletionLookup::foundDecl(ValueDecl *D, DeclVisibilityKind Reason,
17991801
if (D->shouldHideFromEditor())
18001802
return;
18011803

1802-
if (IsKeyPathExpr && !KeyPathFilter(D, Reason))
1804+
if (IsKeyPathExpr && !KeyPathFilter(D, Reason, dynamicLookupInfo))
18031805
return;
18041806

18051807
if (IsSwiftKeyPathExpr && !SwiftKeyPathFilter(D, Reason))
@@ -2695,7 +2697,8 @@ void CompletionLookup::getUnresolvedMemberCompletions(Type T) {
26952697
// type and has the same type (or if the member is a function, then the
26962698
// same result type) as the contextual type.
26972699
FilteredDeclConsumer consumer(*this,
2698-
[=](ValueDecl *VD, DeclVisibilityKind Reason) {
2700+
[=](ValueDecl *VD, DeclVisibilityKind Reason,
2701+
DynamicLookupInfo dynamicLookupInfo) {
26992702
// In optional context, ignore
27002703
// '.init(<some>)', 'init(nilLiteral:)',
27012704
return !isInitializerOnOptional(T, VD);
@@ -3118,3 +3121,32 @@ void CompletionLookup::getStmtLabelCompletions(SourceLoc Loc, bool isContinue) {
31183121
Builder.addTextChunk(name.str());
31193122
}
31203123
}
3124+
3125+
void CompletionLookup::getOptionalBindingCompletions(SourceLoc Loc) {
3126+
ExprType = Type();
3127+
Kind = LookupKind::ValueInDeclContext;
3128+
NeedLeadingDot = false;
3129+
3130+
AccessFilteringDeclConsumer AccessFilteringConsumer(CurrDeclContext, *this);
3131+
3132+
// Suggest only 'Optional' type var decls (incl. parameters)
3133+
FilteredDeclConsumer FilteringConsumer(
3134+
AccessFilteringConsumer,
3135+
[&](ValueDecl *VD, DeclVisibilityKind Reason,
3136+
DynamicLookupInfo dynamicLookupInfo) -> bool {
3137+
auto *VarD = dyn_cast<VarDecl>(VD);
3138+
if (!VarD)
3139+
return false;
3140+
3141+
auto Ty = getTypeOfMember(VD, dynamicLookupInfo);
3142+
return Ty->isOptional();
3143+
});
3144+
3145+
// FIXME: Currently, it doesn't include top level decls for performance
3146+
// reason. Enabling 'IncludeTopLevel' pulls everything including imported
3147+
// modules. For suggesting top level results, we need a way to filter cached
3148+
// results.
3149+
3150+
lookupVisibleDecls(FilteringConsumer, CurrDeclContext,
3151+
/*IncludeTopLevel=*/false, Loc);
3152+
}

lib/Parse/ParseStmt.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1590,6 +1590,13 @@ Parser::parseStmtConditionElement(SmallVectorImpl<StmtConditionElement> &result,
15901590
ThePattern = makeParserResult(Status, P);
15911591
}
15921592

1593+
} else if (Tok.is(tok::code_complete)) {
1594+
if (CodeCompletion) {
1595+
CodeCompletion->completeOptionalBinding();
1596+
}
1597+
ThePattern = makeParserResult(new (Context) AnyPattern(Tok.getLoc()));
1598+
ThePattern.setHasCodeCompletionAndIsError();
1599+
consumeToken(tok::code_complete);
15931600
} else {
15941601
ConditionCtxt.setCreateSyntax(SyntaxKind::OptionalBindingCondition);
15951602
// Otherwise, this is an implicit optional binding "if let".
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-swift-ide-test -batch-code-completion -source-filename %s -filecheck %raw-FileCheck -completion-output-dir %t
3+
4+
let topLevelOpt: Int?
5+
6+
do {
7+
let topLevelLocalOpt: Int?
8+
let topLevelLocalNonOpt: Int
9+
10+
if let #^TOPLEVEL_IF_LET?check=TOPLEVEL^#
11+
// TOPLEVEL: Begin completions, 1 items
12+
// TOPLEVEL-DAG: Decl[LocalVar]/Local: topLevelLocalOpt[#Int?#];
13+
// TOPLEVEL: End completions
14+
// FIXME: show 'topLevelOpt'
15+
}
16+
17+
struct MyStruct<T> {
18+
var propOpt: Int?
19+
var propNonOpt: Int
20+
var propGenOpt: T?
21+
var propGenNonOpt: T
22+
23+
func testMethod<U>(paramGenOpt: U?, paramGenNonOpt: U, paramOpt: Int?, paramNonOpt: Int) {
24+
var localOpt: Int?
25+
var localNonOpt: Int
26+
var localGenOpt: U?
27+
var localGenNonOpt: U
28+
29+
do {
30+
if let #^IF_LET?check=IN_FUNC^#
31+
}
32+
do {
33+
if var #^IF_VAR?check=IN_FUNC^#
34+
}
35+
do {
36+
if true {} else if let #^ELSEIF_LET?check=IN_FUNC^#
37+
}
38+
do {
39+
if true {} else if var #^ELSEIF_VAR?check=IN_FUNC^#
40+
}
41+
do {
42+
guard let #^GUARD_LET?check=IN_FUNC^#
43+
}
44+
do {
45+
guard var #^GUARD_VAR?check=IN_FUNC^#
46+
}
47+
do {
48+
while let #^WHILE_LET?check=IN_FUNC^#
49+
}
50+
do {
51+
while var #^WHILE_VAR?check=IN_FUNC^#
52+
}
53+
54+
// IN_FUNC: Begin completions, 6 items
55+
// IN_FUNC-DAG: Decl[LocalVar]/Local: localOpt[#Int?#];
56+
// IN_FUNC-DAG: Decl[LocalVar]/Local: localGenOpt[#U?#];
57+
// IN_FUNC-DAG: Decl[LocalVar]/Local: paramGenOpt[#U?#];
58+
// IN_FUNC-DAG: Decl[LocalVar]/Local: paramOpt[#Int?#];
59+
// IN_FUNC-DAG: Decl[InstanceVar]/CurrNominal: propOpt[#Int?#];
60+
// IN_FUNC-DAG: Decl[InstanceVar]/CurrNominal: propGenOpt[#T?#];
61+
// IN_FUNC-NOT: NonOpt
62+
// IN_FUNC: End completions
63+
}
64+
}
65+
66+
func testPreviousElements() {
67+
let localOptOpt: Int??
68+
69+
if let localOpt = localOptOpt, let localNonOpt = localOpt, let #^PREV_ELEMENT^#
70+
// PREV_ELEMENT: Begin completions, 2 items
71+
// PREV_ELEMENT-DAG: Decl[LocalVar]/Local: localOptOpt[#Int??#];
72+
// PREV_ELEMENT-DAG: Decl[LocalVar]/Local: localOpt[#Int?#];
73+
// PREV_ELEMENT-NOT: NonOpt
74+
// PREV_ELEMENT: End completions
75+
}

0 commit comments

Comments
 (0)