Skip to content

Commit e33f4d0

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 ebe3885 commit e33f4d0

File tree

4 files changed

+92
-21
lines changed

4 files changed

+92
-21
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: 31 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -110,21 +110,36 @@ extension SyntaxProtocol {
110110
/// (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).
111111
/// The intended use case for this is to print a syntax tree and create a substructure assertion from the generated expression.
112112
/// When `includeTrivia` is set to `false`, the token's leading and trailing trivia will not be included in the generated expression.
113+
///
114+
/// - Warning: This is only designed for use in the debugger. Do not call it outside of the debugger.
115+
@available(*, deprecated, message: "For use in debugger only")
113116
public func debugInitCall(includeTrivia: Bool = true) -> String {
114117
return self.debugInitCallExpr(includeTrivia: includeTrivia).formatted(using: InitializerExprFormat()).description
115118
}
116119

117120
private func debugInitCallExpr(includeTrivia: Bool) -> ExprSyntax {
118-
let mirror = Mirror(reflecting: self)
119-
if self.kind.isSyntaxCollection {
121+
if type(of: self) != self.syntaxNodeType {
122+
let nestedInitCall = Syntax(self).asProtocol(SyntaxProtocol.self).debugInitCallExpr(includeTrivia: includeTrivia)
123+
var typeName = "\(type(of: self))"
124+
// If the type is `SyntaxChildChoices`, it is a nested type that needs to be qualified.
125+
if self is SyntaxChildChoices, let parent = parent {
126+
typeName = "\(parent.syntaxNodeType).\(typeName)"
127+
}
128+
return ExprSyntax(
129+
FunctionCallExprSyntax(callee: ExprSyntax("\(raw: typeName)")) {
130+
TupleExprElementSyntax(expression: nestedInitCall)
131+
}
132+
)
133+
}
134+
135+
if case .collection(let collectionElementType) = self.syntaxNodeType.structure {
120136
let typeName = String(describing: type(of: self))
121137
return ExprSyntax(
122138
FunctionCallExprSyntax(callee: IdentifierExprSyntax(identifier: .identifier(typeName))) {
123139
TupleExprElementSyntax(
124140
expression: ArrayExprSyntax {
125-
for child in mirror.children {
126-
let value = child.value as! SyntaxProtocol?
127-
ArrayElementSyntax(expression: value?.debugInitCallExpr(includeTrivia: includeTrivia) ?? ExprSyntax(NilLiteralExprSyntax()))
141+
for child in self.children(viewMode: .all) {
142+
ArrayElementSyntax(expression: child.as(collectionElementType)!.debugInitCallExpr(includeTrivia: includeTrivia))
128143
}
129144
}
130145
)
@@ -134,12 +149,12 @@ extension SyntaxProtocol {
134149
let tokenKind = token.tokenKind
135150
let tokenInitializerName: String
136151
let tokenKindArgument: ExprSyntax?
137-
if tokenKind.isLexerClassifiedKeyword || tokenKind == .eof {
138-
tokenInitializerName = String(describing: tokenKind)
139-
tokenKindArgument = nil
140-
} else if case .keyword(let keyword) = tokenKind {
152+
if case .keyword(let keyword) = tokenKind {
141153
tokenInitializerName = "keyword"
142154
tokenKindArgument = ExprSyntax(".\(raw: keyword)")
155+
} else if tokenKind.isLexerClassifiedKeyword || tokenKind == .eof {
156+
tokenInitializerName = String(describing: tokenKind)
157+
tokenKindArgument = nil
143158
} else if tokenKind.decomposeToRaw().rawKind.defaultText != nil {
144159
tokenInitializerName = "\(String(describing: tokenKind))Token"
145160
tokenKindArgument = nil
@@ -176,15 +191,15 @@ extension SyntaxProtocol {
176191
}
177192
}
178193
)
179-
} else {
194+
} else if case .layout(let layout) = self.syntaxNodeType.structure {
180195
let typeName = String(describing: type(of: self))
181196
return ExprSyntax(
182197
FunctionCallExprSyntax(callee: IdentifierExprSyntax(identifier: .identifier(typeName))) {
183-
for child in mirror.children {
184-
let label = child.label!
185-
let value = child.value as! SyntaxProtocol?
198+
for keyPath in layout {
199+
let label = childName(keyPath) ?? ""
200+
let value = self[keyPath: keyPath as! PartialKeyPath<Self>] as! SyntaxProtocol?
186201
let isUnexpected = label.hasPrefix("unexpected")
187-
if !isUnexpected || value != nil {
202+
if value != nil {
188203
TupleExprElementSyntax(
189204
label: isUnexpected ? nil : .identifier(label),
190205
colon: isUnexpected ? nil : .colonToken(),
@@ -194,6 +209,8 @@ extension SyntaxProtocol {
194209
}
195210
}
196211
)
212+
} else {
213+
fatalError()
197214
}
198215
}
199216
}

Tests/SwiftSyntaxTest/DebugDescriptionTests.swift

Lines changed: 57 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import XCTest
1414
import SwiftSyntax
1515
import _SwiftSyntaxTestSupport
16+
import SwiftParser
1617

1718
private extension String {
1819
// This implementation is really slow; to use it outside a test it should be optimized.
@@ -158,7 +159,8 @@ public class DebugDescriptionTests: XCTestCase {
158159

159160
testCases.forEach { keyAndValue in
160161
let (key:line, value:testCase) = keyAndValue
161-
let actualDumped = dumped(testCase.syntax)
162+
var actualDumped = ""
163+
dump(testCase.syntax, to: &actualDumped)
162164
assertStringsEqualWithDiff(
163165
actualDumped.trimmingTrailingWhitespace(),
164166
testCase.expectedDumped.trimmingTrailingWhitespace(),
@@ -167,9 +169,59 @@ public class DebugDescriptionTests: XCTestCase {
167169
}
168170
}
169171

170-
public func dumped(_ syntax: Any) -> String {
171-
var result = ""
172-
dump(syntax, to: &result)
173-
return result
172+
#if DEBUG
173+
// debugInitCall is only available in debug builds, so we can only test it in those.
174+
@available(*, deprecated, message: "Purposefully tests debugInitCall, which is marked deprecated for debugger use only")
175+
func testDebugInitCall() {
176+
let sourceFile: SourceFileSyntax = """
177+
test(1, 2)
178+
"""
179+
180+
assertStringsEqualWithDiff(
181+
sourceFile.debugInitCall(includeTrivia: true).trimmingTrailingWhitespace(),
182+
"""
183+
SourceFileSyntax(
184+
statements: CodeBlockItemListSyntax([
185+
CodeBlockItemSyntax(item: CodeBlockItemSyntax.Item(FunctionCallExprSyntax(
186+
calledExpression: ExprSyntax(IdentifierExprSyntax(identifier: .identifier("test"))),
187+
leftParen: .leftParenToken(),
188+
argumentList: TupleExprElementListSyntax([
189+
TupleExprElementSyntax(
190+
expression: ExprSyntax(IntegerLiteralExprSyntax(digits: .integerLiteral("1"))),
191+
trailingComma: .commaToken(trailingTrivia: .space)
192+
),
193+
TupleExprElementSyntax(expression: ExprSyntax(IntegerLiteralExprSyntax(digits: .integerLiteral("2"))))
194+
]),
195+
rightParen: .rightParenToken()
196+
)))
197+
]),
198+
eofToken: .eof()
199+
)
200+
"""
201+
)
202+
203+
assertStringsEqualWithDiff(
204+
sourceFile.debugInitCall(includeTrivia: false).trimmingTrailingWhitespace(),
205+
"""
206+
SourceFileSyntax(
207+
statements: CodeBlockItemListSyntax([
208+
CodeBlockItemSyntax(item: CodeBlockItemSyntax.Item(FunctionCallExprSyntax(
209+
calledExpression: ExprSyntax(IdentifierExprSyntax(identifier: .identifier("test"))),
210+
leftParen: .leftParenToken(),
211+
argumentList: TupleExprElementListSyntax([
212+
TupleExprElementSyntax(
213+
expression: ExprSyntax(IntegerLiteralExprSyntax(digits: .integerLiteral("1"))),
214+
trailingComma: .commaToken()
215+
),
216+
TupleExprElementSyntax(expression: ExprSyntax(IntegerLiteralExprSyntax(digits: .integerLiteral("2"))))
217+
]),
218+
rightParen: .rightParenToken()
219+
)))
220+
]),
221+
eofToken: .eof()
222+
)
223+
"""
224+
)
174225
}
226+
#endif
175227
}

0 commit comments

Comments
 (0)