Skip to content

Commit 4ad27ba

Browse files
committed
[Parse] Implement parsing for using declarations
1 parent 202907d commit 4ad27ba

File tree

6 files changed

+163
-2
lines changed

6 files changed

+163
-2
lines changed

include/swift/AST/DiagnosticsParse.def

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2187,5 +2187,12 @@ ERROR(nonisolated_nonsending_expected_rparen,PointsToFirstBadToken,
21872187
ERROR(nonisolated_nonsending_repeated,none,
21882188
"parameter may have at most one 'nonisolated(nonsending)' specifier", ())
21892189

2190+
//------------------------------------------------------------------------------
2191+
// MARK: using @<attribute> or using <identifier>
2192+
//------------------------------------------------------------------------------
2193+
ERROR(using_decl_invalid_specifier,PointsToFirstBadToken,
2194+
"'using' declaration does not support %0 %select{modifier|attribute}1",
2195+
(Identifier, bool))
2196+
21902197
#define UNDEFINE_DIAGNOSTIC_MACROS
21912198
#include "DefineDiagnosticMacros.h"

include/swift/Parse/Parser.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1223,6 +1223,9 @@ class Parser {
12231223
ParserResult<ImportDecl> parseDeclImport(ParseDeclOptions Flags,
12241224
DeclAttributes &Attributes);
12251225

1226+
ParserResult<UsingDecl> parseDeclUsing(ParseDeclOptions Flags,
1227+
DeclAttributes &Attributes);
1228+
12261229
/// Parse an inheritance clause into a vector of InheritedEntry's.
12271230
///
12281231
/// \param allowClassRequirement whether to permit parsing of 'class'

include/swift/Parse/Token.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -196,8 +196,9 @@ class Token {
196196
#define CONTEXTUAL_SIMPLE_DECL_ATTR(KW, ...) CONTEXTUAL_CASE(KW)
197197
#include "swift/AST/DeclAttr.def"
198198
#undef CONTEXTUAL_CASE
199-
.Case("macro", true)
200-
.Default(false);
199+
.Case("macro", true)
200+
.Case("using", true)
201+
.Default(false);
201202
}
202203

203204
bool isContextualPunctuator(StringRef ContextPunc) const {

lib/Parse/ParseDecl.cpp

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5938,6 +5938,17 @@ bool Parser::isStartOfSwiftDecl(bool allowPoundIfAttributes,
59385938
}
59395939
}
59405940

5941+
// `using @<attribute>` or `using <identifier>`.
5942+
if (Tok.isContextualKeyword("using")) {
5943+
// `using` declarations don't support attributes or modifiers.
5944+
if (hadAttrsOrModifiers)
5945+
return false;
5946+
5947+
return !Tok2.isAtStartOfLine() &&
5948+
(Tok2.is(tok::at_sign) || Tok2.is(tok::identifier) ||
5949+
Tok2.is(tok::code_complete));
5950+
}
5951+
59415952
// If the next token is obviously not the start of a decl, bail early.
59425953
if (!isKeywordPossibleDeclStart(Context.LangOpts, Tok2))
59435954
return false;
@@ -6292,6 +6303,17 @@ ParserStatus Parser::parseDecl(bool IsAtStartOfLineOrPreviousHadSemi,
62926303
break;
62936304
}
62946305

6306+
// `using @<attribute>` or `using <identifier>`
6307+
if (Tok.isContextualKeyword("using")) {
6308+
auto nextToken = peekToken();
6309+
if (!nextToken.isAtStartOfLine() &&
6310+
(nextToken.is(tok::at_sign) || nextToken.is(tok::identifier) ||
6311+
nextToken.is(tok::code_complete))) {
6312+
DeclResult = parseDeclUsing(Flags, Attributes);
6313+
break;
6314+
}
6315+
}
6316+
62956317
if (Flags.contains(PD_HasContainerType) &&
62966318
IsAtStartOfLineOrPreviousHadSemi) {
62976319

@@ -6639,6 +6661,65 @@ ParserResult<ImportDecl> Parser::parseDeclImport(ParseDeclOptions Flags,
66396661
return DCC.fixupParserResult(ID);
66406662
}
66416663

6664+
/// Parse an `using` declaration.
6665+
///
6666+
/// \verbatim
6667+
/// decl-using:
6668+
/// 'using' (@<attribute> | <modifier>)
6669+
/// \endverbatim
6670+
ParserResult<UsingDecl> Parser::parseDeclUsing(ParseDeclOptions Flags,
6671+
DeclAttributes &Attributes) {
6672+
assert(Tok.isContextualKeyword("using"));
6673+
DebuggerContextChange DCC(*this);
6674+
6675+
SourceLoc UsingLoc = consumeToken();
6676+
6677+
if (Tok.is(tok::code_complete)) {
6678+
if (CodeCompletionCallbacks) {
6679+
CodeCompletionCallbacks->completeUsingDecl();
6680+
}
6681+
return makeParserCodeCompletionStatus();
6682+
}
6683+
6684+
SourceLoc AtLoc;
6685+
// @<<attribute>>
6686+
if (Tok.is(tok::at_sign))
6687+
AtLoc = consumeToken();
6688+
6689+
SourceLoc SpecifierLoc;
6690+
Identifier RawSpecifier;
6691+
6692+
if (parseIdentifier(RawSpecifier, SpecifierLoc,
6693+
/*diagnoseDollarPrefix=*/false,
6694+
diag::expected_identifier_in_decl, "using"))
6695+
return nullptr;
6696+
6697+
std::optional<UsingSpecifier> Specifier =
6698+
llvm::StringSwitch<std::optional<UsingSpecifier>>(RawSpecifier.str())
6699+
.Case("MainActor", UsingSpecifier::MainActor)
6700+
.Case("nonisolated", UsingSpecifier::nonisolated)
6701+
.Default(std::nullopt);
6702+
6703+
if (!Specifier) {
6704+
diagnose(SpecifierLoc, diag::using_decl_invalid_specifier, RawSpecifier,
6705+
AtLoc.isValid());
6706+
return nullptr;
6707+
}
6708+
6709+
// Complain the `using` not being at top-level only after the specifier
6710+
// has been consumed, otherwise the specifier is going to be interpreted
6711+
// as a start of another declaration.
6712+
if (!CodeCompletionCallbacks && !DCC.movedToTopLevel() &&
6713+
!(Flags & PD_AllowTopLevel)) {
6714+
diagnose(UsingLoc, diag::decl_inner_scope);
6715+
return nullptr;
6716+
}
6717+
6718+
auto *UD = UsingDecl::create(Context, UsingLoc, AtLoc ? AtLoc : SpecifierLoc,
6719+
*Specifier, CurDeclContext);
6720+
return DCC.fixupParserResult(UD);
6721+
}
6722+
66426723
/// Parse an inheritance clause.
66436724
///
66446725
/// \verbatim

test/IDE/complete_using.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// RUN: %batch-code-completion
2+
3+
// USING-DAG: Keyword/None: @MainActor; name=@MainActor
4+
// USING-DAG: Keyword/None: nonisolated; name=nonisolated
5+
6+
using #^USING^#

test/Parse/using.swift

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// RUN: %target-typecheck-verify-swift
2+
3+
// REQUIRES: concurrency
4+
5+
using @MainActor
6+
using nonisolated
7+
8+
using @Test // expected-error {{'using' declaration does not support 'Test' attribute}}
9+
using test // expected-error {{'using' declaration does not support 'test' modifier}}
10+
11+
using
12+
@MainActor
13+
14+
using
15+
nonisolated
16+
17+
do {
18+
func
19+
using (x: Int) {}
20+
21+
using(x: 42)
22+
}
23+
24+
do {
25+
func
26+
using
27+
(x: Int) {}
28+
29+
using(x: 42)
30+
}
31+
32+
let
33+
using = 42
34+
35+
let (x: Int, using: String) = (x: 42, using: "")
36+
37+
do {
38+
using @MainActor // expected-error {{declaration is only valid at file scope}}
39+
}
40+
41+
func test() {
42+
using @MainActor // expected-error {{declaration is only valid at file scope}}
43+
}
44+
45+
struct S {
46+
var x: Int {
47+
using @MainActor // expected-error {{declaration is only valid at file scope}}
48+
}
49+
50+
using @MainActor func test() {}
51+
// expected-error@-1 {{declaration is only valid at file scope}}
52+
// expected-error@-2 {{consecutive declarations on a line must be separated by ';'}}
53+
54+
using nonisolated subscript(a: Int) -> Bool { false }
55+
// expected-error@-1 {{declaration is only valid at file scope}}
56+
// expected-error@-2 {{consecutive declarations on a line must be separated by ';'}}
57+
}
58+
59+
do {
60+
@objc using @MainActor
61+
// expected-error@-1 {{expected expression}}
62+
// expected-error@-2 {{declaration is only valid at file scope}}
63+
}

0 commit comments

Comments
 (0)