Skip to content

Commit 251492d

Browse files
authored
Merge pull request #1216 from DougGregor/accessor-decl-interpolation
Accessor decl interpolation
2 parents 80b28f4 + 18f57b6 commit 251492d

File tree

7 files changed

+105
-55
lines changed

7 files changed

+105
-55
lines changed

CodeGeneration/Sources/SyntaxSupport/gyb_generated/DeclNodes.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1026,6 +1026,7 @@ public let DECL_NODES: [Node] = [
10261026
traits: [
10271027
"Attributed"
10281028
],
1029+
parserFunction: "parseAccessorDecl",
10291030
children: [
10301031
Child(name: "Attributes",
10311032
kind: "AttributeList",

Sources/SwiftParser/Declarations.swift

Lines changed: 70 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1543,12 +1543,14 @@ extension Parser {
15431543
var token: RawTokenSyntax
15441544
}
15451545

1546-
mutating func parseAccessorIntroducer() -> AccessorIntroducer? {
1546+
mutating func parseAccessorIntroducer(
1547+
forcedKind: (AccessorKind, TokenConsumptionHandle)? = nil
1548+
) -> AccessorIntroducer? {
15471549
// Check there is an identifier before consuming
15481550
var look = self.lookahead()
15491551
let _ = look.consumeAttributeList()
15501552
let hasModifier = look.consume(ifAny: [.contextualKeyword(.mutating), .contextualKeyword(.nonmutating), .contextualKeyword(.__consuming)]) != nil
1551-
guard let (kind, handle) = look.at(anyIn: AccessorKind.self) else {
1553+
guard let (kind, handle) = look.at(anyIn: AccessorKind.self) ?? forcedKind else {
15521554
return nil
15531555
}
15541556

@@ -1611,15 +1613,18 @@ extension Parser {
16111613
return specifiers
16121614
}
16131615

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

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

Sources/SwiftParser/generated/Parser+Entry.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,14 @@ public protocol SyntaxParseable: SyntaxProtocol {
4343
static func parse(from parser: inout Parser) -> Self
4444
}
4545

46+
extension AccessorDeclSyntax: SyntaxParseable {
47+
public static func parse(from parser: inout Parser) -> Self {
48+
let node = parser.parseAccessorDecl()
49+
let raw = RawSyntax(parser.parseRemainder(into: node))
50+
return Syntax(raw: raw).cast(Self.self)
51+
}
52+
}
53+
4654
extension AttributeSyntax: SyntaxParseable {
4755
public static func parse(from parser: inout Parser) -> Self {
4856
let node = parser.parseAttribute()

Sources/SwiftSyntaxBuilder/generated/SyntaxExpressibleByStringInterpolationConformances.swift

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,12 @@ extension SyntaxParseable {
2626
}
2727
}
2828

29-
extension AccessorDeclSyntax: SyntaxExpressibleByStringInterpolation {
29+
extension AccessorDeclSyntax: SyntaxExpressibleByStringInterpolation {
30+
public init(stringInterpolationOrThrow stringInterpolation: SyntaxStringInterpolation) throws {
31+
self = try performParse(source: stringInterpolation.sourceText, parse: { parser in
32+
return Self.parse(from: &parser)
33+
})
34+
}
3035
}
3136

3237
extension ActorDeclSyntax: SyntaxExpressibleByStringInterpolation {

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,

Tests/SwiftSyntaxBuilderTest/StringInterpolation.swift

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -421,4 +421,22 @@ final class StringInterpolationTests: XCTestCase {
421421
"""
422422
)
423423
}
424+
425+
func testAccessorInterpolation() {
426+
let setter: AccessorDeclSyntax =
427+
"""
428+
set(newValue) {
429+
_storage = newValue
430+
}
431+
"""
432+
XCTAssertTrue(setter.is(AccessorDeclSyntax.self))
433+
AssertStringsEqualWithDiff(
434+
setter.description,
435+
"""
436+
set(newValue) {
437+
_storage = newValue
438+
}
439+
"""
440+
)
441+
}
424442
}

gyb_syntax_support/DeclNodes.py

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

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

0 commit comments

Comments
 (0)