Skip to content

Commit 62d93ae

Browse files
authored
Merge pull request #17857 from benlangmuir/cc-if-else-if
[codecomplete] Add completion of `if` after `else`
2 parents ec3f0d3 + b394e35 commit 62d93ae

File tree

5 files changed

+86
-50
lines changed

5 files changed

+86
-50
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: 47 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1467,6 +1467,7 @@ class CodeCompletionCallbacksImpl : public CodeCompletionCallbacks {
14671467
void completeReturnStmt(CodeCompletionExpr *E) override;
14681468
void completeAfterPound(CodeCompletionExpr *E, StmtKind ParentKind) override;
14691469
void completeGenericParams(TypeLoc TL) override;
1470+
void completeAfterIfStmt(bool hasElse) override;
14701471
void addKeywords(CodeCompletionResultSink &Sink, bool MaybeFuncBody);
14711472

14721473
void doneParsing() override;
@@ -4745,6 +4746,15 @@ void CodeCompletionCallbacksImpl::completeAfterPound(CodeCompletionExpr *E,
47454746
ParentStmtKind = ParentKind;
47464747
}
47474748

4749+
void CodeCompletionCallbacksImpl::completeAfterIfStmt(bool hasElse) {
4750+
CurDeclContext = P.CurDeclContext;
4751+
if (hasElse) {
4752+
Kind = CompletionKind::AfterIfStmtElse;
4753+
} else {
4754+
Kind = CompletionKind::StmtOrExpr;
4755+
}
4756+
}
4757+
47484758
void CodeCompletionCallbacksImpl::completeGenericParams(TypeLoc TL) {
47494759
CurDeclContext = P.CurDeclContext;
47504760
Kind = CompletionKind::GenericParams;
@@ -4770,8 +4780,20 @@ static bool isClangSubModule(ModuleDecl *TheModule) {
47704780
return false;
47714781
}
47724782

4783+
static void addKeyword(CodeCompletionResultSink &Sink, StringRef Name,
4784+
CodeCompletionKeywordKind Kind,
4785+
StringRef TypeAnnotation = "") {
4786+
CodeCompletionResultBuilder Builder(
4787+
Sink, CodeCompletionResult::ResultKind::Keyword,
4788+
SemanticContextKind::None, {});
4789+
Builder.setKeywordKind(Kind);
4790+
Builder.addTextChunk(Name);
4791+
if (!TypeAnnotation.empty())
4792+
Builder.addTypeAnnotation(TypeAnnotation);
4793+
}
4794+
47734795
static void addDeclKeywords(CodeCompletionResultSink &Sink) {
4774-
auto AddKeyword = [&](StringRef Name, CodeCompletionKeywordKind Kind,
4796+
auto AddDeclKeyword = [&](StringRef Name, CodeCompletionKeywordKind Kind,
47754797
Optional<DeclAttrKind> DAK) {
47764798
if (Name == "let" || Name == "var") {
47774799
// Treat keywords that could be the start of a pattern specially.
@@ -4781,19 +4803,15 @@ static void addDeclKeywords(CodeCompletionResultSink &Sink) {
47814803
// Remove user inaccessible keywords.
47824804
if (DAK.hasValue() && DeclAttribute::isUserInaccessible(*DAK)) return;
47834805

4784-
CodeCompletionResultBuilder Builder(
4785-
Sink, CodeCompletionResult::ResultKind::Keyword,
4786-
SemanticContextKind::None, {});
4787-
Builder.setKeywordKind(Kind);
4788-
Builder.addTextChunk(Name);
4806+
addKeyword(Sink, Name, Kind);
47894807
};
47904808

4791-
#define DECL_KEYWORD(kw) AddKeyword(#kw, CodeCompletionKeywordKind::kw_##kw, None);
4809+
#define DECL_KEYWORD(kw) AddDeclKeyword(#kw, CodeCompletionKeywordKind::kw_##kw, None);
47924810
#include "swift/Syntax/TokenKinds.def"
47934811

47944812
// Context-sensitive keywords.
47954813
auto AddCSKeyword = [&](StringRef Name, DeclAttrKind Kind) {
4796-
AddKeyword(Name, CodeCompletionKeywordKind::None, Kind);
4814+
AddDeclKeyword(Name, CodeCompletionKeywordKind::None, Kind);
47974815
};
47984816

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

48084826
static void addStmtKeywords(CodeCompletionResultSink &Sink, bool MaybeFuncBody) {
4809-
auto AddKeyword = [&](StringRef Name, CodeCompletionKeywordKind Kind) {
4827+
auto AddStmtKeyword = [&](StringRef Name, CodeCompletionKeywordKind Kind) {
48104828
if (!MaybeFuncBody && Kind == CodeCompletionKeywordKind::kw_return)
48114829
return;
4812-
4813-
CodeCompletionResultBuilder Builder(
4814-
Sink, CodeCompletionResult::ResultKind::Keyword,
4815-
SemanticContextKind::None, {});
4816-
Builder.setKeywordKind(Kind);
4817-
Builder.addTextChunk(Name);
4830+
addKeyword(Sink, Name, Kind);
48184831
};
4819-
#define STMT_KEYWORD(kw) AddKeyword(#kw, CodeCompletionKeywordKind::kw_##kw);
4832+
#define STMT_KEYWORD(kw) AddStmtKeyword(#kw, CodeCompletionKeywordKind::kw_##kw);
48204833
#include "swift/Syntax/TokenKinds.def"
48214834
}
48224835

48234836
static void addLetVarKeywords(CodeCompletionResultSink &Sink) {
4824-
auto AddKeyword = [&](StringRef Name, CodeCompletionKeywordKind Kind) {
4825-
CodeCompletionResultBuilder Builder(
4826-
Sink, CodeCompletionResult::ResultKind::Keyword,
4827-
SemanticContextKind::None, {});
4828-
Builder.setKeywordKind(Kind);
4829-
Builder.addTextChunk(Name);
4830-
};
4831-
4832-
AddKeyword("let", CodeCompletionKeywordKind::kw_let);
4833-
AddKeyword("var", CodeCompletionKeywordKind::kw_var);
4837+
addKeyword(Sink, "let", CodeCompletionKeywordKind::kw_let);
4838+
addKeyword(Sink, "var", CodeCompletionKeywordKind::kw_var);
48344839
}
48354840

48364841
static void addExprKeywords(CodeCompletionResultSink &Sink) {
4837-
auto AddKeyword = [&](StringRef Name, StringRef TypeAnnotation, CodeCompletionKeywordKind Kind) {
4838-
CodeCompletionResultBuilder Builder(
4839-
Sink, CodeCompletionResult::ResultKind::Keyword,
4840-
SemanticContextKind::None, {});
4841-
Builder.setKeywordKind(Kind);
4842-
Builder.addTextChunk(Name);
4843-
if (!TypeAnnotation.empty())
4844-
Builder.addTypeAnnotation(TypeAnnotation);
4845-
};
4846-
48474842
// Expr keywords.
4848-
AddKeyword("try", StringRef(), CodeCompletionKeywordKind::kw_try);
4849-
AddKeyword("try!", StringRef(), CodeCompletionKeywordKind::kw_try);
4850-
AddKeyword("try?", StringRef(), CodeCompletionKeywordKind::kw_try);
4843+
addKeyword(Sink, "try", CodeCompletionKeywordKind::kw_try);
4844+
addKeyword(Sink, "try!", CodeCompletionKeywordKind::kw_try);
4845+
addKeyword(Sink, "try?", CodeCompletionKeywordKind::kw_try);
48514846
// FIXME: The pedantically correct way to find the type is to resolve the
48524847
// Swift.StringLiteralType type.
4853-
AddKeyword("#function", "String", CodeCompletionKeywordKind::pound_function);
4854-
AddKeyword("#file", "String", CodeCompletionKeywordKind::pound_file);
4848+
addKeyword(Sink, "#function", CodeCompletionKeywordKind::pound_function, "String");
4849+
addKeyword(Sink, "#file", CodeCompletionKeywordKind::pound_file, "String");
48554850
// Same: Swift.IntegerLiteralType.
4856-
AddKeyword("#line", "Int", CodeCompletionKeywordKind::pound_line);
4857-
AddKeyword("#column", "Int", CodeCompletionKeywordKind::pound_column);
4858-
AddKeyword("#dsohandle", "UnsafeMutableRawPointer", CodeCompletionKeywordKind::pound_dsohandle);
4851+
addKeyword(Sink, "#line", CodeCompletionKeywordKind::pound_line, "Int");
4852+
addKeyword(Sink, "#column", CodeCompletionKeywordKind::pound_column, "Int");
4853+
addKeyword(Sink, "#dsohandle", CodeCompletionKeywordKind::pound_dsohandle, "UnsafeMutableRawPointer");
48594854
}
48604855

48614856
static void addAnyTypeKeyword(CodeCompletionResultSink &Sink) {
4862-
CodeCompletionResultBuilder Builder(
4863-
Sink, CodeCompletionResult::ResultKind::Keyword,
4864-
SemanticContextKind::None, {});
4865-
Builder.setKeywordKind(CodeCompletionKeywordKind::None);
4866-
Builder.addTextChunk("Any");
4867-
Builder.addTypeAnnotation("Any");
4857+
addKeyword(Sink, "Any", CodeCompletionKeywordKind::None, "Any");
48684858
}
48694859

48704860

@@ -4918,6 +4908,10 @@ void CodeCompletionCallbacksImpl::addKeywords(CodeCompletionResultSink &Sink,
49184908
addDeclKeywords(Sink);
49194909
addLetVarKeywords(Sink);
49204910
break;
4911+
4912+
case CompletionKind::AfterIfStmtElse:
4913+
addKeyword(Sink, "if", CodeCompletionKeywordKind::kw_if);
4914+
break;
49214915
}
49224916
}
49234917

@@ -5494,6 +5488,9 @@ void CodeCompletionCallbacksImpl::doneParsing() {
54945488
}
54955489
}
54965490
break;
5491+
case CompletionKind::AfterIfStmtElse:
5492+
// Handled earlier by keyword completions.
5493+
break;
54975494
}
54985495

54995496
if (Lookup.RequestedCachedResults) {

lib/Parse/ParseStmt.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1545,10 +1545,20 @@ ParserResult<Stmt> Parser::parseStmtIf(LabeledStmtInfo LabelInfo) {
15451545
if (Tok.is(tok::kw_if)) {
15461546
SyntaxParsingContext ElseIfCtxt(SyntaxContext, SyntaxKind::IfStmt);
15471547
ElseBody = parseStmtIf(LabeledStmtInfo());
1548+
} else if (Tok.is(tok::code_complete)) {
1549+
if (CodeCompletion)
1550+
CodeCompletion->completeAfterIfStmt(/*hasElse*/true);
1551+
Status.setHasCodeCompletion();
1552+
consumeToken(tok::code_complete);
15481553
} else {
15491554
ElseBody = parseBraceItemList(diag::expected_lbrace_after_else);
15501555
}
15511556
Status |= ElseBody;
1557+
} else if (Tok.is(tok::code_complete)) {
1558+
if (CodeCompletion)
1559+
CodeCompletion->completeAfterIfStmt(/*hasElse*/false);
1560+
Status.setHasCodeCompletion();
1561+
consumeToken(tok::code_complete);
15521562
}
15531563

15541564
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)