Skip to content

Commit 3e8278a

Browse files
author
Nathan Hawes
authored
Merge pull request swiftlang#33676 from nathawes/parser-completion-fixes
[Parse][IDE] Various parser fixes for code completion
2 parents 73f4273 + 8980356 commit 3e8278a

File tree

9 files changed

+99
-83
lines changed

9 files changed

+99
-83
lines changed

include/swift/Parse/Parser.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1133,8 +1133,7 @@ class Parser {
11331133
ParseDeclOptions Flags,
11341134
DeclAttributes &Attributes,
11351135
bool HasFuncKeyword = true);
1136-
ParserResult<BraceStmt>
1137-
parseAbstractFunctionBodyImpl(AbstractFunctionDecl *AFD);
1136+
BraceStmt *parseAbstractFunctionBodyImpl(AbstractFunctionDecl *AFD);
11381137
void parseAbstractFunctionBody(AbstractFunctionDecl *AFD);
11391138
BraceStmt *parseAbstractFunctionBodyDelayed(AbstractFunctionDecl *AFD);
11401139
ParserResult<ProtocolDecl> parseDeclProtocol(ParseDeclOptions Flags,

lib/IDE/ExprContextAnalysis.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,11 @@ void swift::ide::typeCheckContextAt(DeclContext *DC, SourceLoc Loc) {
101101
typeCheckPatternBinding(PBD, i);
102102
}
103103
}
104+
} else if (auto *defaultArg = dyn_cast<DefaultArgumentInitializer>(DC)) {
105+
if (auto *AFD = dyn_cast<AbstractFunctionDecl>(defaultArg->getParent())) {
106+
auto *Param = AFD->getParameters()->get(defaultArg->getIndex());
107+
(void*)Param->getTypeCheckedDefaultExpr();
108+
}
104109
}
105110
break;
106111

lib/Parse/ParseDecl.cpp

Lines changed: 66 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -6442,10 +6442,9 @@ ParserResult<FuncDecl> Parser::parseDeclFunc(SourceLoc StaticLoc,
64426442
return DCC.fixupParserResult(FD);
64436443
}
64446444

6445-
/// Parse a function body for \p AFD and returns it without setting the body
6446-
/// to \p AFD .
6447-
ParserResult<BraceStmt>
6448-
Parser::parseAbstractFunctionBodyImpl(AbstractFunctionDecl *AFD) {
6445+
/// Parse a function body for \p AFD, setting the body to \p AFD before
6446+
/// returning it.
6447+
BraceStmt *Parser::parseAbstractFunctionBodyImpl(AbstractFunctionDecl *AFD) {
64496448
assert(Tok.is(tok::l_brace));
64506449

64516450
// Enter the arguments for the function into a new function-body scope. We
@@ -6473,13 +6472,70 @@ Parser::parseAbstractFunctionBodyImpl(AbstractFunctionDecl *AFD) {
64736472
CodeCompletion->completeAccessorBeginning(CCE);
64746473
RBraceLoc = Tok.getLoc();
64756474
consumeToken(tok::code_complete);
6476-
return makeParserCodeCompletionResult(
6477-
BraceStmt::create(Context, LBraceLoc, ASTNode(CCE), RBraceLoc,
6478-
/*implicit*/ true));
6475+
auto *BS = BraceStmt::create(Context, LBraceLoc, ASTNode(CCE), RBraceLoc,
6476+
/*implicit*/ true);
6477+
AFD->setBodyParsed(BS);
6478+
return BS;
64796479
}
64806480
}
64816481

6482-
return parseBraceItemList(diag::invalid_diagnostic);
6482+
ParserResult<BraceStmt> Body = parseBraceItemList(diag::invalid_diagnostic);
6483+
if (Body.isNull())
6484+
return nullptr;
6485+
6486+
BraceStmt *BS = Body.get();
6487+
AFD->setBodyParsed(BS);
6488+
6489+
// If the body consists of a single expression, turn it into a return
6490+
// statement.
6491+
//
6492+
// But don't do this transformation during code completion, as the source
6493+
// may be incomplete and the type mismatch in return statement will just
6494+
// confuse the type checker.
6495+
if (BS->getNumElements() != 1 || Body.hasCodeCompletion())
6496+
return BS;
6497+
6498+
auto Element = BS->getFirstElement();
6499+
if (auto *stmt = Element.dyn_cast<Stmt *>()) {
6500+
if (isa<FuncDecl>(AFD)) {
6501+
if (auto *returnStmt = dyn_cast<ReturnStmt>(stmt)) {
6502+
if (!returnStmt->hasResult()) {
6503+
auto returnExpr = TupleExpr::createEmpty(Context,
6504+
SourceLoc(),
6505+
SourceLoc(),
6506+
/*implicit*/true);
6507+
returnStmt->setResult(returnExpr);
6508+
AFD->setHasSingleExpressionBody();
6509+
AFD->setSingleExpressionBody(returnExpr);
6510+
}
6511+
}
6512+
}
6513+
} else if (auto *E = Element.dyn_cast<Expr *>()) {
6514+
if (auto SE = dyn_cast<SequenceExpr>(E->getSemanticsProvidingExpr())) {
6515+
if (SE->getNumElements() > 1 && isa<AssignExpr>(SE->getElement(1))) {
6516+
// This is an assignment. We don't want to implicitly return
6517+
// it.
6518+
return BS;
6519+
}
6520+
}
6521+
if (isa<FuncDecl>(AFD)) {
6522+
auto RS = new (Context) ReturnStmt(SourceLoc(), E);
6523+
BS->setFirstElement(RS);
6524+
AFD->setHasSingleExpressionBody();
6525+
AFD->setSingleExpressionBody(E);
6526+
} else if (auto *F = dyn_cast<ConstructorDecl>(AFD)) {
6527+
if (F->isFailable() && isa<NilLiteralExpr>(E)) {
6528+
// If it's a nil literal, just insert return. This is the only
6529+
// legal thing to return.
6530+
auto RS = new (Context) ReturnStmt(E->getStartLoc(), E);
6531+
BS->setFirstElement(RS);
6532+
AFD->setHasSingleExpressionBody();
6533+
AFD->setSingleExpressionBody(E);
6534+
}
6535+
}
6536+
}
6537+
6538+
return BS;
64836539
}
64846540

