Skip to content

Commit 89c3b0b

Browse files
committed
[Parse] Implement parsing for using declarations
(cherry picked from commit 4ad27ba)
1 parent c38cf0f commit 89c3b0b

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
@@ -2180,5 +2180,12 @@ ERROR(nonisolated_nonsending_expected_rparen,PointsToFirstBadToken,
21802180
ERROR(nonisolated_nonsending_repeated,none,
21812181
"parameter may have at most one 'nonisolated(nonsending)' specifier", ())
21822182

2183+
//------------------------------------------------------------------------------
2184+
// MARK: using @<attribute> or using <identifier>
2185+
//------------------------------------------------------------------------------
2186+
ERROR(using_decl_invalid_specifier,PointsToFirstBadToken,
2187+
"'using' declaration does not support %0 %select{modifier|attribute}1",
2188+
(Identifier, bool))
2189+
21832190
#define UNDEFINE_DIAGNOSTIC_MACROS
21842191
#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
@@ -5913,6 +5913,17 @@ bool Parser::isStartOfSwiftDecl(bool allowPoundIfAttributes,
59135913
}
59145914
}
59155915

5916+
// `using @<attribute>` or `using <identifier>`.
5917+
if (Tok.isContextualKeyword("using")) {
5918+
// `using` declarations don't support attributes or modifiers.
5919+
if (hadAttrsOrModifiers)
5920+
return false;
5921+
5922+
return !Tok2.isAtStartOfLine() &&
5923+
(Tok2.is(tok::at_sign) || Tok2.is(tok::identifier) ||
5924+
Tok2.is(tok::code_complete));
5925+
}
5926+
59165927
// If the next token is obviously not the start of a decl, bail early.
59175928
if (!isKeywordPossibleDeclStart(Context.LangOpts, Tok2))
59185929
return false;
@@ -6267,6 +6278,17 @@ ParserStatus Parser::parseDecl(bool IsAtStartOfLineOrPreviousHadSemi,
62676278
break;
62686279
}
62696280

6281+
// `using @<attribute>` or `using <identifier>`
6282+
if (Tok.isContextualKeyword("using")) {
6283+
auto nextToken = peekToken();
6284+
if (!nextToken.isAtStartOfLine() &&
6285+
(nextToken.is(tok::at_sign) || nextToken.is(tok::identifier) ||
6286+
nextToken.is(tok::code_complete))) {
6287+
DeclResult = parseDeclUsing(Flags, Attributes);
6288+
break;
6289+
}
6290+
}
6291+
62706292
if (Flags.contains(PD_HasContainerType) &&
62716293
IsAtStartOfLineOrPreviousHadSemi) {
62726294

@@ -6614,6 +6636,65 @@ ParserResult<ImportDecl> Parser::parseDeclImport(ParseDeclOptions Flags,
66146636
return DCC.fixupParserResult(ID);
66156637
}
66166638

6639+
/// Parse an `using` declaration.
6640+
///
6641+
/// \verbatim
6642+
/// decl-using:
6643+
/// 'using' (@<attribute> | <modifier>)
6644+
/// \endverbatim
6645+
ParserResult<UsingDecl> Parser::parseDeclUsing(ParseDeclOptions Flags,
6646+
DeclAttributes &Attributes) {
6647+
assert(Tok.isContextualKeyword("using"));
6648+
DebuggerContextChange DCC(*this);
6649+
6650+
SourceLoc UsingLoc = consumeToken();
6651+
6652+
if (Tok.is(tok::code_complete)) {
6653+
if (CodeCompletionCallbacks) {
6654+
CodeCompletionCallbacks->completeUsingDecl();
6655+
}
6656+
return makeParserCodeCompletionStatus();
6657+
}
6658+
6659+
SourceLoc AtLoc;
6660+
// @<<attribute>>
6661+
if (Tok.is(tok::at_sign))
6662+
AtLoc = consumeToken();
6663+
6664+
SourceLoc SpecifierLoc;
6665+
Identifier RawSpecifier;
6666+
6667+
if (parseIdentifier(RawSpecifier, SpecifierLoc,
6668+
/*diagnoseDollarPrefix=*/false,
6669+
diag::expected_identifier_in_decl, "using"))
6670+
return nullptr;
6671+
6672+
std::optional<UsingSpecifier> Specifier =
6673+
llvm::StringSwitch<std::optional<UsingSpecifier>>(RawSpecifier.str())
6674+
.Case("MainActor", UsingSpecifier::MainActor)
6675+
.Case("nonisolated", UsingSpecifier::nonisolated)
6676+
.Default(std::nullopt);
6677+
6678+
if (!Specifier) {
6679+
diagnose(SpecifierLoc, diag::using_decl_invalid_specifier, RawSpecifier,
6680+
AtLoc.isValid());
6681+
return nullptr;
6682+
}
6683+
6684+
// Complain the `using` not being at top-level only after the specifier
6685+
// has been consumed, otherwise the specifier is going to be interpreted
6686+
// as a start of another declaration.
6687+
if (!CodeCompletionCallbacks && !DCC.movedToTopLevel() &&
6688+
!(Flags & PD_AllowTopLevel)) {
6689+
diagnose(UsingLoc, diag::decl_inner_scope);
6690+
return nullptr;
6691+
}
6692+
6693+
auto *UD = UsingDecl::create(Context, UsingLoc, AtLoc ? AtLoc : SpecifierLoc,
6694+
*Specifier, CurDeclContext);
6695+
return DCC.fixupParserResult(UD);
6696+
}
6697+
66176698
/// Parse an inheritance clause.
66186699
///
66196700
/// \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)