Skip to content

[5.7][CodeCompletion] Update for SE-0345 shorthand optional binding #42224

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions include/swift/IDE/CodeCompletionResult.h
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,7 @@ enum class CompletionKind : uint8_t {
StmtLabel,
ForEachPatternBeginning,
TypeAttrBeginning,
OptionalBinding,
};

enum class CodeCompletionDiagnosticSeverity : uint8_t {
Expand Down
13 changes: 9 additions & 4 deletions include/swift/IDE/CompletionLookup.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,15 @@
namespace swift {
namespace ide {

using DeclFilter = std::function<bool(ValueDecl *, DeclVisibilityKind)>;
using DeclFilter =
std::function<bool(ValueDecl *, DeclVisibilityKind, DynamicLookupInfo)>;

/// A filter that always returns \c true.
bool DefaultFilter(ValueDecl *VD, DeclVisibilityKind Kind);
bool DefaultFilter(ValueDecl *VD, DeclVisibilityKind Kind,
DynamicLookupInfo dynamicLookupInfo);

bool KeyPathFilter(ValueDecl *decl, DeclVisibilityKind);
bool KeyPathFilter(ValueDecl *decl, DeclVisibilityKind,
DynamicLookupInfo dynamicLookupInfo);

/// Returns \c true only if the completion is happening for top-level
/// declrarations. i.e.:
Expand Down Expand Up @@ -528,7 +531,7 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
: Consumer(Consumer), Filter(Filter) {}
void foundDecl(ValueDecl *VD, DeclVisibilityKind Kind,
DynamicLookupInfo dynamicLookupInfo) override {
if (Filter(VD, Kind))
if (Filter(VD, Kind, dynamicLookupInfo))
Consumer.foundDecl(VD, Kind, dynamicLookupInfo);
}
};
Expand Down Expand Up @@ -588,6 +591,8 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
bool ResultsHaveLeadingDot);

void getStmtLabelCompletions(SourceLoc Loc, bool isContinue);

void getOptionalBindingCompletions(SourceLoc Loc);
};

} // end namespace ide
Expand Down
2 changes: 2 additions & 0 deletions include/swift/Parse/CodeCompletionCallbacks.h
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,8 @@ class CodeCompletionCallbacks {

virtual void completeTypeAttrBeginning() {};

virtual void completeOptionalBinding(){};

/// Signals that the AST for the all the delayed-parsed code was
/// constructed. No \c complete*() callbacks will be done after this.
virtual void doneParsing() = 0;
Expand Down
13 changes: 13 additions & 0 deletions lib/IDE/CodeCompletion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,7 @@ class CodeCompletionCallbacksImpl : public CodeCompletionCallbacks {
void completeStmtLabel(StmtKind ParentKind) override;
void completeForEachPatternBeginning(bool hasTry, bool hasAwait) override;
void completeTypeAttrBeginning() override;
void completeOptionalBinding() override;

void doneParsing() override;

Expand Down Expand Up @@ -625,6 +626,11 @@ void CodeCompletionCallbacksImpl::completeForEachPatternBeginning(
ParsedKeywords.emplace_back("await");
}

void CodeCompletionCallbacksImpl::completeOptionalBinding() {
CurDeclContext = P.CurDeclContext;
Kind = CompletionKind::OptionalBinding;
}

void CodeCompletionCallbacksImpl::completeTypeAttrBeginning() {
CurDeclContext = P.CurDeclContext;
Kind = CompletionKind::TypeAttrBeginning;
Expand Down Expand Up @@ -907,6 +913,7 @@ void CodeCompletionCallbacksImpl::addKeywords(CodeCompletionResultSink &Sink,
case CompletionKind::PrecedenceGroup:
case CompletionKind::StmtLabel:
case CompletionKind::TypeAttrBeginning:
case CompletionKind::OptionalBinding:
break;

case CompletionKind::EffectsSpecifier: {
Expand Down Expand Up @@ -1886,6 +1893,12 @@ void CodeCompletionCallbacksImpl::doneParsing() {
break;

}
case CompletionKind::OptionalBinding: {
SourceLoc Loc = P.Context.SourceMgr.getCodeCompletionLoc();
Lookup.getOptionalBindingCompletions(Loc);
break;
}

case CompletionKind::AfterIfStmtElse:
case CompletionKind::CaseStmtKeyword:
case CompletionKind::EffectsSpecifier:
Expand Down
40 changes: 36 additions & 4 deletions lib/IDE/CompletionLookup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -119,11 +119,13 @@ static bool hasTrivialTrailingClosure(const FuncDecl *FD,
}
} // end anonymous namespace

bool swift::ide::DefaultFilter(ValueDecl *VD, DeclVisibilityKind Kind) {
bool swift::ide::DefaultFilter(ValueDecl *VD, DeclVisibilityKind Kind,
DynamicLookupInfo dynamicLookupInfo) {
return true;
}

bool swift::ide::KeyPathFilter(ValueDecl *decl, DeclVisibilityKind) {
bool swift::ide::KeyPathFilter(ValueDecl *decl, DeclVisibilityKind,
DynamicLookupInfo dynamicLookupInfo) {
return isa<TypeDecl>(decl) ||
(isa<VarDecl>(decl) && decl->getDeclContext()->isTypeContext());
}
Expand Down Expand Up @@ -1799,7 +1801,7 @@ void CompletionLookup::foundDecl(ValueDecl *D, DeclVisibilityKind Reason,
if (D->shouldHideFromEditor())
return;

if (IsKeyPathExpr && !KeyPathFilter(D, Reason))
if (IsKeyPathExpr && !KeyPathFilter(D, Reason, dynamicLookupInfo))
return;

if (IsSwiftKeyPathExpr && !SwiftKeyPathFilter(D, Reason))
Expand Down Expand Up @@ -2695,7 +2697,8 @@ void CompletionLookup::getUnresolvedMemberCompletions(Type T) {
// type and has the same type (or if the member is a function, then the
// same result type) as the contextual type.
FilteredDeclConsumer consumer(*this,
[=](ValueDecl *VD, DeclVisibilityKind Reason) {
[=](ValueDecl *VD, DeclVisibilityKind Reason,
DynamicLookupInfo dynamicLookupInfo) {
// In optional context, ignore
// '.init(<some>)', 'init(nilLiteral:)',
return !isInitializerOnOptional(T, VD);
Expand Down Expand Up @@ -3118,3 +3121,32 @@ void CompletionLookup::getStmtLabelCompletions(SourceLoc Loc, bool isContinue) {
Builder.addTextChunk(name.str());
}
}

void CompletionLookup::getOptionalBindingCompletions(SourceLoc Loc) {
ExprType = Type();
Kind = LookupKind::ValueInDeclContext;
NeedLeadingDot = false;

AccessFilteringDeclConsumer AccessFilteringConsumer(CurrDeclContext, *this);

// Suggest only 'Optional' type var decls (incl. parameters)
FilteredDeclConsumer FilteringConsumer(
AccessFilteringConsumer,
[&](ValueDecl *VD, DeclVisibilityKind Reason,
DynamicLookupInfo dynamicLookupInfo) -> bool {
auto *VarD = dyn_cast<VarDecl>(VD);
if (!VarD)
return false;

auto Ty = getTypeOfMember(VD, dynamicLookupInfo);
return Ty->isOptional();
});

// FIXME: Currently, it doesn't include top level decls for performance
// reason. Enabling 'IncludeTopLevel' pulls everything including imported
// modules. For suggesting top level results, we need a way to filter cached
// results.

lookupVisibleDecls(FilteringConsumer, CurrDeclContext,
/*IncludeTopLevel=*/false, Loc);
}
7 changes: 7 additions & 0 deletions lib/Parse/ParseStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1590,6 +1590,13 @@ Parser::parseStmtConditionElement(SmallVectorImpl<StmtConditionElement> &result,
ThePattern = makeParserResult(Status, P);
}

} else if (Tok.is(tok::code_complete)) {
if (CodeCompletion) {
CodeCompletion->completeOptionalBinding();
}
ThePattern = makeParserResult(new (Context) AnyPattern(Tok.getLoc()));
ThePattern.setHasCodeCompletionAndIsError();
consumeToken(tok::code_complete);
} else {
ConditionCtxt.setCreateSyntax(SyntaxKind::OptionalBindingCondition);
// Otherwise, this is an implicit optional binding "if let".
Expand Down
75 changes: 75 additions & 0 deletions test/IDE/complete_optional_binding.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// RUN: %empty-directory(%t)
// RUN: %target-swift-ide-test -batch-code-completion -source-filename %s -filecheck %raw-FileCheck -completion-output-dir %t

let topLevelOpt: Int?

do {
let topLevelLocalOpt: Int?
let topLevelLocalNonOpt: Int

if let #^TOPLEVEL_IF_LET?check=TOPLEVEL^#
// TOPLEVEL: Begin completions, 1 items
// TOPLEVEL-DAG: Decl[LocalVar]/Local: topLevelLocalOpt[#Int?#];
// TOPLEVEL: End completions
// FIXME: show 'topLevelOpt'
}

struct MyStruct<T> {
var propOpt: Int?
var propNonOpt: Int
var propGenOpt: T?
var propGenNonOpt: T

func testMethod<U>(paramGenOpt: U?, paramGenNonOpt: U, paramOpt: Int?, paramNonOpt: Int) {
var localOpt: Int?
var localNonOpt: Int
var localGenOpt: U?
var localGenNonOpt: U

do {
if let #^IF_LET?check=IN_FUNC^#
}
do {
if var #^IF_VAR?check=IN_FUNC^#
}
do {
if true {} else if let #^ELSEIF_LET?check=IN_FUNC^#
}
do {
if true {} else if var #^ELSEIF_VAR?check=IN_FUNC^#
}
do {
guard let #^GUARD_LET?check=IN_FUNC^#
}
do {
guard var #^GUARD_VAR?check=IN_FUNC^#
}
do {
while let #^WHILE_LET?check=IN_FUNC^#
}
do {
while var #^WHILE_VAR?check=IN_FUNC^#
}

// IN_FUNC: Begin completions, 6 items
// IN_FUNC-DAG: Decl[LocalVar]/Local: localOpt[#Int?#];
// IN_FUNC-DAG: Decl[LocalVar]/Local: localGenOpt[#U?#];
// IN_FUNC-DAG: Decl[LocalVar]/Local: paramGenOpt[#U?#];
// IN_FUNC-DAG: Decl[LocalVar]/Local: paramOpt[#Int?#];
// IN_FUNC-DAG: Decl[InstanceVar]/CurrNominal: propOpt[#Int?#];
// IN_FUNC-DAG: Decl[InstanceVar]/CurrNominal: propGenOpt[#T?#];
// IN_FUNC-NOT: NonOpt
// IN_FUNC: End completions
}
}

func testPreviousElements() {
let localOptOpt: Int??

if let localOpt = localOptOpt, let localNonOpt = localOpt, let #^PREV_ELEMENT^#
// PREV_ELEMENT: Begin completions, 2 items
// PREV_ELEMENT-DAG: Decl[LocalVar]/Local: localOptOpt[#Int??#];
// PREV_ELEMENT-DAG: Decl[LocalVar]/Local: localOpt[#Int?#];
// PREV_ELEMENT-NOT: NonOpt
// PREV_ELEMENT: End completions
}