64856541
/// Parse function body into \p AFD or skip it for delayed parsing.
@@ -6504,60 +6560,7 @@ void Parser::parseAbstractFunctionBody(AbstractFunctionDecl *AFD) {
65046560
}
65056561

65066562
Scope S(this, ScopeKind::FunctionBody);
6507-
6508-
ParserResult<BraceStmt> Body = parseAbstractFunctionBodyImpl(AFD);
6509-
if (!Body.isNull()) {
6510-
BraceStmt * BS = Body.get();
6511-
AFD->setBodyParsed(BS);
6512-
6513-
// If the body consists of a single expression, turn it into a return
6514-
// statement.
6515-
//
6516-
// But don't do this transformation during code completion, as the source
6517-
// may be incomplete and the type mismatch in return statement will just
6518-
// confuse the type checker.
6519-
if (!Body.hasCodeCompletion() && BS->getNumElements() == 1) {
6520-
auto Element = BS->getFirstElement();
6521-
if (auto *stmt = Element.dyn_cast<Stmt *>()) {
6522-
if (isa<FuncDecl>(AFD)) {
6523-
if (auto *returnStmt = dyn_cast<ReturnStmt>(stmt)) {
6524-
if (!returnStmt->hasResult()) {
6525-
auto returnExpr = TupleExpr::createEmpty(Context,
6526-
SourceLoc(),
6527-
SourceLoc(),
6528-
/*implicit*/true);
6529-
returnStmt->setResult(returnExpr);
6530-
AFD->setHasSingleExpressionBody();
6531-
AFD->setSingleExpressionBody(returnExpr);
6532-
}
6533-
}
6534-
}
6535-
} else if (auto *E = Element.dyn_cast<Expr *>()) {
6536-
if (auto SE = dyn_cast<SequenceExpr>(E->getSemanticsProvidingExpr())) {
6537-
if (SE->getNumElements() > 1 && isa<AssignExpr>(SE->getElement(1))) {
6538-
// This is an assignment. We don't want to implicitly return
6539-
// it.
6540-
return;
6541-
}
6542-
}
6543-
if (isa<FuncDecl>(AFD)) {
6544-
auto RS = new (Context) ReturnStmt(SourceLoc(), E);
6545-
BS->setFirstElement(RS);
6546-
AFD->setHasSingleExpressionBody();
6547-
AFD->setSingleExpressionBody(E);
6548-
} else if (auto *F = dyn_cast<ConstructorDecl>(AFD)) {
6549-
if (F->isFailable() && isa<NilLiteralExpr>(E)) {
6550-
// If it's a nil literal, just insert return. This is the only
6551-
// legal thing to return.
6552-
auto RS = new (Context) ReturnStmt(E->getStartLoc(), E);
6553-
BS->setFirstElement(RS);
6554-
AFD->setHasSingleExpressionBody();
6555-
AFD->setSingleExpressionBody(E);
6556-
}
6557-
}
6558-
}
6559-
}
6560-
}
6563+
(void)parseAbstractFunctionBodyImpl(AFD);
65616564
}
65626565

