Skip to content

[codecomplete] Add completion of if after else #17857

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 2 commits into from
Jul 11, 2018
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/CodeCompletion.h
Original file line number Diff line number Diff line change
Expand Up @@ -503,6 +503,7 @@ enum class CompletionKind {
ReturnStmtExpr,
ForEachSequence,
AfterPound,
AfterIfStmtElse,
GenericParams,
SwiftKeyPath,
};
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 @@ -202,6 +202,8 @@ class CodeCompletionCallbacks {

virtual void completeAfterPound(CodeCompletionExpr *E, StmtKind ParentKind) = 0;

virtual void completeAfterIfStmt(bool hasElse) = 0;

virtual void completeGenericParams(TypeLoc TL) = 0;

/// \brief Signals that the AST for the all the delayed-parsed code was
Expand Down
97 changes: 47 additions & 50 deletions lib/IDE/CodeCompletion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1467,6 +1467,7 @@ class CodeCompletionCallbacksImpl : public CodeCompletionCallbacks {
void completeReturnStmt(CodeCompletionExpr *E) override;
void completeAfterPound(CodeCompletionExpr *E, StmtKind ParentKind) override;
void completeGenericParams(TypeLoc TL) override;
void completeAfterIfStmt(bool hasElse) override;
void addKeywords(CodeCompletionResultSink &Sink, bool MaybeFuncBody);

void doneParsing() override;
Expand Down Expand Up @@ -4745,6 +4746,15 @@ void CodeCompletionCallbacksImpl::completeAfterPound(CodeCompletionExpr *E,
ParentStmtKind = ParentKind;
}

void CodeCompletionCallbacksImpl::completeAfterIfStmt(bool hasElse) {
CurDeclContext = P.CurDeclContext;
if (hasElse) {
Kind = CompletionKind::AfterIfStmtElse;
} else {
Kind = CompletionKind::StmtOrExpr;
}
}

void CodeCompletionCallbacksImpl::completeGenericParams(TypeLoc TL) {
CurDeclContext = P.CurDeclContext;
Kind = CompletionKind::GenericParams;
Expand All @@ -4770,8 +4780,20 @@ static bool isClangSubModule(ModuleDecl *TheModule) {
return false;
}

static void addKeyword(CodeCompletionResultSink &Sink, StringRef Name,
CodeCompletionKeywordKind Kind,
StringRef TypeAnnotation = "") {
CodeCompletionResultBuilder Builder(
Sink, CodeCompletionResult::ResultKind::Keyword,
SemanticContextKind::None, {});
Builder.setKeywordKind(Kind);
Builder.addTextChunk(Name);
if (!TypeAnnotation.empty())
Builder.addTypeAnnotation(TypeAnnotation);
}

static void addDeclKeywords(CodeCompletionResultSink &Sink) {
auto AddKeyword = [&](StringRef Name, CodeCompletionKeywordKind Kind,
auto AddDeclKeyword = [&](StringRef Name, CodeCompletionKeywordKind Kind,
Optional<DeclAttrKind> DAK) {
if (Name == "let" || Name == "var") {
// Treat keywords that could be the start of a pattern specially.
Expand All @@ -4781,19 +4803,15 @@ static void addDeclKeywords(CodeCompletionResultSink &Sink) {
// Remove user inaccessible keywords.
if (DAK.hasValue() && DeclAttribute::isUserInaccessible(*DAK)) return;

CodeCompletionResultBuilder Builder(
Sink, CodeCompletionResult::ResultKind::Keyword,
SemanticContextKind::None, {});
Builder.setKeywordKind(Kind);
Builder.addTextChunk(Name);
addKeyword(Sink, Name, Kind);
};

#define DECL_KEYWORD(kw) AddKeyword(#kw, CodeCompletionKeywordKind::kw_##kw, None);
#define DECL_KEYWORD(kw) AddDeclKeyword(#kw, CodeCompletionKeywordKind::kw_##kw, None);
#include "swift/Syntax/TokenKinds.def"

// Context-sensitive keywords.
auto AddCSKeyword = [&](StringRef Name, DeclAttrKind Kind) {
AddKeyword(Name, CodeCompletionKeywordKind::None, Kind);
AddDeclKeyword(Name, CodeCompletionKeywordKind::None, Kind);
};

#define CONTEXTUAL_CASE(KW, CLASS) AddCSKeyword(#KW, DAK_##CLASS);
Expand All @@ -4806,65 +4824,37 @@ static void addDeclKeywords(CodeCompletionResultSink &Sink) {
}

static void addStmtKeywords(CodeCompletionResultSink &Sink, bool MaybeFuncBody) {
auto AddKeyword = [&](StringRef Name, CodeCompletionKeywordKind Kind) {
auto AddStmtKeyword = [&](StringRef Name, CodeCompletionKeywordKind Kind) {
if (!MaybeFuncBody && Kind == CodeCompletionKeywordKind::kw_return)
return;

CodeCompletionResultBuilder Builder(
Sink, CodeCompletionResult::ResultKind::Keyword,
SemanticContextKind::None, {});
Builder.setKeywordKind(Kind);
Builder.addTextChunk(Name);
addKeyword(Sink, Name, Kind);
};
#define STMT_KEYWORD(kw) AddKeyword(#kw, CodeCompletionKeywordKind::kw_##kw);
#define STMT_KEYWORD(kw) AddStmtKeyword(#kw, CodeCompletionKeywordKind::kw_##kw);
#include "swift/Syntax/TokenKinds.def"
}

static void addLetVarKeywords(CodeCompletionResultSink &Sink) {
auto AddKeyword = [&](StringRef Name, CodeCompletionKeywordKind Kind) {
CodeCompletionResultBuilder Builder(
Sink, CodeCompletionResult::ResultKind::Keyword,
SemanticContextKind::None, {});
Builder.setKeywordKind(Kind);
Builder.addTextChunk(Name);
};

AddKeyword("let", CodeCompletionKeywordKind::kw_let);
AddKeyword("var", CodeCompletionKeywordKind::kw_var);
addKeyword(Sink, "let", CodeCompletionKeywordKind::kw_let);
addKeyword(Sink, "var", CodeCompletionKeywordKind::kw_var);
}

static void addExprKeywords(CodeCompletionResultSink &Sink) {
auto AddKeyword = [&](StringRef Name, StringRef TypeAnnotation, CodeCompletionKeywordKind Kind) {
CodeCompletionResultBuilder Builder(
Sink, CodeCompletionResult::ResultKind::Keyword,
SemanticContextKind::None, {});
Builder.setKeywordKind(Kind);
Builder.addTextChunk(Name);
if (!TypeAnnotation.empty())
Builder.addTypeAnnotation(TypeAnnotation);
};

// Expr keywords.
AddKeyword("try", StringRef(), CodeCompletionKeywordKind::kw_try);
AddKeyword("try!", StringRef(), CodeCompletionKeywordKind::kw_try);
AddKeyword("try?", StringRef(), CodeCompletionKeywordKind::kw_try);
addKeyword(Sink, "try", CodeCompletionKeywordKind::kw_try);
addKeyword(Sink, "try!", CodeCompletionKeywordKind::kw_try);
addKeyword(Sink, "try?", CodeCompletionKeywordKind::kw_try);
// FIXME: The pedantically correct way to find the type is to resolve the
// Swift.StringLiteralType type.
AddKeyword("#function", "String", CodeCompletionKeywordKind::pound_function);
AddKeyword("#file", "String", CodeCompletionKeywordKind::pound_file);
addKeyword(Sink, "#function", CodeCompletionKeywordKind::pound_function, "String");
addKeyword(Sink, "#file", CodeCompletionKeywordKind::pound_file, "String");
// Same: Swift.IntegerLiteralType.
AddKeyword("#line", "Int", CodeCompletionKeywordKind::pound_line);
AddKeyword("#column", "Int", CodeCompletionKeywordKind::pound_column);
AddKeyword("#dsohandle", "UnsafeMutableRawPointer", CodeCompletionKeywordKind::pound_dsohandle);
addKeyword(Sink, "#line", CodeCompletionKeywordKind::pound_line, "Int");
addKeyword(Sink, "#column", CodeCompletionKeywordKind::pound_column, "Int");
addKeyword(Sink, "#dsohandle", CodeCompletionKeywordKind::pound_dsohandle, "UnsafeMutableRawPointer");
}

static void addAnyTypeKeyword(CodeCompletionResultSink &Sink) {
CodeCompletionResultBuilder Builder(
Sink, CodeCompletionResult::ResultKind::Keyword,
SemanticContextKind::None, {});
Builder.setKeywordKind(CodeCompletionKeywordKind::None);
Builder.addTextChunk("Any");
Builder.addTypeAnnotation("Any");
addKeyword(Sink, "Any", CodeCompletionKeywordKind::None, "Any");
}


Expand Down Expand Up @@ -4918,6 +4908,10 @@ void CodeCompletionCallbacksImpl::addKeywords(CodeCompletionResultSink &Sink,
addDeclKeywords(Sink);
addLetVarKeywords(Sink);
break;

case CompletionKind::AfterIfStmtElse:
addKeyword(Sink, "if", CodeCompletionKeywordKind::kw_if);
break;
}
}

Expand Down Expand Up @@ -5494,6 +5488,9 @@ void CodeCompletionCallbacksImpl::doneParsing() {
}
}
break;
case CompletionKind::AfterIfStmtElse:
// Handled earlier by keyword completions.
break;
}

if (Lookup.RequestedCachedResults) {
Expand Down
10 changes: 10 additions & 0 deletions lib/Parse/ParseStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1545,10 +1545,20 @@ ParserResult<Stmt> Parser::parseStmtIf(LabeledStmtInfo LabelInfo) {
if (Tok.is(tok::kw_if)) {
SyntaxParsingContext ElseIfCtxt(SyntaxContext, SyntaxKind::IfStmt);
ElseBody = parseStmtIf(LabeledStmtInfo());
} else if (Tok.is(tok::code_complete)) {
if (CodeCompletion)
CodeCompletion->completeAfterIfStmt(/*hasElse*/true);
Status.setHasCodeCompletion();
consumeToken(tok::code_complete);
} else {
ElseBody = parseBraceItemList(diag::expected_lbrace_after_else);
}
Status |= ElseBody;
} else if (Tok.is(tok::code_complete)) {
if (CodeCompletion)
CodeCompletion->completeAfterIfStmt(/*hasElse*/false);
Status.setHasCodeCompletion();
consumeToken(tok::code_complete);
}

return makeParserResult(
Expand Down
26 changes: 26 additions & 0 deletions test/IDE/complete_keywords.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,18 @@
// RUN: %FileCheck %s -check-prefix=KW_DECL_STMT < %t.top2
// RUN: %FileCheck %s -check-prefix=KW_NO_RETURN < %t.top2

// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TOP_LEVEL_AFTER_IF_1 > %t.top3
// RUN: %FileCheck %s -check-prefix=KW_DECL_STMT < %t.top3
// RUN: %FileCheck %s -check-prefix=KW_NO_RETURN < %t.top3

// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TOP_LEVEL_AFTER_IF_ELSE_1 | %FileCheck %s -check-prefix=AFTER_IF_ELSE

// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=AFTER_IF_1 > %t.if1
// RUN: %FileCheck %s -check-prefix=KW_DECL_STMT < %t.if1
// RUN: %FileCheck %s -check-prefix=KW_RETURN < %t.if1

// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=AFTER_IF_ELSE_1 | %FileCheck %s -check-prefix=AFTER_IF_ELSE

// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=IN_FUNC_BODY_1 > %t.func1
// RUN: %FileCheck %s -check-prefix=KW_DECL_STMT < %t.func1
// RUN: %FileCheck %s -check-prefix=KW_RETURN < %t.func1
Expand Down Expand Up @@ -237,6 +249,20 @@ for _ in 1...10 {
#^TOP_LEVEL_2^#
}

if true {} #^TOP_LEVEL_AFTER_IF_1^#

if true {} else #^TOP_LEVEL_AFTER_IF_ELSE_1^# {}

// AFTER_IF_ELSE: Begin completions, 1 items
// AFTER_IF_ELSE: Keyword[if]/None: if;

func testAfterIf1() {
if true {} #^AFTER_IF_1^#
}
func testAfterIfElse1() {
if true {} else #^AFTER_IF_ELSE_1^# {}
}

func testInFuncBody1() {
#^IN_FUNC_BODY_1^#
}
Expand Down