Skip to content

Commit 4c38ec5

Browse files
authored
Merge pull request swiftlang#35069 from rintaro/ide-completion-rdar72199413
[Parse/CodeCompletion] Implement effects specifier completion
2 parents b940709 + 31595e3 commit 4c38ec5

File tree

12 files changed

+305
-167
lines changed

12 files changed

+305
-167
lines changed

include/swift/AST/DiagnosticsParse.def

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -732,8 +732,7 @@ ERROR(rethrowing_function_type,none,
732732
"only function declarations may be marked 'rethrows'; "
733733
"did you mean 'throws'?", ())
734734
ERROR(async_or_throws_in_wrong_position,none,
735-
"%select{'throws'|'rethrows'|'async'}0 may only occur before '->'",
736-
(unsigned))
735+
"'%0' may only occur before '->'", (StringRef))
737736
ERROR(throw_in_function_type,none,
738737
"expected throwing specifier; did you mean 'throws'?", ())
739738
ERROR(expected_type_before_arrow,none,
@@ -748,6 +747,8 @@ ERROR(expected_dynamic_func_attr,none,
748747
ERROR(async_after_throws,none,
749748
"'async' must precede %select{'throws'|'rethrows'}0", (bool))
750749
ERROR(async_init,none, "initializer cannot be marked 'async'", ())
750+
ERROR(duplicate_effects_specifier,none,
751+
"'%0' has already been specified", (StringRef))
751752

752753
// Enum Types
753754
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: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1353,9 +1353,12 @@ 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);
1359+
1360+
/// Returns 'true' if \p T is considered effects specifier.
1361+
bool isEffectsSpecifier(const Token &T);
13591362

13601363
//===--------------------------------------------------------------------===//
13611364
// Pattern Parsing
@@ -1433,6 +1436,15 @@ class Parser {
14331436
/// \endverbatim
14341437
bool canParseBaseTypeForQualifiedDeclName();
14351438

1439+
/// Returns true if the current token is '->' or effects specifiers followed
1440+
/// by '->'.
1441+
///
1442+
/// e.g.
1443+
/// throws -> // true
1444+
/// async throws -> // true
1445+
/// throws { // false
1446+
bool isAtFunctionTypeArrow();
1447+
14361448
//===--------------------------------------------------------------------===//
14371449
// Expression Parsing
14381450
ParserResult<Expr> parseExpr(Diag<> ID) {
@@ -1554,8 +1566,9 @@ class Parser {
15541566
/// \param explicitResultType The explicit result type, if specified.
15551567
/// \param inLoc The location of the 'in' keyword, if present.
15561568
///
1557-
/// \returns true if an error occurred, false otherwise.
1558-
bool parseClosureSignatureIfPresent(
1569+
/// \returns ParserStatus error if an error occurred. Success if no signature
1570+
/// is present or succssfully parsed.
1571+
ParserStatus parseClosureSignatureIfPresent(
15591572
SourceRange &bracketRange,
15601573
SmallVectorImpl<CaptureListEntry> &captureList,
15611574
VarDecl *&capturedSelfParamDecl,

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;
@@ -5783,6 +5795,15 @@ void CodeCompletionCallbacksImpl::addKeywords(CodeCompletionResultSink &Sink,
57835795
case CompletionKind::StmtLabel:
57845796
break;
57855797

5798+
case CompletionKind::EffectsSpecifier: {
5799+
if (!llvm::is_contained(ParsedKeywords, "async") &&
5800+
Context.LangOpts.EnableExperimentalConcurrency)
5801+
addKeyword(Sink, "async", CodeCompletionKeywordKind::None);
5802+
if (!llvm::is_contained(ParsedKeywords, "throws"))
5803+
addKeyword(Sink, "throws", CodeCompletionKeywordKind::kw_throws);
5804+
break;
5805+
}
5806+
57865807
case CompletionKind::AccessorBeginning: {
57875808
// TODO: Omit already declared or mutally exclusive accessors.
57885809
// E.g. If 'get' is already declared, emit 'set' only.
@@ -6760,6 +6781,7 @@ void CodeCompletionCallbacksImpl::doneParsing() {
67606781
}
67616782
case CompletionKind::AfterIfStmtElse:
67626783
case CompletionKind::CaseStmtKeyword:
6784+
case CompletionKind::EffectsSpecifier:
67636785
// Handled earlier by keyword completions.
67646786
break;
67656787
}

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: 47 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -126,20 +126,26 @@ 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());
134140
diagnose(throwsLoc.isValid() ? throwsLoc : asyncLoc,
135141
diag::async_or_throws_in_wrong_position,
136-
throwsLoc.isValid() ? 0 : 2);
142+
throwsLoc.isValid() ? "throws" : "async");
137143
return nullptr;
138144
}
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);
@@ -2317,14 +2323,14 @@ static void printTupleNames(const TypeRepr *typeRepr, llvm::raw_ostream &OS) {
23172323
OS << ")";
23182324
}
23192325

2320-
bool Parser::
2321-
parseClosureSignatureIfPresent(SourceRange &bracketRange,
2322-
SmallVectorImpl<CaptureListEntry> &captureList,
2323-
VarDecl *&capturedSelfDecl,
2324-
ParameterList *&params,
2325-
SourceLoc &asyncLoc, SourceLoc &throwsLoc,
2326-
SourceLoc &arrowLoc,
2327-
TypeExpr *&explicitResultType, SourceLoc &inLoc){
2326+
ParserStatus Parser::parseClosureSignatureIfPresent(
2327+
SourceRange &bracketRange,
2328+
SmallVectorImpl<CaptureListEntry> &captureList,
2329+
VarDecl *&capturedSelfDecl,
2330+
ParameterList *&params,
2331+
SourceLoc &asyncLoc, SourceLoc &throwsLoc,
2332+
SourceLoc &arrowLoc,
2333+
TypeExpr *&explicitResultType, SourceLoc &inLoc) {
23282334
// Clear out result parameters.
23292335
bracketRange = SourceRange();
23302336
capturedSelfDecl = nullptr;
@@ -2335,21 +2341,10 @@ parseClosureSignatureIfPresent(SourceRange &bracketRange,
23352341
inLoc = SourceLoc();
23362342

23372343
// Consume 'async', 'throws', and 'rethrows', but in any order.
2338-
auto consumeAsyncThrows = [&] {
2339-
bool hadAsync = false;
2340-
if (shouldParseExperimentalConcurrency() &&
2341-
Tok.isContextualKeyword("async")) {
2342-
consumeToken();
2343-
hadAsync = true;
2344-
}
2345-
2346-
if (!consumeIf(tok::kw_throws) && !consumeIf(tok::kw_rethrows))
2347-
return;
2348-
2349-
if (shouldParseExperimentalConcurrency() && !hadAsync &&
2350-
Tok.isContextualKeyword("async")) {
2344+
auto consumeEffectsSpecifiers = [&] {
2345+
while (isEffectsSpecifier(Tok) ||
2346+
(Tok.is(tok::code_complete) && !Tok.isAtStartOfLine()))
23512347
consumeToken();
2352-
}
23532348
};
23542349

23552350
// If we have a leading token that may be part of the closure signature, do a
@@ -2361,7 +2356,7 @@ parseClosureSignatureIfPresent(SourceRange &bracketRange,
23612356
if (consumeIf(tok::l_square)) {
23622357
skipUntil(tok::r_square);
23632358
if (!consumeIf(tok::r_square))
2364-
return false;
2359+
return makeParserSuccess();
23652360
}
23662361

23672362
// Parse pattern-tuple func-signature-result? 'in'.
@@ -2373,12 +2368,14 @@ parseClosureSignatureIfPresent(SourceRange &bracketRange,
23732368

23742369
// Consume the ')', if it's there.
23752370
if (consumeIf(tok::r_paren)) {
2376-
consumeAsyncThrows();
2371+
consumeEffectsSpecifiers();
23772372

23782373
// Parse the func-signature-result, if present.
23792374
if (consumeIf(tok::arrow)) {
23802375
if (!canParseType())
2381-
return false;
2376+
return makeParserSuccess();
2377+
2378+
consumeEffectsSpecifiers();
23822379
}
23832380
}
23842381

@@ -2392,27 +2389,30 @@ parseClosureSignatureIfPresent(SourceRange &bracketRange,
23922389
continue;
23932390
}
23942391

2395-
return false;
2392+
return makeParserSuccess();
23962393
}
23972394

2398-
consumeAsyncThrows();
2395+
consumeEffectsSpecifiers();
23992396

24002397
// Parse the func-signature-result, if present.
24012398
if (consumeIf(tok::arrow)) {
24022399
if (!canParseType())
2403-
return false;
2400+
return makeParserSuccess();
2401+
2402+
consumeEffectsSpecifiers();
24042403
}
24052404
}
24062405

24072406
// Parse the 'in' at the end.
24082407
if (Tok.isNot(tok::kw_in))
2409-
return false;
2408+
return makeParserSuccess();
24102409

24112410
// Okay, we have a closure signature.
24122411
} else {
24132412
// No closure signature.
2414-
return false;
2413+
return makeParserSuccess();
24152414
}
2415+
ParserStatus status;
24162416
SyntaxParsingContext ClosureSigCtx(SyntaxContext, SyntaxKind::ClosureSignature);
24172417
if (Tok.is(tok::l_square) && peekToken().is(tok::r_square)) {
24182418

@@ -2557,7 +2557,7 @@ parseClosureSignatureIfPresent(SourceRange &bracketRange,
25572557
if (pattern.isNonNull())
25582558
params = pattern.get();
25592559
else
2560-
invalid = true;
2560+
status.setIsParseError();
25612561
} else {
25622562
SyntaxParsingContext ClParamListCtx(SyntaxContext,
25632563
SyntaxKind::ClosureParamList);
@@ -2568,7 +2568,7 @@ parseClosureSignatureIfPresent(SourceRange &bracketRange,
25682568
SyntaxParsingContext ClParamCtx(SyntaxContext, SyntaxKind::ClosureParam);
25692569
if (Tok.isNot(tok::identifier, tok::kw__)) {
25702570
diagnose(Tok, diag::expected_closure_parameter_name);
2571-
invalid = true;
2571+
status.setIsParseError();
25722572
break;
25732573
}
25742574

@@ -2592,12 +2592,8 @@ parseClosureSignatureIfPresent(SourceRange &bracketRange,
25922592
params = ParameterList::create(Context, elements);
25932593
}
25942594

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-
}
2595+
status |= parseEffectsSpecifiers(SourceLoc(), asyncLoc, throwsLoc,
2596+
/*rethrows*/nullptr);
26012597

26022598
// Parse the optional explicit return type.
26032599
if (Tok.is(tok::arrow)) {
@@ -2611,9 +2607,13 @@ parseClosureSignatureIfPresent(SourceRange &bracketRange,
26112607
if (!explicitResultTypeRepr) {
26122608
// If we couldn't parse the result type, clear out the arrow location.
26132609
arrowLoc = SourceLoc();
2614-
invalid = true;
2610+
status.setIsParseError();
26152611
} else {
26162612
explicitResultType = new (Context) TypeExpr(explicitResultTypeRepr);
2613+
2614+
// Check for 'throws' and 'rethrows' after the type and correct it.
2615+
parseEffectsSpecifiers(arrowLoc, asyncLoc, throwsLoc,
2616+
/*rethrows*/nullptr);
26172617
}
26182618
}
26192619
}
@@ -2651,7 +2651,7 @@ parseClosureSignatureIfPresent(SourceRange &bracketRange,
26512651
}
26522652

26532653
if (!params)
2654-
return invalid;
2654+
return status;
26552655

26562656
// If this was a closure declaration (maybe even trailing)
26572657
// tuple parameter destructuring is one of the common
@@ -2689,14 +2689,15 @@ parseClosureSignatureIfPresent(SourceRange &bracketRange,
26892689
.fixItReplace(param->getSourceRange(), argName)
26902690
.fixItInsert(Tok.getLoc(), OS.str());
26912691

2692-
invalid = true;
2692+
status.setIsParseError();
26932693
}
26942694

2695-
return invalid;
2695+
return status;
26962696
}
26972697

26982698
ParserResult<Expr> Parser::parseExprClosure() {
26992699
assert(Tok.is(tok::l_brace) && "Not at a left brace?");
2700+
ParserStatus Status;
27002701
SyntaxParsingContext ClosureContext(SyntaxContext, SyntaxKind::ClosureExpr);
27012702
// We may be parsing this closure expr in a matching pattern context. If so,
27022703
// reset our state to not be in a pattern for any recursive pattern parses.
@@ -2716,7 +2717,7 @@ ParserResult<Expr> Parser::parseExprClosure() {
27162717
SourceLoc arrowLoc;
27172718
TypeExpr *explicitResultType;
27182719
SourceLoc inLoc;
2719-
parseClosureSignatureIfPresent(
2720+
Status |= parseClosureSignatureIfPresent(
27202721
bracketRange, captureList, capturedSelfDecl, params, asyncLoc, throwsLoc,
27212722
arrowLoc, explicitResultType, inLoc);
27222723

@@ -2751,7 +2752,6 @@ ParserResult<Expr> Parser::parseExprClosure() {
27512752

27522753
// Parse the body.
27532754
SmallVector<ASTNode, 4> bodyElements;
2754-
ParserStatus Status;
27552755
Status |= parseBraceItems(bodyElements, BraceItemListKind::Brace);
27562756

27572757
if (SourceMgr.rangeContainsCodeCompletionLoc({leftBrace, PreviousLoc})) {

0 commit comments

Comments
 (0)