65636566
BraceStmt *Parser::parseAbstractFunctionBodyDelayed(AbstractFunctionDecl *AFD) {
@@ -6589,7 +6592,7 @@ BraceStmt *Parser::parseAbstractFunctionBodyDelayed(AbstractFunctionDecl *AFD) {
65896592
Scope TopLevelScope(this, ScopeKind::TopLevel);
65906593
Scope S(this, ScopeKind::FunctionBody);
65916594

6592-
return parseAbstractFunctionBodyImpl(AFD).getPtrOrNull();
6595+
return parseAbstractFunctionBodyImpl(AFD);
65936596
}
65946597

65956598
/// Parse a 'enum' declaration, returning true (and doing no token

lib/Parse/ParseExpr.cpp

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -802,8 +802,6 @@ ParserResult<Expr> Parser::parseExprSelector() {
802802
parseExpr(selectorKind == ObjCSelectorExpr::Method
803803
? diag::expr_selector_expected_method_expr
804804
: diag::expr_selector_expected_property_expr);
805-
if (subExpr.hasCodeCompletion())
806-
return makeParserCodeCompletionResult<Expr>();
807805

808806
// Parse the closing ')'.
809807
SourceLoc rParenLoc;
@@ -819,7 +817,7 @@ ParserResult<Expr> Parser::parseExprSelector() {
819817
}
820818

821819
// If the subexpression was in error, just propagate the error.
822-
if (subExpr.isParseError())
820+
if (subExpr.isParseError() && !subExpr.hasCodeCompletion())
823821
return makeParserResult<Expr>(
824822
new (Context) ErrorExpr(SourceRange(keywordLoc, rParenLoc)));
825823

lib/Parse/ParsePattern.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,8 +130,10 @@ static ParserStatus parseDefaultArgument(
130130

131131
defaultArgs->HasDefaultArgument = true;
132132

133-
if (initR.hasCodeCompletion())
133+
if (initR.hasCodeCompletion()) {
134+
init = initR.get();
134135
return makeParserCodeCompletionStatus();
136+
}
135137

136138
if (initR.isNull())
137139
return makeParserError();

lib/Parse/ParseStmt.cpp

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -898,9 +898,7 @@ ParserResult<Stmt> Parser::parseStmtThrow(SourceLoc tryLoc) {
898898
exprLoc = Tok.getLoc();
899899

900900
ParserResult<Expr> Result = parseExpr(diag::expected_expr_throw);
901-
902-
if (Result.hasCodeCompletion())
903-
return makeParserCodeCompletionResult<Stmt>();
901+
bool hasCodeCompletion = Result.hasCodeCompletion();
904902

905903
if (Result.isNull())
906904
Result = makeParserErrorResult(new (Context) ErrorExpr(throwLoc));
@@ -916,6 +914,9 @@ ParserResult<Stmt> Parser::parseStmtThrow(SourceLoc tryLoc) {
916914
Result = makeParserResult(new (Context) TryExpr(exprLoc, Result.get()));
917915
}
918916

917+
if (hasCodeCompletion)
918+
Result.setHasCodeCompletion();
919+
919920
return makeParserResult(Result,
920921
new (Context) ThrowStmt(throwLoc, Result.get()));
921922
}
@@ -2012,9 +2013,6 @@ ParserResult<CaseStmt> Parser::parseStmtCatch() {
20122013
GuardedPattern PatternResult;
20132014
parseGuardedPattern(*this, PatternResult, status, boundDecls,
20142015
GuardedPatternContext::Catch, isFirst);
2015-
if (status.hasCodeCompletion()) {
2016-
return makeParserCodeCompletionResult<CaseStmt>();
2017-
}
20182016
caseLabelItems.emplace_back(PatternResult.ThePattern,
20192017
PatternResult.WhereLoc, PatternResult.Guard);
20202018
isFirst = false;

lib/Parse/Parser.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ void Parser::performCodeCompletionSecondPassImpl(
203203

204204
case CodeCompletionDelayedDeclKind::FunctionBody: {
205205
auto *AFD = cast<AbstractFunctionDecl>(DC);
206-
AFD->setBodyParsed(parseAbstractFunctionBodyImpl(AFD).getPtrOrNull());
206+
(void)parseAbstractFunctionBodyImpl(AFD);
207207
break;
208208
}
209209
}

test/IDE/complete_exception.swift

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@
55
// RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -code-completion -source-filename %s -code-completion-token=CATCH2 | %FileCheck %s -check-prefix=CATCH2
66
// RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -code-completion -source-filename %s -code-completion-token=THROW2 | %FileCheck %s -check-prefix=THROW2
77
// RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -code-completion -source-filename %s -code-completion-token=CATCH3 | %FileCheck %s -check-prefix=CATCH3
8+
// RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -code-completion -source-filename %s -code-completion-token=THROW3 | %FileCheck %s -check-prefix=THROW3
89
// RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -code-completion -source-filename %s -code-completion-token=TOP_LEVEL_CATCH1 | %FileCheck %s -check-prefix=CATCH1
910
// RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -code-completion -source-filename %s -code-completion-token=TOP_LEVEL_THROW1 | %FileCheck %s -check-prefix=THROW1
1011

1112
// RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -code-completion -source-filename %s -code-completion-token=TOP_LEVEL_CATCH2 | %FileCheck %s -check-prefix=CATCH2
1213
// RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -code-completion -source-filename %s -code-completion-token=TOP_LEVEL_THROW2 | %FileCheck %s -check-prefix=THROW2
14+
// RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -code-completion -source-filename %s -code-completion-token=TOP_LEVEL_THROW3 | %FileCheck %s -check-prefix=THROW3
1315

1416
// RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -code-completion -source-filename %s -code-completion-token=INSIDE_CATCH1 > %t.inside_catch1
1517
// RUN: %FileCheck %s -check-prefix=STMT < %t.inside_catch1
@@ -70,10 +72,10 @@ func test001() {
7072
do {} catch #^CATCH1^#
7173

7274
// CATCH1: Begin completions
73-
// CATCH1-DAG: Decl[Enum]/CurrModule: Error4[#Error4#]; name=Error4{{$}}
74-
// CATCH1-DAG: Decl[Class]/CurrModule: Error3[#Error3#]; name=Error3{{$}}
75-
// CATCH1-DAG: Decl[Class]/CurrModule: Error2[#Error2#]; name=Error2{{$}}
76-
// CATCH1-DAG: Decl[Class]/CurrModule: Error1[#Error1#]; name=Error1{{$}}
75+
// CATCH1-DAG: Decl[Enum]/CurrModule/TypeRelation[Convertible]: Error4[#Error4#]; name=Error4{{$}}
76+
// CATCH1-DAG: Decl[Class]/CurrModule/TypeRelation[Convertible]: Error3[#Error3#]; name=Error3{{$}}
77+
// CATCH1-DAG: Decl[Class]/CurrModule/TypeRelation[Convertible]: Error2[#Error2#]; name=Error2{{$}}
78+
// CATCH1-DAG: Decl[Class]/CurrModule/TypeRelation[Convertible]: Error1[#Error1#]; name=Error1{{$}}
7779
// CATCH1-DAG: Keyword[let]/None: let{{; name=.+$}}
7880
// CATCH1-DAG: Decl[Class]/CurrModule: NoneError1[#NoneError1#]; name=NoneError1{{$}}
7981
// CATCH1-DAG: Decl[Class]/OtherModule[Foundation]/IsSystem: NSError[#NSError#]{{; name=.+$}}
@@ -126,11 +128,20 @@ func test005() {
126128
// CATCH3: End completions
127129
}
128130

131+
func testInvalid() {
132+
try throw Error4.#^THROW3^#
133+
// THROW3: Begin completions
134+
// THROW3: Decl[EnumElement]/CurrNominal: E1[#Error4#]{{; name=.+$}}
135+
// THROW3: Decl[EnumElement]/CurrNominal: E2({#Int32#})[#Error4#]{{; name=.+$}}
136+
// THROW3: End completions
137+
}
138+
129139
//===--- Top-level throw/catch
130140
do {} catch #^TOP_LEVEL_CATCH1^# {}
131141
throw #^TOP_LEVEL_THROW1^#
132142
do {} catch Error4.#^TOP_LEVEL_CATCH2^# {}
133143
throw Error4.#^TOP_LEVEL_THROW2^#
144+
try throw Error4.#^TOP_LEVEL_THROW3^#
134145

135146
//===--- Inside catch body
136147

test/IDE/complete_unresolved_members.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -109,9 +109,9 @@
109109
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=GENERICPARAM_21 | %FileCheck %s -check-prefix=GENERICPARAM_1
110110

111111
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=DECL_MEMBER_INIT_1 | %FileCheck %s -check-prefix=UNRESOLVED_3
112-
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=DEFAULT_ARG_1 | %FileCheck %s -check-prefix=UNRESOLVED_3_NOTIDEAL
113-
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=DEFAULT_ARG_2 | %FileCheck %s -check-prefix=UNRESOLVED_3_NOTIDEAL
114-
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=DEFAULT_ARG_3 | %FileCheck %s -check-prefix=UNRESOLVED_3_NOTIDEAL
112+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=DEFAULT_ARG_1 | %FileCheck %s -check-prefix=UNRESOLVED_3
113+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=DEFAULT_ARG_2 | %FileCheck %s -check-prefix=UNRESOLVED_3
114+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=DEFAULT_ARG_3 | %FileCheck %s -check-prefix=UNRESOLVED_3
115115

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

0 commit comments

Comments
 (0)