Skip to content

Commit 3a10a8f

Browse files
committed
Make it possible to parse an accessor declaration on its own
1 parent 8e6ae6e commit 3a10a8f

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
@@ -1547,12 +1547,14 @@ extension Parser {
15471547
var token: RawTokenSyntax
15481548
}
15491549

1550-
mutating func parseAccessorIntroducer() -> AccessorIntroducer? {
1550+
mutating func parseAccessorIntroducer(
1551+
forcedKind: (AccessorKind, TokenConsumptionHandle)? = nil
1552+
) -> AccessorIntroducer? {
15511553
// Check there is an identifier before consuming
15521554
var look = self.lookahead()
15531555
let _ = look.consumeAttributeList()
15541556
let hasModifier = look.consume(ifAny: [.contextualKeyword(.mutating), .contextualKeyword(.nonmutating), .contextualKeyword(.__consuming)]) != nil
1555-
guard let (kind, handle) = look.at(anyIn: AccessorKind.self) else {
1557+
guard let (kind, handle) = look.at(anyIn: AccessorKind.self) ?? forcedKind else {
15561558
return nil
15571559
}
15581560

@@ -1615,15 +1617,17 @@ extension Parser {
16151617
return specifiers
16161618
}
16171619

1618-
/// Parse the body of a variable declaration. This can include explicit
1619-
/// getters, setters, and observers, or the body of a computed property.
1620+
/// Parse an accessor.
1621+
mutating func parseAccessorDecl() -> RawAccessorDeclSyntax {
1622+
let introducer = parseAccessorIntroducer(forcedKind: (.get, TokenConsumptionHandle(tokenKind: .contextualKeyword, missing: true)))!
1623+
return parseAccessorDecl(introducer: introducer)
1624+
}
1625+
1626+
/// Parse an accessor once we know we have an introducer
16201627
///
16211628
/// Grammar
16221629
/// =======
16231630
///
1624-
/// getter-setter-block → code-block
1625-
/// getter-setter-block → { getter-clause setter-clause opt }
1626-
/// getter-setter-block → { setter-clause getter-clause }
16271631
/// getter-clause → attributes opt mutation-modifier opt get code-block
16281632
/// setter-clause → attributes opt mutation-modifier opt set setter-name opt code-block
16291633
/// setter-name → ( identifier )
@@ -1635,6 +1639,63 @@ extension Parser {
16351639
/// willSet-didSet-block → { didSet-clause willSet-clause opt }
16361640
/// willSet-clause → attributes opt willSet setter-name opt code-block
16371641
/// didSet-clause → attributes opt didSet setter-name opt code-block
1642+
private mutating func parseAccessorDecl(
1643+
introducer: AccessorIntroducer
1644+
) -> RawAccessorDeclSyntax {
1645+
// 'set' and 'willSet' can have an optional name. This isn't valid in a
1646+
// protocol, but we parse and then reject it for better QoI.
1647+
//
1648+
// set-name ::= '(' identifier ')'
1649+
let parameter: RawAccessorParameterSyntax?
1650+
if [AccessorKind.set, .willSet, .didSet].contains(introducer.kind), let lparen = self.consume(if: .leftParen) {
1651+
let (unexpectedBeforeName, name) = self.expectIdentifier()
1652+
let (unexpectedBeforeRParen, rparen) = self.expect(.rightParen)
1653+
parameter = RawAccessorParameterSyntax(
1654+
leftParen: lparen,
1655+
unexpectedBeforeName,
1656+
name: name,
1657+
unexpectedBeforeRParen,
1658+
rightParen: rparen,
1659+
arena: self.arena
1660+
)
1661+
} else {
1662+
parameter = nil
1663+
}
1664+
1665+
// Next, parse effects specifiers. While it's only valid to have them
1666+
// on 'get' accessors, we also emit diagnostics if they show up on others.
1667+
let asyncKeyword: RawTokenSyntax?
1668+
let throwsKeyword: RawTokenSyntax?
1669+
if self.at(anyIn: EffectsSpecifier.self) != nil {
1670+
asyncKeyword = self.parseEffectsSpecifier()
1671+
throwsKeyword = self.parseEffectsSpecifier()
1672+
} else {
1673+
asyncKeyword = nil
1674+
throwsKeyword = nil
1675+
}
1676+
1677+
let body = self.parseOptionalCodeBlock()
1678+
return RawAccessorDeclSyntax(
1679+
attributes: introducer.attributes,
1680+
modifier: introducer.modifier,
1681+
accessorKind: introducer.token,
1682+
parameter: parameter,
1683+
asyncKeyword: asyncKeyword,
1684+
throwsKeyword: throwsKeyword,
1685+
body: body,
1686+
arena: self.arena
1687+
)
1688+
}
1689+
1690+
/// Parse the body of a variable declaration. This can include explicit
1691+
/// getters, setters, and observers, or the body of a computed property.
1692+
///
1693+
/// Grammar
1694+
/// =======
1695+
///
1696+
/// getter-setter-block → code-block
1697+
/// getter-setter-block → { getter-clause setter-clause opt }
1698+
/// getter-setter-block → { setter-clause getter-clause }
16381699
@_spi(RawSyntax)
16391700
public mutating func parseGetSet() -> RawSubscriptDeclSyntax.Accessor {
16401701
// Parse getter and setter.
@@ -1689,52 +1750,7 @@ extension Parser {
16891750
)
16901751
}
16911752

1692-
// 'set' and 'willSet' can have an optional name. This isn't valid in a
1693-
// protocol, but we parse and then reject it for better QoI.
1694-
//
1695-
// set-name ::= '(' identifier ')'
1696-
let parameter: RawAccessorParameterSyntax?
1697-
if [AccessorKind.set, .willSet, .didSet].contains(introducer.kind), let lparen = self.consume(if: .leftParen) {
1698-
let (unexpectedBeforeName, name) = self.expectIdentifier()
1699-
let (unexpectedBeforeRParen, rparen) = self.expect(.rightParen)
1700-
parameter = RawAccessorParameterSyntax(
1701-
leftParen: lparen,
1702-
unexpectedBeforeName,
1703-
name: name,
1704-
unexpectedBeforeRParen,
1705-
rightParen: rparen,
1706-
arena: self.arena
1707-
)
1708-
} else {
1709-
parameter = nil
1710-
}
1711-
1712-
// Next, parse effects specifiers. While it's only valid to have them
1713-
// on 'get' accessors, we also emit diagnostics if they show up on others.
1714-
let asyncKeyword: RawTokenSyntax?
1715-
let throwsKeyword: RawTokenSyntax?
1716-
if self.at(anyIn: EffectsSpecifier.self) != nil {
1717-
asyncKeyword = self.parseEffectsSpecifier()
1718-
throwsKeyword = self.parseEffectsSpecifier()
1719-
} else {
1720-
asyncKeyword = nil
1721-
throwsKeyword = nil
1722-
}
1723-
1724-
let body = self.parseOptionalCodeBlock()
1725-
1726-
elements.append(
1727-
RawAccessorDeclSyntax(
1728-
attributes: introducer.attributes,
1729-
modifier: introducer.modifier,
1730-
accessorKind: introducer.token,
1731-
parameter: parameter,
1732-
asyncKeyword: asyncKeyword,
1733-
throwsKeyword: throwsKeyword,
1734-
body: body,
1735-
arena: self.arena
1736-
)
1737-
)
1753+
elements.append(parseAccessorDecl(introducer: introducer))
17381754
}
17391755
}
17401756

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)