Skip to content

Commit 2ff55a8

Browse files
authored
Merge pull request #17883 from benlangmuir/cc-if-else-if-42
[4.2] [codecomplete] Add completion of `if` after `else`
2 parents ac67317 + dee5110 commit 2ff55a8

File tree

5 files changed

+87
-51
lines changed

5 files changed

+87
-51
lines changed

include/swift/IDE/CodeCompletion.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -503,6 +503,7 @@ enum class CompletionKind {
503503
ReturnStmtExpr,
504504
ForEachSequence,
505505
AfterPound,
506+
AfterIfStmtElse,
506507
GenericParams,
507508
SwiftKeyPath,
508509
};

include/swift/Parse/CodeCompletionCallbacks.h

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

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

205+
virtual void completeAfterIfStmt(bool hasElse) = 0;
206+
205207
virtual void completeGenericParams(TypeLoc TL) = 0;
206208

207209
/// \brief Signals that the AST for the all the delayed-parsed code was

lib/IDE/CodeCompletion.cpp

Lines changed: 48 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1461,6 +1461,7 @@ class CodeCompletionCallbacksImpl : public CodeCompletionCallbacks {
14611461
void completeReturnStmt(CodeCompletionExpr *E) override;
14621462
void completeAfterPound(CodeCompletionExpr *E, StmtKind ParentKind) override;
14631463
void completeGenericParams(TypeLoc TL) override;
1464+
void completeAfterIfStmt(bool hasElse) override;
14641465
void addKeywords(CodeCompletionResultSink &Sink, bool MaybeFuncBody);
14651466

14661467
void doneParsing() override;
@@ -4844,6 +4845,15 @@ void CodeCompletionCallbacksImpl::completeAfterPound(CodeCompletionExpr *E,
48444845
ParentStmtKind = ParentKind;
48454846
}
48464847

4848+
void CodeCompletionCallbacksImpl::completeAfterIfStmt(bool hasElse) {
4849+
CurDeclContext = P.CurDeclContext;
4850+
if (hasElse) {
4851+
Kind = CompletionKind::AfterIfStmtElse;
4852+
} else {
4853+
Kind = CompletionKind::StmtOrExpr;
4854+
}
4855+
}
4856+
48474857
void CodeCompletionCallbacksImpl::completeGenericParams(TypeLoc TL) {
48484858
CurDeclContext = P.CurDeclContext;
48494859
Kind = CompletionKind::GenericParams;
@@ -4869,8 +4879,20 @@ static bool isClangSubModule(ModuleDecl *TheModule) {
48694879
return false;
48704880
}
48714881

4882+
static void addKeyword(CodeCompletionResultSink &Sink, StringRef Name,
4883+
CodeCompletionKeywordKind Kind,
4884+
StringRef TypeAnnotation = "") {
4885+
CodeCompletionResultBuilder Builder(
4886+
Sink, CodeCompletionResult::ResultKind::Keyword,
4887+
SemanticContextKind::None, {});
4888+
Builder.setKeywordKind(Kind);
4889+
Builder.addTextChunk(Name);
4890+
if (!TypeAnnotation.empty())
4891+
Builder.addTypeAnnotation(TypeAnnotation);
4892+
}
4893+
48724894
static void addDeclKeywords(CodeCompletionResultSink &Sink) {
4873-
auto AddKeyword = [&](StringRef Name, CodeCompletionKeywordKind Kind,
4895+
auto AddDeclKeyword = [&](StringRef Name, CodeCompletionKeywordKind Kind,
48744896
Optional<DeclAttrKind> DAK) {
48754897
if (Name == "let" || Name == "var") {
48764898
// Treat keywords that could be the start of a pattern specially.
@@ -4880,19 +4902,15 @@ static void addDeclKeywords(CodeCompletionResultSink &Sink) {
48804902
// Remove user inaccessible keywords.
48814903
if (DAK.hasValue() && DeclAttribute::isUserInaccessible(*DAK)) return;
48824904

4883-
CodeCompletionResultBuilder Builder(
4884-
Sink, CodeCompletionResult::ResultKind::Keyword,
4885-
SemanticContextKind::None, {});
4886-
Builder.setKeywordKind(Kind);
4887-
Builder.addTextChunk(Name);
4905+
addKeyword(Sink, Name, Kind);
48884906
};
48894907

4890-
#define DECL_KEYWORD(kw) AddKeyword(#kw, CodeCompletionKeywordKind::kw_##kw, None);
4908+
#define DECL_KEYWORD(kw) AddDeclKeyword(#kw, CodeCompletionKeywordKind::kw_##kw, None);
48914909
#include "swift/Syntax/TokenKinds.def"
48924910

48934911
// Context-sensitive keywords.
48944912
auto AddCSKeyword = [&](StringRef Name, DeclAttrKind Kind) {
4895-
AddKeyword(Name, CodeCompletionKeywordKind::None, Kind);
4913+
AddDeclKeyword(Name, CodeCompletionKeywordKind::None, Kind);
48964914
};
48974915

48984916
#define CONTEXTUAL_CASE(KW, CLASS) AddCSKeyword(#KW, DAK_##CLASS);
@@ -4905,68 +4923,40 @@ static void addDeclKeywords(CodeCompletionResultSink &Sink) {
49054923
}
49064924

49074925
static void addStmtKeywords(CodeCompletionResultSink &Sink, bool MaybeFuncBody) {
4908-
auto AddKeyword = [&](StringRef Name, CodeCompletionKeywordKind Kind) {
4926+
auto AddStmtKeyword = [&](StringRef Name, CodeCompletionKeywordKind Kind) {
49094927
if (!MaybeFuncBody && Kind == CodeCompletionKeywordKind::kw_return)
49104928
return;
4911-
4912-
CodeCompletionResultBuilder Builder(
4913-
Sink, CodeCompletionResult::ResultKind::Keyword,
4914-
SemanticContextKind::None, {});
4915-
Builder.setKeywordKind(Kind);
4916-
Builder.addTextChunk(Name);
4929+
addKeyword(Sink, Name, Kind);
49174930
};
4918-
#define STMT_KEYWORD(kw) AddKeyword(#kw, CodeCompletionKeywordKind::kw_##kw);
4931+
#define STMT_KEYWORD(kw) AddStmtKeyword(#kw, CodeCompletionKeywordKind::kw_##kw);
49194932
#include "swift/Syntax/TokenKinds.def"
49204933

49214934
// Throw is not marked as a STMT_KEYWORD.
4922-
AddKeyword("throw", CodeCompletionKeywordKind::kw_throw);
4935+
AddStmtKeyword("throw", CodeCompletionKeywordKind::kw_throw);
49234936
}
49244937

49254938
static void addLetVarKeywords(CodeCompletionResultSink &Sink) {
4926-
auto AddKeyword = [&](StringRef Name, CodeCompletionKeywordKind Kind) {
4927-
CodeCompletionResultBuilder Builder(
4928-
Sink, CodeCompletionResult::ResultKind::Keyword,
4929-
SemanticContextKind::None, {});
4930-
Builder.setKeywordKind(Kind);
4931-
Builder.addTextChunk(Name);
4932-
};
4933-
4934-
AddKeyword("let", CodeCompletionKeywordKind::kw_let);
4935-
AddKeyword("var", CodeCompletionKeywordKind::kw_var);
4939+
addKeyword(Sink, "let", CodeCompletionKeywordKind::kw_let);
4940+
addKeyword(Sink, "var", CodeCompletionKeywordKind::kw_var);
49364941
}
49374942

49384943
static void addExprKeywords(CodeCompletionResultSink &Sink) {
4939-
auto AddKeyword = [&](StringRef Name, StringRef TypeAnnotation, CodeCompletionKeywordKind Kind) {
4940-
CodeCompletionResultBuilder Builder(
4941-
Sink, CodeCompletionResult::ResultKind::Keyword,
4942-
SemanticContextKind::None, {});
4943-
Builder.setKeywordKind(Kind);
4944-
Builder.addTextChunk(Name);
4945-
if (!TypeAnnotation.empty())
4946-
Builder.addTypeAnnotation(TypeAnnotation);
4947-
};
4948-
49494944
// Expr keywords.
4950-
AddKeyword("try", StringRef(), CodeCompletionKeywordKind::kw_try);
4951-
AddKeyword("try!", StringRef(), CodeCompletionKeywordKind::kw_try);
4952-
AddKeyword("try?", StringRef(), CodeCompletionKeywordKind::kw_try);
4945+
addKeyword(Sink, "try", CodeCompletionKeywordKind::kw_try);
4946+
addKeyword(Sink, "try!", CodeCompletionKeywordKind::kw_try);
4947+
addKeyword(Sink, "try?", CodeCompletionKeywordKind::kw_try);
49534948
// FIXME: The pedantically correct way to find the type is to resolve the
49544949
// Swift.StringLiteralType type.
4955-
AddKeyword("#function", "String", CodeCompletionKeywordKind::pound_function);
4956-
AddKeyword("#file", "String", CodeCompletionKeywordKind::pound_file);
4950+
addKeyword(Sink, "#function", CodeCompletionKeywordKind::pound_function, "String");
4951+
addKeyword(Sink, "#file", CodeCompletionKeywordKind::pound_file, "String");
49574952
// Same: Swift.IntegerLiteralType.
4958-
AddKeyword("#line", "Int", CodeCompletionKeywordKind::pound_line);
4959-
AddKeyword("#column", "Int", CodeCompletionKeywordKind::pound_column);
4960-
AddKeyword("#dsohandle", "UnsafeMutableRawPointer", CodeCompletionKeywordKind::pound_dsohandle);
4953+
addKeyword(Sink, "#line", CodeCompletionKeywordKind::pound_line, "Int");
4954+
addKeyword(Sink, "#column", CodeCompletionKeywordKind::pound_column, "Int");
4955+
addKeyword(Sink, "#dsohandle", CodeCompletionKeywordKind::pound_dsohandle, "UnsafeMutableRawPointer");
49614956
}
49624957

49634958
static void addAnyTypeKeyword(CodeCompletionResultSink &Sink) {
4964-
CodeCompletionResultBuilder Builder(
4965-
Sink, CodeCompletionResult::ResultKind::Keyword,
4966-
SemanticContextKind::None, {});
4967-
Builder.setKeywordKind(CodeCompletionKeywordKind::None);
4968-
Builder.addTextChunk("Any");
4969-
Builder.addTypeAnnotation("Any");
4959+
addKeyword(Sink, "Any", CodeCompletionKeywordKind::None, "Any");
49704960
}
49714961

49724962

@@ -5020,6 +5010,10 @@ void CodeCompletionCallbacksImpl::addKeywords(CodeCompletionResultSink &Sink,
50205010
addDeclKeywords(Sink);
50215011
addLetVarKeywords(Sink);
50225012
break;
5013+
5014+
case CompletionKind::AfterIfStmtElse:
5015+
addKeyword(Sink, "if", CodeCompletionKeywordKind::kw_if);
5016+
break;
50235017
}
50245018
}
50255019

@@ -5578,6 +5572,9 @@ void CodeCompletionCallbacksImpl::doneParsing() {
55785572
}
55795573
}
55805574
break;
5575+
case CompletionKind::AfterIfStmtElse:
5576+
// Handled earlier by keyword completions.
5577+
break;
55815578
}
55825579

55835580
if (Lookup.RequestedCachedResults) {

lib/Parse/ParseStmt.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1538,10 +1538,20 @@ ParserResult<Stmt> Parser::parseStmtIf(LabeledStmtInfo LabelInfo) {
15381538
if (Tok.is(tok::kw_if)) {
15391539
SyntaxParsingContext ElseIfCtxt(SyntaxContext, SyntaxKind::IfStmt);
15401540
ElseBody = parseStmtIf(LabeledStmtInfo());
1541+
} else if (Tok.is(tok::code_complete)) {
1542+
if (CodeCompletion)
1543+
CodeCompletion->completeAfterIfStmt(/*hasElse*/true);
1544+
Status.setHasCodeCompletion();
1545+
consumeToken(tok::code_complete);
15411546
} else {
15421547
ElseBody = parseBraceItemList(diag::expected_lbrace_after_else);
15431548
}
15441549
Status |= ElseBody;
1550+
} else if (Tok.is(tok::code_complete)) {
1551+
if (CodeCompletion)
1552+
CodeCompletion->completeAfterIfStmt(/*hasElse*/false);
1553+
Status.setHasCodeCompletion();
1554+
consumeToken(tok::code_complete);
15451555
}
15461556

15471557
return makeParserResult(

test/IDE/complete_keywords.swift

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,18 @@
66
// RUN: %FileCheck %s -check-prefix=KW_DECL_STMT < %t.top2
77
// RUN: %FileCheck %s -check-prefix=KW_NO_RETURN < %t.top2
88

9+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TOP_LEVEL_AFTER_IF_1 > %t.top3
10+
// RUN: %FileCheck %s -check-prefix=KW_DECL_STMT < %t.top3
11+
// RUN: %FileCheck %s -check-prefix=KW_NO_RETURN < %t.top3
12+
13+
// 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
14+
15+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=AFTER_IF_1 > %t.if1
16+
// RUN: %FileCheck %s -check-prefix=KW_DECL_STMT < %t.if1
17+
// RUN: %FileCheck %s -check-prefix=KW_RETURN < %t.if1
18+
19+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=AFTER_IF_ELSE_1 | %FileCheck %s -check-prefix=AFTER_IF_ELSE
20+
921
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=IN_FUNC_BODY_1 > %t.func1
1022
// RUN: %FileCheck %s -check-prefix=KW_DECL_STMT < %t.func1
1123
// RUN: %FileCheck %s -check-prefix=KW_RETURN < %t.func1
@@ -237,6 +249,20 @@ for _ in 1...10 {
237249
#^TOP_LEVEL_2^#
238250
}
239251

252+
if true {} #^TOP_LEVEL_AFTER_IF_1^#
253+
254+
if true {} else #^TOP_LEVEL_AFTER_IF_ELSE_1^# {}
255+
256+
// AFTER_IF_ELSE: Begin completions, 1 items
257+
// AFTER_IF_ELSE: Keyword[if]/None: if;
258+
259+
func testAfterIf1() {
260+
if true {} #^AFTER_IF_1^#
261+
}
262+
func testAfterIfElse1() {
263+
if true {} else #^AFTER_IF_ELSE_1^# {}
264+
}
265+
240266
func testInFuncBody1() {
241267
#^IN_FUNC_BODY_1^#
242268
}

0 commit comments

Comments
 (0)