Skip to content

Commit 17d8e3a

Browse files
committed
Make it possible to parse an accessor declaration on its own
1 parent f5f6ae2 commit 17d8e3a

File tree

3 files changed

+71
-54
lines changed

3 files changed

+71
-54
lines changed

Sources/SwiftParser/Declarations.swift

Lines changed: 69 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1522,12 +1522,14 @@ extension Parser {
15221522
var token: RawTokenSyntax
15231523
}
15241524

1525-
mutating func parseAccessorIntroducer() -> AccessorIntroducer? {
1525+
mutating func parseAccessorIntroducer(
1526+
forcedKind: (AccessorKind, TokenConsumptionHandle)? = nil
1527+
) -> AccessorIntroducer? {
15261528
// Check there is an identifier before consuming
15271529
var look = self.lookahead()
15281530
let _ = look.consumeAttributeList()
15291531
let hasModifier = look.consume(ifAny: [], contextualKeywords: ["mutating", "nonmutating", "__consuming"]) != nil
1530-
guard let (kind, handle) = look.at(anyIn: AccessorKind.self) else {
1532+
guard let (kind, handle) = look.at(anyIn: AccessorKind.self) ?? forcedKind else {
15311533
return nil
15321534
}
15331535

@@ -1590,15 +1592,17 @@ extension Parser {
15901592
return specifiers
15911593
}
15921594

1593-
/// Parse the body of a variable declaration. This can include explicit
1594-
/// getters, setters, and observers, or the body of a computed property.
1595+
/// Parse an accessor.
1596+
mutating func parseAccessorDecl() -> RawAccessorDeclSyntax {
1597+
let introducer = parseAccessorIntroducer(forcedKind: (.get, TokenConsumptionHandle(tokenKind: .contextualKeyword, missing: true)))!
1598+
return parseAccessorDecl(introducer: introducer)
1599+
}
1600+
1601+
/// Parse an accessor once we know we have an introducer
15951602
///
15961603
/// Grammar
15971604
/// =======
15981605
///
1599-
/// getter-setter-block → code-block
1600-
/// getter-setter-block → { getter-clause setter-clause opt }
1601-
/// getter-setter-block → { setter-clause getter-clause }
16021606
/// getter-clause → attributes opt mutation-modifier opt get code-block
16031607
/// setter-clause → attributes opt mutation-modifier opt set setter-name opt code-block
16041608
/// setter-name → ( identifier )
@@ -1610,6 +1614,63 @@ extension Parser {
16101614
/// willSet-didSet-block → { didSet-clause willSet-clause opt }
16111615
/// willSet-clause → attributes opt willSet setter-name opt code-block
16121616
/// didSet-clause → attributes opt didSet setter-name opt code-block
1617+
private mutating func parseAccessorDecl(
1618+
introducer: AccessorIntroducer
1619+
) -> RawAccessorDeclSyntax {
1620+
// 'set' and 'willSet' can have an optional name. This isn't valid in a
1621+
// protocol, but we parse and then reject it for better QoI.
1622+
//
1623+
// set-name ::= '(' identifier ')'
1624+
let parameter: RawAccessorParameterSyntax?
1625+
if [AccessorKind.set, .willSet, .didSet].contains(introducer.kind), let lparen = self.consume(if: .leftParen) {
1626+
let (unexpectedBeforeName, name) = self.expectIdentifier()
1627+
let (unexpectedBeforeRParen, rparen) = self.expect(.rightParen)
1628+
parameter = RawAccessorParameterSyntax(
1629+
leftParen: lparen,
1630+
unexpectedBeforeName,
1631+
name: name,
1632+
unexpectedBeforeRParen,
1633+
rightParen: rparen,
1634+
arena: self.arena
1635+
)
1636+
} else {
1637+
parameter = nil
1638+
}
1639+
1640+
// Next, parse effects specifiers. While it's only valid to have them
1641+
// on 'get' accessors, we also emit diagnostics if they show up on others.
1642+
let asyncKeyword: RawTokenSyntax?
1643+
let throwsKeyword: RawTokenSyntax?
1644+
if self.at(anyIn: EffectsSpecifier.self) != nil {
1645+
asyncKeyword = self.parseEffectsSpecifier()
1646+
throwsKeyword = self.parseEffectsSpecifier()
1647+
} else {
1648+
asyncKeyword = nil
1649+
throwsKeyword = nil
1650+
}
1651+
1652+
let body = self.parseOptionalCodeBlock()
1653+
return RawAccessorDeclSyntax(
1654+
attributes: introducer.attributes,
1655+
modifier: introducer.modifier,
1656+
accessorKind: introducer.token,
1657+
parameter: parameter,
1658+
asyncKeyword: asyncKeyword,
1659+
throwsKeyword: throwsKeyword,
1660+
body: body,
1661+
arena: self.arena
1662+
)
1663+
}
1664+
1665+
/// Parse the body of a variable declaration. This can include explicit
1666+
/// getters, setters, and observers, or the body of a computed property.
1667+
///
1668+
/// Grammar
1669+
/// =======
1670+
///
1671+
/// getter-setter-block → code-block
1672+
/// getter-setter-block → { getter-clause setter-clause opt }
1673+
/// getter-setter-block → { setter-clause getter-clause }
16131674
@_spi(RawSyntax)
16141675
public mutating func parseGetSet() -> RawSubscriptDeclSyntax.Accessor {
16151676
// Parse getter and setter.
@@ -1664,52 +1725,7 @@ extension Parser {
16641725
)
16651726
}
16661727

1667-
// 'set' and 'willSet' can have an optional name. This isn't valid in a
1668-
// protocol, but we parse and then reject it for better QoI.
1669-
//
1670-
// set-name ::= '(' identifier ')'
1671-
let parameter: RawAccessorParameterSyntax?
1672-
if [AccessorKind.set, .willSet, .didSet].contains(introducer.kind), let lparen = self.consume(if: .leftParen) {
1673-
let (unexpectedBeforeName, name) = self.expectIdentifier()
1674-
let (unexpectedBeforeRParen, rparen) = self.expect(.rightParen)
1675-
parameter = RawAccessorParameterSyntax(
1676-
leftParen: lparen,
1677-
unexpectedBeforeName,
1678-
name: name,
1679-
unexpectedBeforeRParen,
1680-
rightParen: rparen,
1681-
arena: self.arena
1682-
)
1683-
} else {
1684-
parameter = nil
1685-
}
1686-
1687-
// Next, parse effects specifiers. While it's only valid to have them
1688-
// on 'get' accessors, we also emit diagnostics if they show up on others.
1689-
let asyncKeyword: RawTokenSyntax?
1690-
let throwsKeyword: RawTokenSyntax?
1691-
if self.at(anyIn: EffectsSpecifier.self) != nil {
1692-
asyncKeyword = self.parseEffectsSpecifier()
1693-
throwsKeyword = self.parseEffectsSpecifier()
1694-
} else {
1695-
asyncKeyword = nil
1696-
throwsKeyword = nil
1697-
}
1698-
1699-
let body = self.parseOptionalCodeBlock()
1700-
1701-
elements.append(
1702-
RawAccessorDeclSyntax(
1703-
attributes: introducer.attributes,
1704-
modifier: introducer.modifier,
1705-
accessorKind: introducer.token,
1706-
parameter: parameter,
1707-
asyncKeyword: asyncKeyword,
1708-
throwsKeyword: throwsKeyword,
1709-
body: body,
1710-
arena: self.arena
1711-
)
1712-
)
1728+
elements.append(parseAccessorDecl(introducer: introducer))
17131729
}
17141730
}
17151731

Sources/_SwiftSyntaxMacros/PeerDeclarationMacro.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ public protocol PeerDeclarationMacro: DeclarationMacro {
1515
/// particular expansion context.
1616
///
1717
/// The macro expansion can introduce "peer" declarations that sit alongside
18-
/// the
18+
/// the given declaration.
1919
static func expansion(
2020
of node: CustomAttributeSyntax,
2121
attachedTo declaration: DeclSyntax,

gyb_syntax_support/DeclNodes.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -556,6 +556,7 @@
556556

557557
Node('AccessorDecl', name_for_diagnostics='accessor', kind='Decl',
558558
traits=['Attributed'],
559+
parser_function='parseAccessorDecl',
559560
children=[
560561
Child('Attributes', kind='AttributeList', name_for_diagnostics='attributes',
561562
collection_element_name='Attribute', is_optional=True),

0 commit comments

Comments
 (0)