Skip to content

Commit 51f14c4

Browse files
committed
Pretty results of dump() for Syntax
The result of dump() with Syntax is not useful. For example, the dumped result of Tests/SwiftSyntaxTest/Inputs/closure.swift is the following: ``` ▿ // A closure without a signature. The test will ensure it stays the same after // applying a rewriting pass. let x: () -> Void = {} ▿ data: SwiftSyntax.SyntaxData - parent: nil ▿ absoluteRaw: SwiftSyntax.AbsoluteRawSyntax - raw: // A closure without a signature. The test will ensure it stays the same after // applying a rewriting pass. let x: () -> Void = {} #0 ▿ super: Swift.ManagedBuffer<SwiftSyntax.RawSyntaxBase, Swift.UInt64> ▿ header: SwiftSyntax.RawSyntaxBase ... ``` This patch improve this result to the following: ``` ▿ SourceFileSyntax ▿ statements: CodeBlockItemListSyntax ▿ CodeBlockItemSyntax ▿ item: VariableDeclSyntax - attributes: nil - modifiers: nil ▿ letOrVarKeyword: TokenSyntax - text: "let" ▿ leadingTrivia: SwiftSyntax.Trivia ▿ pieces: 4 elements ▿ TriviaPiece - lineComment: "// A closure without a signature. The test will ensure it stays the same after" ... ```
1 parent eb622fa commit 51f14c4

File tree

6 files changed

+121
-1
lines changed

6 files changed

+121
-1
lines changed

Sources/SwiftSyntax/Syntax.swift

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
/// Each node has accessors for its known children, and allows efficient
1515
/// iteration over the children through its `children` property.
1616
public protocol Syntax:
17-
CustomStringConvertible, TextOutputStreamable {}
17+
CustomStringConvertible, CustomDebugStringConvertible, TextOutputStreamable {}
1818

1919
internal protocol _SyntaxBase: Syntax {
2020
/// The data backing this node.
@@ -262,6 +262,11 @@ extension _SyntaxBase {
262262
return s
263263
}
264264

265+
/// Returns a description used by dump.
266+
public var debugDescription: String {
267+
return "\(type(of: self))"
268+
}
269+
265270
/// Prints the raw value of this node to the provided stream.
266271
/// - Parameter stream: The stream to which to print the raw tree.
267272
public func write<Target>(to target: inout Target)
@@ -623,6 +628,17 @@ public struct TokenSyntax: _SyntaxBase, Hashable {
623628
}
624629
}
625630

631+
extension TokenSyntax: CustomReflectable {
632+
public var customMirror: Mirror {
633+
return Mirror(self, children: [
634+
"text": text,
635+
"leadingTrivia": leadingTrivia,
636+
"trailingTrivia": trailingTrivia,
637+
"tokenKind": tokenKind,
638+
])
639+
}
640+
}
641+
626642
/// Sequence of tokens that are part of the provided Syntax node.
627643
public struct TokenSequence: Sequence {
628644
public struct Iterator: IteratorProtocol {
@@ -664,6 +680,13 @@ public struct TokenSequence: Sequence {
664680
}
665681
}
666682

683+
extension TokenSequence: CustomReflectable {
684+
public var customMirror: Mirror {
685+
let keyAndValues = enumerated().map { (label: String($0.0) as String?, value: $0.1 as Any) }
686+
return Mirror(self, children: keyAndValues)
687+
}
688+
}
689+
667690
/// Reverse sequence of tokens that are part of the provided Syntax node.
668691
public struct ReversedTokenSequence: Sequence {
669692
public struct Iterator: IteratorProtocol {
@@ -704,3 +727,11 @@ public struct ReversedTokenSequence: Sequence {
704727
return TokenSequence(node)
705728
}
706729
}
730+
731+
732+
extension ReversedTokenSequence: CustomReflectable {
733+
public var customMirror: Mirror {
734+
let keyAndValues = enumerated().map { (label: String($0.0) as String?, value: $0.1 as Any) }
735+
return Mirror(self, children: keyAndValues)
736+
}
737+
}

Sources/SwiftSyntax/SyntaxCollections.swift.gyb

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,16 @@ extension ${node.name}: Sequence {
223223
}
224224
}
225225
}
226+
% end
227+
% end
226228

229+
% for node in SYNTAX_NODES:
230+
% if node.is_syntax_collection():
231+
extension ${node.name}: CustomReflectable {
232+
public var customMirror: Mirror {
233+
let keyAndValues = enumerated().map { (label: String($0.0) as String?, value: $0.1 as Any) }
234+
return Mirror(self, children: keyAndValues)
235+
}
236+
}
227237
% end
228238
% end

