Skip to content

Commit b6688aa

Browse files
committed
[Parse] Implement parsing for using declarations
1 parent 00c7792 commit b6688aa

File tree

6 files changed

+152
-2
lines changed

6 files changed

+152
-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: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5938,6 +5938,13 @@ bool Parser::isStartOfSwiftDecl(bool allowPoundIfAttributes,
59385938
}
59395939
}
59405940

5941+
// `using @<attribute>` or `using <identifier>`.
5942+
if (Tok.isContextualKeyword("using")) {
5943+
return !Tok2.isAtStartOfLine() &&
5944+
(Tok2.is(tok::at_sign) || Tok2.is(tok::identifier) ||
5945+
Tok2.is(tok::code_complete));
5946+
}
5947+
59415948
// If the next token is obviously not the start of a decl, bail early.
59425949
if (!isKeywordPossibleDeclStart(Context.LangOpts, Tok2))
59435950
return false;
@@ -6292,6 +6299,17 @@ ParserStatus Parser::parseDecl(bool IsAtStartOfLineOrPreviousHadSemi,
62926299
break;
62936300
}
62946301

6302+
// `using @<attribute>` or `using <identifier>`
6303+
if (Tok.isContextualKeyword("using")) {
6304+
auto nextToken = peekToken();
6305+
if (!nextToken.isAtStartOfLine() &&
6306+
(nextToken.is(tok::at_sign) || nextToken.is(tok::identifier) ||
6307+
nextToken.is(tok::code_complete))) {
6308+
DeclResult = parseDeclUsing(Flags, Attributes);
6309+
break;
6310+
}
6311+
}
6312+
62956313
if (Flags.contains(PD_HasContainerType) &&
62966314
IsAtStartOfLineOrPreviousHadSemi) {
62976315

@@ -6639,6 +6657,66 @@ ParserResult<ImportDecl> Parser::parseDeclImport(ParseDeclOptions Flags,
66396657
return DCC.fixupParserResult(ID);
66406658
}
66416659

6660+
/// Parse an `using` declaration.
6661+
///
6662+
/// \verbatim
6663+
/// decl-using:
6664+
/// 'using' (@<attribute> | <modifier>)
6665+
/// \endverbatim
6666+
ParserResult<UsingDecl> Parser::parseDeclUsing(ParseDeclOptions Flags,
6667+
DeclAttributes &Attributes) {
6668+
assert(Tok.isContextualKeyword("using"));
6669+
DebuggerContextChange DCC(*this);
6670+
6671+
SourceLoc UsingLoc = consumeToken();
6672+
6673+
if (Tok.is(tok::code_complete)) {
6674+
if (CodeCompletionCallbacks) {
6675+
CodeCompletionCallbacks->completeUsingDecl();
6676+
}
6677+
return makeParserCodeCompletionStatus();
6678+
}
6679+
6680+
SourceLoc AtLoc;
6681+
// @<<attribute>>
6682+
if (Tok.is(tok::at_sign))
6683+
AtLoc = consumeToken();
6684+
6685+
SourceLoc SpecifierLoc;
6686+
Identifier RawSpecifier;
6687+
6688+
if (parseIdentifier(RawSpecifier, SpecifierLoc,
6689+
/*diagnoseDollarPrefix=*/false,
6690+
diag::expected_identifier_in_decl, "using"))
6691+
return nullptr;
6692+
6693+
std::optional<UsingSpecifier> Specifier =
6694+
llvm::StringSwitch<std::optional<UsingSpecifier>>(RawSpecifier.str())
6695+
.Case("MainActor", UsingSpecifier::MainActor)
6696+
.Case("nonisolated", UsingSpecifier::nonisolated)
6697+
.Default(std::nullopt);
6698+
6699+
if (!Specifier) {
6700+
diagnose(SpecifierLoc, diag::using_decl_invalid_specifier, RawSpecifier,
6701+
AtLoc.isValid());
6702+
return nullptr;
6703+
}
6704+
6705+
// Complain the `using` not being at top-level only after the specifier
6706+
// has been consumed, otherwise the specifier is going to be interpreted
6707+
// as a start of another declaration.
6708+
if (!CodeCompletionCallbacks && !DCC.movedToTopLevel() &&
6709+
!(Flags & PD_AllowTopLevel)) {
6710+
diagnose(UsingLoc, diag::decl_inner_scope);
6711+
return nullptr;
6712+
}
6713+
6714+
auto *UD = UsingDecl::create(Context, UsingLoc, SpecifierLoc, *Specifier,
6715+
CurDeclContext);
6716+
UD->attachParsedAttrs(Attributes);
6717+
return DCC.fixupParserResult(UD);
6718+
}
6719+
66426720
/// Parse an inheritance clause.
66436721
///
66446722
/// \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: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// RUN: %target-typecheck-verify-swift
2+
3+
using @MainActor
4+
using nonisolated
5+
6+
using @Test // expected-error {{'using' declaration does not support 'Test' attribute}}
7+
using test // expected-error {{'using' declaration does not support 'test' modifier}}
8+
9+
using
10+
@MainActor
11+
12+
using
13+
nonisolated
14+
15+
do {
16+
func
17+
using (x: Int) {}
18+
19+
using(x: 42)
20+
}
21+
22+
do {
23+
func
24+
using
25+
(x: Int) {}
26+
27+
using(x: 42)
28+
}
29+
30+
let
31+
using = 42
32+
33+
let (x: Int, using: String) = (x: 42, using: "")
34+
35+
do {
36+
using @MainActor // expected-error {{declaration is only valid at file scope}}
37+
}
38+
39+
func test() {
40+
using @MainActor // expected-error {{declaration is only valid at file scope}}
41+
}
42+
43+
struct S {
44+
var x: Int {
45+
using @MainActor // expected-error {{declaration is only valid at file scope}}
46+
}
47+
48+
using @MainActor func test() {}
49+
// expected-error@-1 {{declaration is only valid at file scope}}
50+
// expected-error@-2 {{consecutive declarations on a line must be separated by ';'}}
51+
52+
using nonisolated subscript(a: Int) -> Bool { false }
53+
// expected-error@-1 {{declaration is only valid at file scope}}
54+
// expected-error@-2 {{consecutive declarations on a line must be separated by ';'}}
55+
}

0 commit comments

Comments
 (0)