Skip to content

Commit bfce22b

Browse files
committed
Rework HasTrailingMemberDeclBlock
`DeclGroup` now requires an initializer that can build a group from a header and member block. With that in place, we can reimplement `HasTrailingMemberDeclBlock` to avoid reparsing anything. Which is kind of neat.
1 parent 3404a13 commit bfce22b

File tree

7 files changed

+157
-19
lines changed

7 files changed

+157
-19
lines changed

CodeGeneration/Sources/SyntaxSupport/CompatibilityLayers.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,8 @@ public struct CompatibilityLayers /*: Sendable*/ {
173173
vars += children.filter { knownVars.insert($0).inserted }
174174

175175
// We don't create compatibility layers for protocol requirement inits.
176+
// In theory, we *could* create compatibility layers for traits with `requiresInit: true`, but the only one we'd
177+
// create so far is unnecessary and tricky to implement.
176178
if !areRequirements {
177179
initSignatures.append(InitSignature(children: children))
178180
}

CodeGeneration/Sources/SyntaxSupport/Traits.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,17 @@ public class Trait {
2121
public let baseKind: SyntaxNodeKind?
2222
public let protocolName: TokenSyntax
2323
public let documentation: SwiftSyntax.Trivia
24+
public let requiresInit: Bool
2425
public let children: [Child]
2526
public let childHistory: Child.History
2627

27-
init(traitName: String, baseKind: SyntaxNodeKind? = nil, documentation: String? = nil, children: [Child], childHistory: Child.History = []) {
28+
init(traitName: String, baseKind: SyntaxNodeKind? = nil, documentation: String? = nil, requiresInit: Bool = false, children: [Child], childHistory: Child.History = []) {
2829
precondition(baseKind?.isBase != false, "`baseKind` must be a base syntax node kind")
2930
self.traitName = traitName
3031
self.baseKind = baseKind
3132
self.protocolName = .identifier("\(traitName)Syntax")
3233
self.documentation = SwiftSyntax.Trivia.docCommentTrivia(from: documentation)
34+
self.requiresInit = requiresInit
3335
self.children = children
3436
self.childHistory = childHistory
3537
}
@@ -46,6 +48,7 @@ public let TRAITS: [Trait] = [
4648
Trait(
4749
traitName: "DeclGroup",
4850
baseKind: .decl,
51+
requiresInit: true,
4952
children: [
5053
Child(
5154
name: "header",

CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntax/SyntaxTraitsFile.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,16 @@ let syntaxTraitsFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
3737
"""
3838
)
3939
}
40+
41+
if trait.requiresInit {
42+
let signature = InitSignature(trait)
43+
DeclSyntax(
44+
"""
45+
\(signature.generateInitializerDocComment())
46+
\(signature.generateInitializerDeclHeader(isRequirement: true))
47+
"""
48+
)
49+
}
4050
}
4151

4252
try! ExtensionDeclSyntax("extension \(trait.protocolName)") {

Sources/SwiftSyntax/CustomTraits.swift

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,23 @@ extension ActorDeclSyntax {
3030
actorHeader = newValue.cast(ActorDeclHeaderSyntax.self)
3131
}
3232
}
33+
34+
public init?(
35+
leadingTrivia: Trivia? = nil,
36+
header: some DeclGroupHeaderSyntaxProtocol,
37+
memberBlock: MemberBlockSyntax,
38+
trailingTrivia: Trivia? = nil
39+
) {
40+
guard let actorHeader = header.as(ActorDeclHeaderSyntax.self) else {
41+
return nil
42+
}
43+
self.init(
44+
leadingTrivia: leadingTrivia,
45+
actorHeader: actorHeader,
46+
memberBlock: memberBlock,
47+
trailingTrivia: trailingTrivia
48+
)
49+
}
3350
}
3451

3552
extension ClassDeclHeaderSyntax {
@@ -52,6 +69,23 @@ extension ClassDeclSyntax {
5269
classHeader = newValue.cast(ClassDeclHeaderSyntax.self)
5370
}
5471
}
72+
73+
public init?(
74+
leadingTrivia: Trivia? = nil,
75+
header: some DeclGroupHeaderSyntaxProtocol,
76+
memberBlock: MemberBlockSyntax,
77+
trailingTrivia: Trivia? = nil
78+
) {
79+
guard let classHeader = header.as(ClassDeclHeaderSyntax.self) else {
80+
return nil
81+
}
82+
self.init(
83+
leadingTrivia: leadingTrivia,
84+
classHeader: classHeader,
85+
memberBlock: memberBlock,
86+
trailingTrivia: trailingTrivia
87+
)
88+
}
5589
}
5690

5791
extension EnumDeclHeaderSyntax {
@@ -74,6 +108,23 @@ extension EnumDeclSyntax {
74108
enumHeader = newValue.cast(EnumDeclHeaderSyntax.self)
75109
}
76110
}
111+
112+
public init?(
113+
leadingTrivia: Trivia? = nil,
114+
header: some DeclGroupHeaderSyntaxProtocol,
115+
memberBlock: MemberBlockSyntax,
116+
trailingTrivia: Trivia? = nil
117+
) {
118+
guard let enumHeader = header.as(EnumDeclHeaderSyntax.self) else {
119+
return nil
120+
}
121+
self.init(
122+
leadingTrivia: leadingTrivia,
123+
enumHeader: enumHeader,
124+
memberBlock: memberBlock,
125+
trailingTrivia: trailingTrivia
126+
)
127+
}
77128
}
78129

79130
extension ExtensionDeclHeaderSyntax {
@@ -96,6 +147,23 @@ extension ExtensionDeclSyntax {
96147
extensionHeader = newValue.cast(ExtensionDeclHeaderSyntax.self)
97148
}
98149
}
150+
151+
public init?(
152+
leadingTrivia: Trivia? = nil,
153+
header: some DeclGroupHeaderSyntaxProtocol,
154+
memberBlock: MemberBlockSyntax,
155+
trailingTrivia: Trivia? = nil
156+
) {
157+
guard let extensionHeader = header.as(ExtensionDeclHeaderSyntax.self) else {
158+
return nil
159+
}
160+
self.init(
161+
leadingTrivia: leadingTrivia,
162+
extensionHeader: extensionHeader,
163+
memberBlock: memberBlock,
164+
trailingTrivia: trailingTrivia
165+
)
166+
}
99167
}
100168

101169
extension MissingDeclHeaderSyntax {
@@ -129,6 +197,23 @@ extension ProtocolDeclSyntax {
129197
protocolHeader = newValue.cast(ProtocolDeclHeaderSyntax.self)
130198
}
131199
}
200+
201+
public init?(
202+
leadingTrivia: Trivia? = nil,
203+
header: some DeclGroupHeaderSyntaxProtocol,
204+
memberBlock: MemberBlockSyntax,
205+
trailingTrivia: Trivia? = nil
206+
) {
207+
guard let protocolHeader = header.as(ProtocolDeclHeaderSyntax.self) else {
208+
return nil
209+
}
210+
self.init(
211+
leadingTrivia: leadingTrivia,
212+
protocolHeader: protocolHeader,
213+
memberBlock: memberBlock,
214+
trailingTrivia: trailingTrivia
215+
)
216+
}
132217
}
133218

134219
extension StructDeclHeaderSyntax {
@@ -151,6 +236,23 @@ extension StructDeclSyntax {
151236
structHeader = newValue.cast(StructDeclHeaderSyntax.self)
152237
}
153238
}
239+
240+
public init?(
241+
leadingTrivia: Trivia? = nil,
242+
header: some DeclGroupHeaderSyntaxProtocol,
243+
memberBlock: MemberBlockSyntax,
244+
trailingTrivia: Trivia? = nil
245+
) {
246+
guard let structHeader = header.as(StructDeclHeaderSyntax.self) else {
247+
return nil
248+
}
249+
self.init(
250+
leadingTrivia: leadingTrivia,
251+
structHeader: structHeader,
252+
memberBlock: memberBlock,
253+
trailingTrivia: trailingTrivia
254+
)
255+
}
154256
}
155257

156258
//==========================================================================//

Sources/SwiftSyntax/generated/SyntaxTraits.swift

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,18 @@ public protocol DeclGroupSyntax: SyntaxProtocol, DeclSyntaxProtocol {
7272
get
7373
set
7474
}
75+
76+
/// - Parameters:
77+
/// - leadingTrivia: Trivia to be prepended to the leading trivia of the node’s first token. If the node is empty, there is no token to attach the trivia to and the parameter is ignored.
78+
/// - header: The header of the declaration.
79+
/// - trailingTrivia: Trivia to be appended to the trailing trivia of the node’s last token. If the node is empty, there is no token to attach the trivia to and the parameter is ignored.
80+
81+
init?(
82+
leadingTrivia: Trivia? ,
83+
header: some DeclGroupHeaderSyntaxProtocol,
84+
memberBlock: MemberBlockSyntax,
85+
trailingTrivia: Trivia?
86+
)
7587
}
7688

7789
extension DeclGroupSyntax {

Sources/SwiftSyntaxBuilder/Syntax+StringInterpolation.swift

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,22 @@ public struct SyntaxStringInterpolationInvalidNodeTypeError: Error, CustomString
184184
}
185185
}
186186

187+
/// Describes an error when building a syntax node with string interpolation resulted in an unexpected node type.
188+
public struct SyntaxStringInterpolationInvalidHeaderForNodeTypeError: Error, CustomStringConvertible {
189+
let declType: SyntaxProtocol.Type
190+
let headerType: SyntaxProtocol.Type
191+
192+
/// Initialize the invalid node type error providing an expected type, and the actual node that resulted.
193+
public init<S: SyntaxProtocol>(declType: SyntaxProtocol.Type, headerNode: S) {
194+
self.declType = declType
195+
self.headerType = type(of: headerNode)
196+
}
197+
198+
public var description: String {
199+
return "Attempted to construct an \(declType) declaration with a header of mismatched type \(headerType)"
200+
}
201+
}
202+
187203
/// A string interpolation error based on a ``SwiftDiagnostics/Diagnostic``.
188204
struct SyntaxStringInterpolationDiagnosticError: Error, CustomStringConvertible {
189205
let diagnostics: [Diagnostic]

Sources/SwiftSyntaxBuilder/SyntaxNodeWithBody.swift

Lines changed: 11 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -177,32 +177,25 @@ public protocol HasTrailingMemberDeclBlock {
177177
///
178178
/// Throws an error if `header` defines a different node type than the type the initializer is called on. E.g. if calling `try StructDeclSyntax("class MyClass") {}`
179179
init(
180-
_ header: SyntaxNodeString,
180+
_ header: DeclGroupHeaderSyntax,
181181
@MemberBlockItemListBuilder membersBuilder: () throws -> MemberBlockItemListSyntax
182182
) throws
183183
}
184184

185-
extension HasTrailingMemberDeclBlock where Self: DeclSyntaxProtocol {
185+
extension HasTrailingMemberDeclBlock where Self: DeclGroupSyntax {
186186
public init(
187-
_ header: SyntaxNodeString,
187+
_ header: DeclGroupHeaderSyntax,
188188
@MemberBlockItemListBuilder membersBuilder: () throws -> MemberBlockItemListSyntax
189189
) throws {
190-
// If the type provides a custom `SyntaxParseable` implementation, use that. Otherwise construct it as a
191-
// `DeclSyntax`.
192-
let decl: DeclSyntax
193-
var stringInterpolation = SyntaxStringInterpolation(literalCapacity: 1, interpolationCount: 1)
194-
stringInterpolation.appendInterpolation(header)
195-
stringInterpolation.appendLiteral(" {}")
196-
if let parsableType = Self.self as? SyntaxParseable.Type {
197-
decl = parsableType.init(stringInterpolation: stringInterpolation).cast(DeclSyntax.self)
198-
} else {
199-
decl = DeclSyntax(stringInterpolation: stringInterpolation)
190+
guard let newSelf = Self(
191+
leadingTrivia: nil,
192+
header: header,
193+
memberBlock: try MemberBlockSyntax(members: membersBuilder()),
194+
trailingTrivia: nil
195+
) else {
196+
throw SyntaxStringInterpolationInvalidHeaderForNodeTypeError(declType: Self.self, headerNode: header)
200197
}
201-
guard let castedDecl = decl.as(Self.self) else {
202-
throw SyntaxStringInterpolationInvalidNodeTypeError(expectedType: Self.self, actualNode: decl)
203-
}
204-
self = castedDecl
205-
self.memberBlock = try MemberBlockSyntax(members: membersBuilder())
198+
self = newSelf
206199
}
207200
}
208201

0 commit comments

Comments
 (0)