Sources/SwiftSyntax/SyntaxNodes.swift.gyb

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,12 @@ public struct UnknownSyntax: _SyntaxBase, Hashable {
5858
}
5959
}
6060

61+
extension UnknownSyntax: CustomReflectable {
62+
public var customMirror: Mirror {
63+
return Mirror(self, children: [:])
64+
}
65+
}
66+
6167
% for node in SYNTAX_NODES:
6268
% base_type = node.base_type
6369
% if node.is_base():
@@ -169,7 +175,20 @@ public struct ${node.name}: ${base_type}, _SyntaxBase, Hashable {
169175
return data.nodeId.hash(into: &hasher)
170176
}
171177
}
178+
% end
179+
% end
172180

181+
% for node in SYNTAX_NODES:
182+
% if not node.is_base() and not node.is_syntax_collection():
183+
extension ${node.name}: CustomReflectable {
184+
public var customMirror: Mirror {
185+
return Mirror(self, children: [
186+
% for child in node.children:
187+
"${child.swift_name}": ${child.swift_name} as Any,
188+
% end
189+
])
190+
}
191+
}
173192
% end
174193
% end
175194

Sources/SwiftSyntax/Trivia.swift.gyb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,13 @@ extension TriviaPiece: TextOutputStreamable {
6262
}
6363
}
6464

65+
extension TriviaPiece: CustomDebugStringConvertible {
66+
/// Returns a description used by dump.
67+
public var debugDescription: String {
68+
return "TriviaPiece"
69+
}
70+
}
71+
6572
/// A collection of leading or trailing trivia. This is the main data structure
6673
/// for thinking about trivia.
6774
public struct Trivia {

Tests/LinuxMain.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ XCTMain({ () -> [XCTestCaseEntry] in
1616
testCase(TokenSyntaxTestCase.allTests),
1717
testCase(SyntaxTreeModifierTests.allTests),
1818
testCase(TriviaTests.allTests),
19+
testCase(CustomRelectableTests.allTests),
1920
]
2021
return testCases
2122
}())
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import XCTest
2+
import SwiftSyntax
3+
4+
5+
public class CustomReflectableTests: XCTestCase {
6+
public static let allTests = [
7+
("testConformanceToCustomReflectable", testConformanceToCustomReflectable),
8+
]
9+
10+
11+
public func testConformanceToCustomReflectable() {
12+
XCTAssertNoThrow(try {
13+
let parsed = try SyntaxParser.parse(getInput("near-empty.swift"))
14+
XCTAssertEqual(collectSyntaxNotConformedCustomReflectable(from: parsed), [])
15+
}())
16+
XCTAssertNoThrow(try {
17+
let parsed = try SyntaxParser.parse(getInput("closure.swift"))
18+
XCTAssertEqual(collectSyntaxNotConformedCustomReflectable(from: parsed), [])
19+
}())
20+
XCTAssertNoThrow(try {
21+
let parsed = try SyntaxParser.parse(getInput("nested-blocks.swift"))
22+
XCTAssertEqual(collectSyntaxNotConformedCustomReflectable(from: parsed), [])
23+
}())
24+
XCTAssertNoThrow(try {
25+
let parsed = try SyntaxParser.parse(getInput("visitor.swift"))
26+
XCTAssertEqual(collectSyntaxNotConformedCustomReflectable(from: parsed), [])
27+
}())
28+
}
29+
30+
31+
public func collectSyntaxNotConformedCustomReflectable<S: Any>(from object: S) -> [String] {
32+
var paths = [String]()
33+
collectSyntaxNotConformedCustomReflectable(from: object, ancestors: ["root"], foundPaths: &paths)
34+
return paths
35+
}
36+
37+
38+
public func collectSyntaxNotConformedCustomReflectable<S: Any>(from object: S, ancestors: [String], foundPaths: inout [String]) {
39+
Mirror(reflecting: object).children.forEach { child in
40+
let (label: label, value: value) = child
41+
42+
var currentPathComponents = ancestors
43+
currentPathComponents.append(label ?? "(nil)")
44+
45+
if let syntax = value as? Syntax, !(syntax is CustomReflectable) {
46+
foundPaths.append("\(currentPathComponents.joined(separator: ".")): \(type(of: value as Any))")
47+
}
48+
49+
collectSyntaxNotConformedCustomReflectable(from: value, ancestors: currentPathComponents, foundPaths: &foundPaths)
50+
}
51+
}
52+
}

0 commit comments

Comments
 (0)