Skip to content

[5.1][CodeCompletion] Use unresolved member completion for case stmt beginning #25763

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
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: 0 additions & 1 deletion include/swift/IDE/CodeCompletion.h
Original file line number Diff line number Diff line change
Expand Up @@ -495,7 +495,6 @@ enum class CompletionKind {
TypeIdentifierWithoutDot,
CaseStmtKeyword,
CaseStmtBeginning,
CaseStmtDotPrefix,
NominalMemberBeginning,
AccessorBeginning,
AttributeBegin,
Expand Down
5 changes: 1 addition & 4 deletions include/swift/Parse/CodeCompletionCallbacks.h
Original file line number Diff line number Diff line change
Expand Up @@ -175,10 +175,7 @@ class CodeCompletionCallbacks {
virtual void completeCaseStmtKeyword() {};

/// Complete at the beginning of a case stmt pattern.
virtual void completeCaseStmtBeginning() {};

/// Complete a case stmt pattern that starts with a dot.
virtual void completeCaseStmtDotPrefix() {};
virtual void completeCaseStmtBeginning(CodeCompletionExpr *E) {};

/// Complete at the beginning of member of a nominal decl member -- no tokens
/// provided by user.
Expand Down
88 changes: 8 additions & 80 deletions lib/IDE/CodeCompletion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -338,44 +338,6 @@ std::string swift::ide::removeCodeCompletionTokens(
return CleanFile;
}

namespace {
class StmtFinder : public ASTWalker {
SourceManager &SM;
SourceLoc Loc;
StmtKind Kind;
Stmt *Found = nullptr;

public:
StmtFinder(SourceManager &SM, SourceLoc Loc, StmtKind Kind)
: SM(SM), Loc(Loc), Kind(Kind) {}

std::pair<bool, Stmt *> walkToStmtPre(Stmt *S) override {
return { SM.rangeContainsTokenLoc(S->getSourceRange(), Loc), S };
}

Stmt *walkToStmtPost(Stmt *S) override {
if (S->getKind() == Kind) {
Found = S;
return nullptr;
}
return S;
}

Stmt *getFoundStmt() const {
return Found;
}
};
} // end anonymous namespace

static Stmt *findNearestStmt(const DeclContext *DC, SourceLoc Loc,
StmtKind Kind) {
auto &SM = DC->getASTContext().SourceMgr;
StmtFinder Finder(SM, Loc, Kind);
// FIXME(thread-safety): the walker is mutating the AST.
const_cast<DeclContext *>(DC)->walkContext(Finder);
return Finder.getFoundStmt();
}

CodeCompletionString::CodeCompletionString(ArrayRef<Chunk> Chunks) {
std::uninitialized_copy(Chunks.begin(), Chunks.end(),
getTrailingObjects<Chunk>());
Expand Down Expand Up @@ -1379,8 +1341,7 @@ class CodeCompletionCallbacksImpl : public CodeCompletionCallbacks {
void completeTypeIdentifierWithoutDot(IdentTypeRepr *ITR) override;

void completeCaseStmtKeyword() override;
void completeCaseStmtBeginning() override;
void completeCaseStmtDotPrefix() override;
void completeCaseStmtBeginning(CodeCompletionExpr *E) override;
void completeDeclAttrBeginning(bool Sil, bool isIndependent) override;
void completeDeclAttrParam(DeclAttrKind DK, int Index) override;
void completeInPrecedenceGroup(SyntaxKind SK) override;
Expand Down Expand Up @@ -3911,27 +3872,6 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
}
}

void getTypeContextEnumElementCompletions(SourceLoc Loc) {
llvm::SaveAndRestore<LookupKind> ChangeLookupKind(
Kind, LookupKind::EnumElement);
NeedLeadingDot = !HaveDot;

auto *Switch = cast_or_null<SwitchStmt>(
findNearestStmt(CurrDeclContext, Loc, StmtKind::Switch));
if (!Switch)
return;
auto Ty = Switch->getSubjectExpr()->getType();
if (!Ty)
return;
ExprType = Ty;
auto *TheEnumDecl = dyn_cast_or_null<EnumDecl>(Ty->getAnyNominal());
if (!TheEnumDecl)
return;
for (auto Element : TheEnumDecl->getAllElements()) {
foundDecl(Element, DeclVisibilityKind::MemberOfCurrentNominal, {});
}
}

void getTypeCompletions(Type BaseType) {
Kind = LookupKind::Type;
this->BaseType = BaseType;
Expand Down Expand Up @@ -4726,18 +4666,12 @@ void CodeCompletionCallbacksImpl::completeCaseStmtKeyword() {
CurDeclContext = P.CurDeclContext;
}

void CodeCompletionCallbacksImpl::completeCaseStmtBeginning() {
void CodeCompletionCallbacksImpl::completeCaseStmtBeginning(CodeCompletionExpr *E) {
assert(!InEnumElementRawValue);

Kind = CompletionKind::CaseStmtBeginning;
CurDeclContext = P.CurDeclContext;
}

void CodeCompletionCallbacksImpl::completeCaseStmtDotPrefix() {
assert(!InEnumElementRawValue);

Kind = CompletionKind::CaseStmtDotPrefix;
CurDeclContext = P.CurDeclContext;
CodeCompleteTokenExpr = E;
}

void CodeCompletionCallbacksImpl::completeImportDecl(
Expand Down Expand Up @@ -5023,7 +4957,6 @@ void CodeCompletionCallbacksImpl::addKeywords(CodeCompletionResultSink &Sink,
case CompletionKind::SuperExpr:
case CompletionKind::SuperExprDot:
case CompletionKind::CaseStmtBeginning:
case CompletionKind::CaseStmtDotPrefix:
case CompletionKind::TypeIdentifierWithDot:
case CompletionKind::TypeIdentifierWithoutDot:
break;
Expand Down Expand Up @@ -5423,16 +5356,11 @@ void CodeCompletionCallbacksImpl::doneParsing() {
}

case CompletionKind::CaseStmtBeginning: {
SourceLoc Loc = P.Context.SourceMgr.getCodeCompletionLoc();
Lookup.getValueCompletionsInDeclContext(Loc);
Lookup.getTypeContextEnumElementCompletions(Loc);
break;
}

case CompletionKind::CaseStmtDotPrefix: {
Lookup.setHaveDot(SourceLoc());
SourceLoc Loc = P.Context.SourceMgr.getCodeCompletionLoc();
Lookup.getTypeContextEnumElementCompletions(Loc);
ExprContextInfo ContextInfo(CurDeclContext, CodeCompleteTokenExpr);
Lookup.setExpectedTypes(ContextInfo.getPossibleTypes(),
ContextInfo.isSingleExpressionBody());
Lookup.getUnresolvedMemberCompletions(ContextInfo.getPossibleTypes());
DoPostfixExprBeginning();
break;
}

Expand Down
8 changes: 2 additions & 6 deletions lib/IDE/TypeContextInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ class ContextInfoCallbacks : public CodeCompletionCallbacks {

void completePostfixExprBeginning(CodeCompletionExpr *E) override;
void completeForEachSequenceBeginning(CodeCompletionExpr *E) override;
void completeCaseStmtBeginning() override;
void completeCaseStmtBeginning(CodeCompletionExpr *E) override;

void completeCallArg(CodeCompletionExpr *E, bool isFirst) override;
void completeReturnStmt(CodeCompletionExpr *E) override;
Expand All @@ -46,7 +46,6 @@ class ContextInfoCallbacks : public CodeCompletionCallbacks {

void completeUnresolvedMember(CodeCompletionExpr *E,
SourceLoc DotLoc) override;
void completeCaseStmtDotPrefix() override;

void doneParsing() override;
};
Expand Down Expand Up @@ -81,10 +80,7 @@ void ContextInfoCallbacks::completeUnresolvedMember(CodeCompletionExpr *E,
ParsedExpr = E;
}

void ContextInfoCallbacks::completeCaseStmtBeginning() {
// TODO: Implement?
}
void ContextInfoCallbacks::completeCaseStmtDotPrefix() {
void ContextInfoCallbacks::completeCaseStmtBeginning(CodeCompletionExpr *E) {
// TODO: Implement?
}

Expand Down
20 changes: 5 additions & 15 deletions lib/Parse/ParseStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1081,29 +1081,19 @@ static void parseGuardedPattern(Parser &P, GuardedPattern &result,

// Do some special-case code completion for the start of the pattern.
if (P.Tok.is(tok::code_complete)) {
auto CCE = new (P.Context) CodeCompletionExpr(P.Tok.getLoc());
result.ThePattern = new (P.Context) ExprPattern(CCE);
if (P.CodeCompletion) {
switch (parsingContext) {
case GuardedPatternContext::Case:
P.CodeCompletion->completeCaseStmtBeginning();
P.CodeCompletion->completeCaseStmtBeginning(CCE);
break;
case GuardedPatternContext::Catch:
P.CodeCompletion->completePostfixExprBeginning(nullptr);
P.CodeCompletion->completePostfixExprBeginning(CCE);
break;
}
}
auto loc = P.consumeToken(tok::code_complete);
result.ThePattern = new (P.Context) AnyPattern(loc);
status.setHasCodeCompletion();
return;
}
if (parsingContext == GuardedPatternContext::Case &&
P.Tok.isAny(tok::period_prefix, tok::period) &&
P.peekToken().is(tok::code_complete)) {
P.consumeToken();
if (P.CodeCompletion)
P.CodeCompletion->completeCaseStmtDotPrefix();
auto loc = P.consumeToken(tok::code_complete);
result.ThePattern = new (P.Context) AnyPattern(loc);
P.consumeToken(tok::code_complete);
status.setHasCodeCompletion();
return;
}
Expand Down
16 changes: 9 additions & 7 deletions test/IDE/complete_enum_elements.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
// RUN: %FileCheck %s -check-prefix=FOO_ENUM_DOT < %t.enum.txt

// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=ENUM_SW_IN_PATTERN_1 > %t.enum.txt
// RUN: %FileCheck %s -check-prefix=WITH_GLOBAL_RESULTS_INVALID < %t.enum.txt
// RUN: %FileCheck %s -check-prefix=WITH_GLOBAL_RESULTS < %t.enum.txt
// RUN: %FileCheck %s -check-prefix=ENUM_SW_IN_PATTERN_1 < %t.enum.txt

// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=ENUM_SW_IN_PATTERN_2 > %t.enum.txt
Expand Down Expand Up @@ -85,16 +85,19 @@
enum FooEnum: CaseIterable {
case Foo1
case Foo2
static var alias1: FooEnum { return .Foo1 }
}

// FOO_ENUM_TYPE_CONTEXT: Begin completions
// FOO_ENUM_TYPE_CONTEXT-DAG: Decl[EnumElement]/ExprSpecific: .Foo1[#FooEnum#]{{; name=.+$}}
// FOO_ENUM_TYPE_CONTEXT-DAG: Decl[EnumElement]/ExprSpecific: .Foo2[#FooEnum#]{{; name=.+$}}
// FOO_ENUM_TYPE_CONTEXT-DAG: Decl[StaticVar]/CurrNominal/TypeRelation[Identical]: .alias1[#FooEnum#]; name=alias1
// FOO_ENUM_TYPE_CONTEXT: End completions

// FOO_ENUM_NO_DOT: Begin completions
// FOO_ENUM_NO_DOT-NEXT: Decl[EnumElement]/CurrNominal: .Foo1[#FooEnum#]{{; name=.+$}}
// FOO_ENUM_NO_DOT-NEXT: Decl[EnumElement]/CurrNominal: .Foo2[#FooEnum#]{{; name=.+$}}
// FOO_ENUM_NO_DOT-NEXT: Decl[StaticVar]/CurrNominal: .alias1[#FooEnum#]{{; name=.+$}}
// FOO_ENUM_NO_DOT-NEXT: Decl[InstanceMethod]/CurrNominal: .hash({#(self): FooEnum#})[#(into: inout Hasher) -> Void#]{{; name=.+$}}
// FOO_ENUM_NO_DOT-NEXT: Decl[TypeAlias]/CurrNominal: .AllCases[#[FooEnum]#]{{; name=.+$}}
// FOO_ENUM_NO_DOT-NEXT: Decl[StaticVar]/CurrNominal: .allCases[#[FooEnum]#]{{; name=.+$}}
Expand All @@ -107,6 +110,7 @@ enum FooEnum: CaseIterable {
// FOO_ENUM_DOT-NEXT: Keyword/CurrNominal: Type[#FooEnum.Type#]; name=Type
// FOO_ENUM_DOT-NEXT: Decl[EnumElement]/CurrNominal: Foo1[#FooEnum#]{{; name=.+$}}
// FOO_ENUM_DOT-NEXT: Decl[EnumElement]/CurrNominal: Foo2[#FooEnum#]{{; name=.+$}}
// FOO_ENUM_DOT-NEXT: Decl[StaticVar]/CurrNominal: alias1[#FooEnum#]{{; name=.+$}}
// FOO_ENUM_DOT-NEXT: Decl[InstanceMethod]/CurrNominal: hash({#(self): FooEnum#})[#(into: inout Hasher) -> Void#]{{; name=.+$}}
// FOO_ENUM_DOT-NEXT: Decl[TypeAlias]/CurrNominal: AllCases[#[FooEnum]#]{{; name=.+$}}
// FOO_ENUM_DOT-NEXT: Decl[StaticVar]/CurrNominal: allCases[#[FooEnum]#]{{; name=.+$}}
Expand All @@ -117,14 +121,16 @@ enum FooEnum: CaseIterable {
// FOO_ENUM_DOT_INVALID-NEXT: Keyword/CurrNominal: Type[#FooEnum.Type#]; name=Type
// FOO_ENUM_DOT_INVALID-NEXT: Decl[EnumElement]/CurrNominal: Foo1[#FooEnum#]{{; name=.+$}}
// FOO_ENUM_DOT_INVALID-NEXT: Decl[EnumElement]/CurrNominal: Foo2[#FooEnum#]{{; name=.+$}}
// FOO_ENUM_DOT_INVALID-NEXT: Decl[StaticVar]/CurrNominal: alias1[#FooEnum#]{{; name=.+$}}
// FOO_ENUM_DOT_INVALID-NEXT: Decl[InstanceMethod]/CurrNominal/NotRecommended/TypeRelation[Invalid]: hash({#(self): FooEnum#})[#(into: inout Hasher) -> Void#]{{; name=.+$}}
// FOO_ENUM_DOT_INVALID-NEXT: Decl[TypeAlias]/CurrNominal: AllCases[#[FooEnum]#]{{; name=.+$}}
// FOO_ENUM_DOT_INVALID-NEXT: Decl[StaticVar]/CurrNominal: allCases[#[FooEnum]#]{{; name=.+$}}
// FOO_ENUM_DOT_INVALID-NEXT: End completions

// FOO_ENUM_DOT_ELEMENTS: Begin completions, 2 items
// FOO_ENUM_DOT_ELEMENTS: Begin completions, 3 items
// FOO_ENUM_DOT_ELEMENTS-NEXT: Decl[EnumElement]/ExprSpecific: Foo1[#FooEnum#]{{; name=.+$}}
// FOO_ENUM_DOT_ELEMENTS-NEXT: Decl[EnumElement]/ExprSpecific: Foo2[#FooEnum#]{{; name=.+$}}
// FOO_ENUM_DOT_ELEMENTS-NEXT: Decl[StaticVar]/CurrNominal/TypeRelation[Identical]: alias1[#FooEnum#]; name=alias1
// FOO_ENUM_DOT_ELEMENTS-NEXT: End completions

enum BarEnum {
Expand Down Expand Up @@ -295,13 +301,9 @@ enum QuxEnum : Int {
func freeFunc() {}

// WITH_GLOBAL_RESULTS: Begin completions
// WITH_GLOBAL_RESULTS: Decl[FreeFunction]/CurrModule: freeFunc()[#Void#]{{; name=.+$}}
// WITH_GLOBAL_RESULTS: Decl[FreeFunction]/CurrModule/NotRecommended/TypeRelation[Invalid]: freeFunc()[#Void#]{{; name=.+$}}
// WITH_GLOBAL_RESULTS: End completions

// WITH_GLOBAL_RESULTS_INVALID: Begin completions
// WITH_GLOBAL_RESULTS_INVALID: Decl[FreeFunction]/CurrModule/NotRecommended/TypeRelation[Invalid]: freeFunc()[#Void#]{{; name=.+$}}
// WITH_GLOBAL_RESULTS_INVALID: End completions

//===--- Complete enum elements in 'switch'.

func testSwitch1(e: FooEnum) {
Expand Down
4 changes: 2 additions & 2 deletions test/IDE/complete_value_expr.swift
Original file line number Diff line number Diff line change
Expand Up @@ -637,8 +637,8 @@ func testSwitch1() {
}

// IN_SWITCH_CASE: Begin completions
// IN_SWITCH_CASE-DAG: Decl[GlobalVar]/CurrModule: fooObject[#FooStruct#]{{; name=.+$}}
// IN_SWITCH_CASE-DAG: Decl[Struct]/CurrModule: FooStruct[#FooStruct#]{{; name=.+$}}
// IN_SWITCH_CASE-DAG: Decl[GlobalVar]/CurrModule{{(/TypeRelation\[Identical\])?}}: fooObject[#FooStruct#]{{; name=.+$}}
// IN_SWITCH_CASE-DAG: Decl[Struct]/CurrModule{{(/TypeRelation\[Identical\])?}}: FooStruct[#FooStruct#]{{; name=.+$}}
// IN_SWITCH_CASE: End completions

//===--- Helper types that are used in this test
Expand Down