Skip to content

Commit 4284a51

Browse files
committed
[Parse/CodeCompletion] Implement effects specifier completion
Rewrote and rename 'parseAsyncThrows' to 'parseEffectsSpecifiers'. Implemented 'CodeCompletionCallbacks::completeEffectsSpecifier()'
1 parent c65a206 commit 4284a51

File tree

11 files changed

+217
-97
lines changed

11 files changed

+217
-97
lines changed

include/swift/AST/DiagnosticsParse.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -748,6 +748,9 @@ ERROR(expected_dynamic_func_attr,none,
748748
ERROR(async_after_throws,none,
749749
"'async' must precede %select{'throws'|'rethrows'}0", (bool))
750750
ERROR(async_init,none, "initializer cannot be marked 'async'", ())
751+
ERROR(duplicate_effect_specifier,none,
752+
"unexpected %select{'throws'|'rethrows'|'async'}0",
753+
(unsigned))
751754

752755
// Enum Types
753756
ERROR(expected_expr_enum_case_raw_value,PointsToFirstBadToken,

include/swift/IDE/CodeCompletion.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -549,6 +549,7 @@ enum class CompletionKind {
549549
AccessorBeginning,
550550
AttributeBegin,
551551
AttributeDeclParen,
552+
EffectsSpecifier,
552553
PoundAvailablePlatform,
553554
CallArg,
554555
LabeledTrailingClosure,

include/swift/Parse/CodeCompletionCallbacks.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,9 @@ class CodeCompletionCallbacks {
181181
/// @available.
182182
virtual void completeDeclAttrParam(DeclAttrKind DK, int Index) {};
183183

184+
/// Complete 'async' and 'throws' at effects specifier position.
185+
virtual void completeEffectsSpecifier(bool hasAsync, bool hasThrows) {};
186+
184187
/// Complete within a precedence group decl or after a colon in an
185188
/// operator decl.
186189
virtual void completeInPrecedenceGroup(SyntaxKind SK) {};

include/swift/Parse/Parser.h

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1353,9 +1353,9 @@ class Parser {
13531353
///
13541354
/// \param rethrows If non-NULL, will also parse the 'rethrows' keyword in
13551355
/// lieu of 'throws'.
1356-
void parseAsyncThrows(
1357-
SourceLoc existingArrowLoc, SourceLoc &asyncLoc, SourceLoc &throwsLoc,
1358-
bool *rethrows);
1356+
ParserStatus parseEffectsSpecifiers(SourceLoc existingArrowLoc,
1357+
SourceLoc &asyncLoc, SourceLoc &throwsLoc,
1358+
bool *rethrows);
13591359

13601360
//===--------------------------------------------------------------------===//
13611361
// Pattern Parsing
@@ -1433,6 +1433,15 @@ class Parser {
14331433
/// \endverbatim
14341434
bool canParseBaseTypeForQualifiedDeclName();
14351435

1436+
/// Returns true if the current token is '->' or effects specifiers followed
1437+
/// by '->'.
1438+
///
1439+
/// e.g.
1440+
/// throws -> // true
1441+
/// async throws -> // true
1442+
/// throws { // false
1443+
bool isAtFunctionTypeArrow();
1444+
14361445
//===--------------------------------------------------------------------===//
14371446
// Expression Parsing
14381447
ParserResult<Expr> parseExpr(Diag<> ID) {

lib/IDE/CodeCompletion.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1640,6 +1640,7 @@ class CodeCompletionCallbacksImpl : public CodeCompletionCallbacks {
16401640
void completeCaseStmtBeginning(CodeCompletionExpr *E) override;
16411641
void completeDeclAttrBeginning(bool Sil, bool isIndependent) override;
16421642
void completeDeclAttrParam(DeclAttrKind DK, int Index) override;
1643+
void completeEffectsSpecifier(bool hasAsync, bool hasThrows) override;
16431644
void completeInPrecedenceGroup(SyntaxKind SK) override;
16441645
void completeNominalMemberBeginning(
16451646
SmallVectorImpl<StringRef> &Keywords, SourceLoc introducerLoc) override;
@@ -5465,6 +5466,17 @@ void CodeCompletionCallbacksImpl::completeDeclAttrParam(DeclAttrKind DK,
54655466
CurDeclContext = P.CurDeclContext;
54665467
}
54675468

5469+
void CodeCompletionCallbacksImpl::completeEffectsSpecifier(bool hasAsync,
5470+
bool hasThrows) {
5471+
Kind = CompletionKind::EffectsSpecifier;
5472+
CurDeclContext = P.CurDeclContext;
5473+
ParsedKeywords.clear();
5474+
if (hasAsync)
5475+
ParsedKeywords.emplace_back("async");
5476+
if (hasThrows)
5477+
ParsedKeywords.emplace_back("throws");
5478+
}
5479+
54685480
void CodeCompletionCallbacksImpl::completeDeclAttrBeginning(
54695481
bool Sil, bool isIndependent) {
54705482
Kind = CompletionKind::AttributeBegin;
@@ -5779,6 +5791,15 @@ void CodeCompletionCallbacksImpl::addKeywords(CodeCompletionResultSink &Sink,
57795791
case CompletionKind::StmtLabel:
57805792
break;
57815793

5794+
case CompletionKind::EffectsSpecifier: {
5795+
if (!llvm::is_contained(ParsedKeywords, "async") &&
5796+
Context.LangOpts.EnableExperimentalConcurrency)
5797+
addKeyword(Sink, "async", CodeCompletionKeywordKind::None);
5798+
if (!llvm::is_contained(ParsedKeywords, "throws"))
5799+
addKeyword(Sink, "throws", CodeCompletionKeywordKind::kw_throws);
5800+
break;
5801+
}
5802+
57825803
case CompletionKind::AccessorBeginning: {
57835804
// TODO: Omit already declared or mutally exclusive accessors.
57845805
// E.g. If 'get' is already declared, emit 'set' only.
@@ -6756,6 +6777,7 @@ void CodeCompletionCallbacksImpl::doneParsing() {
67566777
}
67576778
case CompletionKind::AfterIfStmtElse:
67586779
case CompletionKind::CaseStmtKeyword:
6780+
case CompletionKind::EffectsSpecifier:
67596781
// Handled earlier by keyword completions.
67606782
break;
67616783
}

lib/Parse/ParseDecl.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7511,7 +7511,11 @@ Parser::parseDeclInit(ParseDeclOptions Flags, DeclAttributes &Attributes) {
75117511
SourceLoc asyncLoc;
75127512
SourceLoc throwsLoc;
75137513
bool rethrows = false;
7514-
parseAsyncThrows(SourceLoc(), asyncLoc, throwsLoc, &rethrows);
7514+
Status |= parseEffectsSpecifiers(SourceLoc(), asyncLoc, throwsLoc, &rethrows);
7515+
if (Status.hasCodeCompletion() && !CodeCompletion) {
7516+
// Trigger delayed parsing, no need to continue.
7517+
return Status;
7518+
}
75157519

75167520
if (rethrows) {
75177521
Attributes.add(new (Context) RethrowsAttr(throwsLoc));

lib/Parse/ParseExpr.cpp

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -126,8 +126,14 @@ ParserResult<Expr> Parser::parseExprAs() {
126126
/// 'async'? 'throws'? '->'
127127
ParserResult<Expr> Parser::parseExprArrow() {
128128
SourceLoc asyncLoc, throwsLoc, arrowLoc;
129+
ParserStatus status;
129130

130-
parseAsyncThrows(SourceLoc(), asyncLoc, throwsLoc, /*rethrows=*/nullptr);
131+
status |= parseEffectsSpecifiers(SourceLoc(), asyncLoc, throwsLoc,
132+
/*rethrows=*/nullptr);
133+
if (status.hasCodeCompletion() && !CodeCompletion) {
134+
// Trigger delayed parsing, no need to continue.
135+
return status;
136+
}
131137

132138
if (Tok.isNot(tok::arrow)) {
133139
assert(throwsLoc.isValid() || asyncLoc.isValid());
@@ -139,7 +145,7 @@ ParserResult<Expr> Parser::parseExprArrow() {
139145

140146
arrowLoc = consumeToken(tok::arrow);
141147

142-
parseAsyncThrows(arrowLoc, asyncLoc, throwsLoc, /*rethrows=*/nullptr);
148+
parseEffectsSpecifiers(arrowLoc, asyncLoc, throwsLoc, /*rethrows=*/nullptr);
143149

144150
auto arrow = new (Context) ArrowExpr(asyncLoc, throwsLoc, arrowLoc);
145151
return makeParserResult(arrow);
@@ -2592,12 +2598,8 @@ parseClosureSignatureIfPresent(SourceRange &bracketRange,
25922598
params = ParameterList::create(Context, elements);
25932599
}
25942600

2595-
bool rethrows = false;
2596-
parseAsyncThrows(SourceLoc(), asyncLoc, throwsLoc, &rethrows);
2597-
if (rethrows) {
2598-
diagnose(throwsLoc, diag::rethrowing_function_type)
2599-
.fixItReplace(throwsLoc, "throws");
2600-
}
2601+
parseEffectsSpecifiers(SourceLoc(), asyncLoc, throwsLoc,
2602+
/*rethrows*/nullptr);
26012603

26022604
// Parse the optional explicit return type.
26032605
if (Tok.is(tok::arrow)) {

lib/Parse/ParsePattern.cpp

Lines changed: 76 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -787,7 +787,7 @@ Parser::parseFunctionSignature(Identifier SimpleName,
787787

788788
// Check for the 'async' and 'throws' keywords.
789789
rethrows = false;
790-
parseAsyncThrows(SourceLoc(), asyncLoc, throwsLoc, &rethrows);
790+
Status |= parseEffectsSpecifiers(SourceLoc(), asyncLoc, throwsLoc, &rethrows);
791791

792792
// If there's a trailing arrow, parse the rest as the result type.
793793
SourceLoc arrowLoc;
@@ -802,7 +802,7 @@ Parser::parseFunctionSignature(Identifier SimpleName,
802802

803803
// Check for 'throws' and 'rethrows' after the arrow, but
804804
// before the type, and correct it.
805-
parseAsyncThrows(arrowLoc, asyncLoc, throwsLoc, &rethrows);
805+
parseEffectsSpecifiers(arrowLoc, asyncLoc, throwsLoc, &rethrows);
806806

807807
ParserResult<TypeRepr> ResultType =
808808
parseDeclResultType(diag::expected_type_function_result);
@@ -812,7 +812,7 @@ Parser::parseFunctionSignature(Identifier SimpleName,
812812
return Status;
813813

814814
// Check for 'throws' and 'rethrows' after the type and correct it.
815-
parseAsyncThrows(arrowLoc, asyncLoc, throwsLoc, &rethrows);
815+
parseEffectsSpecifiers(arrowLoc, asyncLoc, throwsLoc, &rethrows);
816816
} else {
817817
// Otherwise, we leave retType null.
818818
retType = nullptr;
@@ -821,52 +821,88 @@ Parser::parseFunctionSignature(Identifier SimpleName,
821821
return Status;
822822
}
823823

824-
void Parser::parseAsyncThrows(
825-
SourceLoc existingArrowLoc, SourceLoc &asyncLoc, SourceLoc &throwsLoc,
826-
bool *rethrows) {
827-
if (shouldParseExperimentalConcurrency() &&
828-
Tok.isContextualKeyword("async")) {
829-
asyncLoc = consumeToken();
830-
831-
if (existingArrowLoc.isValid()) {
832-
diagnose(asyncLoc, diag::async_or_throws_in_wrong_position, 2)
833-
.fixItRemove(asyncLoc)
834-
.fixItInsert(existingArrowLoc, "async ");
835-
}
836-
}
824+
ParserStatus Parser::parseEffectsSpecifiers(SourceLoc existingArrowLoc,
825+
SourceLoc &asyncLoc,
826+
SourceLoc &throwsLoc,
827+
bool *rethrows) {
828+
ParserStatus status;
837829

838-
if (Tok.isAny(tok::kw_throws, tok::kw_throw, tok::kw_try) ||
839-
(rethrows && Tok.is(tok::kw_rethrows))) {
840-
// If we allowed parsing rethrows, record whether we did in fact parse it.
841-
if (rethrows)
842-
*rethrows = Tok.is(tok::kw_rethrows);
830+
while (true) {
831+
// 'async'
832+
if (shouldParseExperimentalConcurrency() &&
833+
Tok.isContextualKeyword("async")) {
843834

844-
// Replace 'throw' or 'try' with 'throws'.
845-
if (Tok.isAny(tok::kw_throw, tok::kw_try)) {
846-
diagnose(Tok, diag::throw_in_function_type)
847-
.fixItReplace(Tok.getLoc(), "throws");
835+
if (asyncLoc.isValid()) {
836+
diagnose(Tok, diag::duplicate_effect_specifier, 2)
837+
.fixItRemove(Tok.getLoc());
838+
} else if (existingArrowLoc.isValid()) {
839+
SourceLoc insertLoc = existingArrowLoc;
840+
if (throwsLoc.isValid() &&
841+
SourceMgr.isBeforeInBuffer(throwsLoc, insertLoc))
842+
insertLoc = throwsLoc;
843+
diagnose(Tok, diag::async_or_throws_in_wrong_position, 2)
844+
.fixItRemove(Tok.getLoc())
845+
.fixItInsert(insertLoc, "async ");
846+
} else if (throwsLoc.isValid()) {
847+
// 'async' cannot be after 'throws'.
848+
assert(existingArrowLoc.isInvalid());
849+
diagnose(Tok, diag::async_after_throws, rethrows && *rethrows)
850+
.fixItRemove(Tok.getLoc())
851+
.fixItInsert(throwsLoc, "async ");
852+
}
853+
if (asyncLoc.isInvalid())
854+
asyncLoc = Tok.getLoc();
855+
consumeToken();
856+
continue;
848857
}
849858

850-
StringRef keyword = Tok.getText();
851-
throwsLoc = consumeToken();
859+
// 'throws'/'rethrows', or diagnose 'throw'/'try'.
860+
if (Tok.isAny(tok::kw_throws, tok::kw_rethrows) ||
861+
(Tok.isAny(tok::kw_throw, tok::kw_try) && !Tok.isAtStartOfLine())) {
862+
bool isRethrows = Tok.is(tok::kw_rethrows);
863+
864+
if (Tok.isAny(tok::kw_throw, tok::kw_try)) {
865+
// Replace 'throw' or 'try' with 'throws'.
866+
diagnose(Tok, diag::throw_in_function_type)
867+
.fixItReplace(Tok.getLoc(), "throws");
868+
} else if (!rethrows && isRethrows) {
869+
// Replace 'rethrows' with 'throws' unless it's allowed.
870+
diagnose(Tok, diag::rethrowing_function_type)
871+
.fixItReplace(Tok.getLoc(), "throws");
872+
} else if (throwsLoc.isValid()) {
873+
diagnose(Tok, diag::duplicate_effect_specifier,
874+
rethrows ? (isRethrows ? 1 : 0) : 0)
875+
.fixItRemove(Tok.getLoc());
876+
} else if (existingArrowLoc.isValid()) {
877+
diagnose(Tok, diag::async_or_throws_in_wrong_position,
878+
rethrows ? (isRethrows ? 1 : 0) : 0)
879+
.fixItRemove(Tok.getLoc())
880+
.fixItInsert(existingArrowLoc, (Tok.getText() + " ").str());
881+
}
852882

853-
if (existingArrowLoc.isValid()) {
854-
diagnose(throwsLoc, diag::async_or_throws_in_wrong_position,
855-
rethrows ? (*rethrows ? 1 : 0) : 0)
856-
.fixItRemove(throwsLoc)
857-
.fixItInsert(existingArrowLoc, (keyword + " ").str());
883+
if (throwsLoc.isInvalid()) {
884+
if (rethrows)
885+
*rethrows = isRethrows;
886+
throwsLoc = Tok.getLoc();
887+
}
888+
consumeToken();
889+
continue;
858890
}
859891

860-
if (shouldParseExperimentalConcurrency() &&
861-
Tok.isContextualKeyword("async")) {
862-
asyncLoc = consumeToken();
863-
864-
diagnose(asyncLoc, diag::async_after_throws, rethrows && *rethrows)
865-
.fixItRemove(asyncLoc)
866-
.fixItInsert(
867-
existingArrowLoc.isValid() ? existingArrowLoc : throwsLoc, "async ");
892+
// Code completion.
893+
if (Tok.is(tok::code_complete) && !Tok.isAtStartOfLine() &&
894+
!existingArrowLoc.isValid()) {
895+
if (CodeCompletion)
896+
CodeCompletion->completeEffectsSpecifier(asyncLoc.isValid(),
897+
throwsLoc.isValid());
898+
consumeToken(tok::code_complete);
899+
status.setHasCodeCompletionAndIsError();
900+
continue;
868901
}
902+
903+
break;
869904
}
905+
return status;
870906
}
871907

872908
/// Parse a pattern with an optional type annotation.

0 commit comments

Comments
 (0)