Skip to content

Commit 09f9c9b

Browse files
committed
Fix debugInitCall
I broke `debugInitCall` when I removed the custom mirrors for syntax nodes. Fix it again, also fixing some more issues along the way that previously caused us to generate Swift code that didn’t compile.
1 parent 087bb4a commit 09f9c9b

File tree

3 files changed

+53
-16
lines changed

3 files changed

+53
-16
lines changed

CodeGeneration/Sources/generate-swiftsyntax/templates/swiftsyntax/ChildNameForKeyPathFile.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ let childNameForKeyPathFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
2020
"""
2121
/// If the keyPath is one from a layout structure, return the property name
2222
/// of it.
23-
internal func childName(_ keyPath: AnyKeyPath) -> String?
23+
@_spi(RawSyntax)
24+
public func childName(_ keyPath: AnyKeyPath) -> String?
2425
"""
2526
) {
2627
try! SwitchExprSyntax("switch keyPath") {

Sources/SwiftSyntax/generated/ChildNameForKeyPath.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@
1414

1515
/// If the keyPath is one from a layout structure, return the property name
1616
/// of it.
17-
internal func childName(_ keyPath: AnyKeyPath) -> String? {
17+
@_spi(RawSyntax)
18+
public func childName(_ keyPath: AnyKeyPath) -> String? {
1819
switch keyPath {
1920
case \AccessPathComponentSyntax.unexpectedBeforeName:
2021
return "unexpectedBeforeName"

Sources/_SwiftSyntaxTestSupport/SyntaxProtocol+Initializer.swift

Lines changed: 49 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -103,26 +103,57 @@ private extension Trivia {
103103
}
104104
}
105105

106+
#if DEBUG
107+
/// We call into the Swift runtime to get the fully-qualified description of a
108+
/// type name in `debugInitCall`. This is fine-ish for two reaons:
109+
/// - The runtime is part of the ABI and is thus stable
110+
/// - We are only using this in a debug function that shouldn’t be called in
111+
/// any production app.
112+
@_silgen_name("swift_getTypeName")
113+
private func _getTypeName(_ type: Any.Type, qualified: Bool) -> (UnsafePointer<UInt8>, Int)
114+
115+
private func getTypeName(_ type: Any.Type, qualified: Bool) -> String? {
116+
let typeName = _getTypeName(type, qualified: true)
117+
let buffer = UnsafeBufferPointer(start: typeName.0, count: typeName.1)
118+
return String(decoding: buffer, as: UTF8.self)
119+
}
120+
106121
extension SyntaxProtocol {
107122
/// Returns a Swift expression that, when parsed, constructs this syntax node
108123
/// (or at least an expression that's very close to constructing this node, the addition of a few manual upcast by hand is still needed).
109124
/// The intended use case for this is to print a syntax tree and create a substructure assertion from the generated expression.
110125
/// When `includeTrivia` is set to `false`, the token's leading and trailing trivia will not be included in the generated expression.
126+
///
127+
/// - Warning: This is only designed for use in the debugger. Do not call it outside of the debugger.
128+
@available(*, deprecated, message: "For use in debugger only")
111129
public func debugInitCall(includeTrivia: Bool = true) -> String {
112130
return self.debugInitCallExpr(includeTrivia: includeTrivia).formatted(using: InitializerExprFormat()).description
113131
}
114132

115133
private func debugInitCallExpr(includeTrivia: Bool) -> ExprSyntax {
116-
let mirror = Mirror(reflecting: self)
117-
if self.kind.isSyntaxCollection {
134+
if type(of: self) != self.syntaxNodeType {
135+
let nestedInitCall = Syntax(self).asProtocol(SyntaxProtocol.self).debugInitCallExpr(includeTrivia: includeTrivia)
136+
if var typeName = getTypeName(type(of: self), qualified: true) {
137+
if typeName.hasPrefix("SwiftSyntax.") {
138+
typeName = String(typeName.dropFirst("SwiftSyntax.".count))
139+
}
140+
return ExprSyntax(
141+
FunctionCallExprSyntax(callee: ExprSyntax("\(raw: typeName)")) {
142+
TupleExprElementSyntax(expression: nestedInitCall)
143+
}
144+
)
145+
}
146+
return nestedInitCall
147+
}
148+
149+
if case .collection(let collectionElementType) = self.syntaxNodeType.structure {
118150
let typeName = String(describing: type(of: self))
119151
return ExprSyntax(
120152
FunctionCallExprSyntax(callee: IdentifierExprSyntax(identifier: .identifier(typeName))) {
121153
TupleExprElementSyntax(
122154
expression: ArrayExprSyntax {
123-
for child in mirror.children {
124-
let value = child.value as! SyntaxProtocol?
125-
ArrayElementSyntax(expression: value?.debugInitCallExpr(includeTrivia: includeTrivia) ?? ExprSyntax(NilLiteralExprSyntax()))
155+
for child in self.children(viewMode: .all) {
156+
ArrayElementSyntax(expression: child.as(collectionElementType)!.debugInitCallExpr(includeTrivia: includeTrivia))
126157
}
127158
}
128159
)
@@ -132,12 +163,12 @@ extension SyntaxProtocol {
132163
let tokenKind = token.tokenKind
133164
let tokenInitializerName: String
134165
let tokenKindArgument: ExprSyntax?
135-
if tokenKind.isLexerClassifiedKeyword || tokenKind == .eof {
136-
tokenInitializerName = String(describing: tokenKind)
137-
tokenKindArgument = nil
138-
} else if case .keyword(let keyword) = tokenKind {
166+
if case .keyword(let keyword) = tokenKind {
139167
tokenInitializerName = "keyword"
140168
tokenKindArgument = ExprSyntax(".\(raw: keyword)")
169+
} else if tokenKind.isLexerClassifiedKeyword || tokenKind == .eof {
170+
tokenInitializerName = String(describing: tokenKind)
171+
tokenKindArgument = nil
141172
} else if tokenKind.decomposeToRaw().rawKind.defaultText != nil {
142173
tokenInitializerName = "\(String(describing: tokenKind))Token"
143174
tokenKindArgument = nil
@@ -174,15 +205,15 @@ extension SyntaxProtocol {
174205
}
175206
}
176207
)
177-
} else {
208+
} else if case .layout(let layout) = self.syntaxNodeType.structure {
178209
let typeName = String(describing: type(of: self))
179210
return ExprSyntax(
180211
FunctionCallExprSyntax(callee: IdentifierExprSyntax(identifier: .identifier(typeName))) {
181-
for child in mirror.children {
182-
let label = child.label!
183-
let value = child.value as! SyntaxProtocol?
212+
for keyPath in layout {
213+
let label = childName(keyPath) ?? ""
214+
let value = self[keyPath: keyPath as! PartialKeyPath<Self>] as! SyntaxProtocol?
184215
let isUnexpected = label.hasPrefix("unexpected")
185-
if !isUnexpected || value != nil {
216+
if value != nil {
186217
TupleExprElementSyntax(
187218
label: isUnexpected ? nil : .identifier(label),
188219
colon: isUnexpected ? nil : .colonToken(),
@@ -192,6 +223,10 @@ extension SyntaxProtocol {
192223
}
193224
}
194225
)
226+
} else {
227+
fatalError()
195228
}
196229
}
197230
}
231+
232+
#endif

0 commit comments

Comments
 (0)