Skip to content

Commit cb0f4b9

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 f77bb25 commit cb0f4b9

File tree

8 files changed

+174
-25
lines changed

8 files changed

+174
-25
lines changed

CodeGeneration/Sources/SyntaxSupport/CompatibilityLayer.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,8 @@ public struct CompatibilityLayer {
180180
vars += children.filter { knownVars.insert($0).inserted }
181181

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

CodeGeneration/Sources/SyntaxSupport/Traits.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,15 @@ 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

2728
init(
2829
traitName: String,
2930
baseKind: SyntaxNodeKind? = nil,
3031
documentation: String? = nil,
32+
requiresInit: Bool = false,
3133
children: [Child],
3234
childHistory: Child.History = []
3335
) {
@@ -36,6 +38,7 @@ public class Trait {
3638
self.baseKind = baseKind
3739
self.protocolName = .identifier("\(traitName)Syntax")
3840
self.documentation = SwiftSyntax.Trivia.docCommentTrivia(from: documentation)
41+
self.requiresInit = requiresInit
3942
self.children = children
4043
self.childHistory = childHistory
4144
}
@@ -52,6 +55,7 @@ public let TRAITS: [Trait] = [
5255
Trait(
5356
traitName: "DeclGroup",
5457
baseKind: .decl,
58+
requiresInit: true,
5559
children: [
5660
Child(
5761
name: "header",

CodeGeneration/Sources/generate-swift-syntax/InitSignature+Extensions.swift

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,11 @@ extension InitSignature {
2828
return "init(leadingTrivia:\(renamedArguments)trailingTrivia:)"
2929
}
3030

31-
func generateInitializerDeclHeader() -> SyntaxNodeString {
31+
func generateInitializerDeclHeader(isRequirement: Bool = false) -> SyntaxNodeString {
32+
let stub = isRequirement ? "init?" : "public init"
33+
3234
if children.isEmpty {
33-
return "public init()"
35+
return "\(raw: stub)()"
3436
}
3537

3638
func createFunctionParameterSyntax(for child: Child) -> FunctionParameterSyntax {
@@ -63,19 +65,25 @@ extension InitSignature {
6365
)
6466
}
6567

68+
func transformParam(_ param: FunctionParameterSyntax) -> FunctionParameterSyntax {
69+
isRequirement ? param.with(\.defaultValue, nil) : param
70+
}
71+
6672
let params = FunctionParameterListSyntax {
67-
FunctionParameterSyntax("leadingTrivia: Trivia? = nil")
73+
transformParam(FunctionParameterSyntax("leadingTrivia: Trivia? = nil"))
6874

6975
for child in children {
70-
createFunctionParameterSyntax(for: child)
76+
transformParam(createFunctionParameterSyntax(for: child))
7177
}
7278

73-
FunctionParameterSyntax("trailingTrivia: Trivia? = nil")
74-
.with(\.leadingTrivia, .newline)
79+
transformParam(
80+
FunctionParameterSyntax("trailingTrivia: Trivia? = nil")
81+
.with(\.leadingTrivia, .newline)
82+
)
7583
}
7684

7785
return """
78-
public init(
86+
\(raw: stub)(
7987
\(params)
8088
)
8189
"""

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: 13 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -177,32 +177,27 @@ 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
191+
let newSelf = Self(
192+
leadingTrivia: nil,
193+
header: header,
194+
memberBlock: try MemberBlockSyntax(members: membersBuilder()),
195+
trailingTrivia: nil
196+
)
197+
else {
198+
throw SyntaxStringInterpolationInvalidHeaderForNodeTypeError(declType: Self.self, headerNode: header)
200199
}
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())
200+
self = newSelf
206201
}
207202
}
208203

0 commit comments

Comments
 (0)