@@ -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+
106121extension 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