Skip to content

Commit efe865e

Browse files
committed
Validate layout of node_choices
1 parent 33b1ac8 commit efe865e

File tree

2 files changed

+1976
-1771
lines changed

2 files changed

+1976
-1771
lines changed

Sources/SwiftSyntax/Raw/RawSyntaxValidation.swift.gyb

Lines changed: 74 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,71 @@
2424
/// Results in an assertion failure if the layout is invalid.
2525
func validateLayout(layout: RawSyntaxBuffer, as kind: SyntaxKind) {
2626
#if DEBUG
27-
func _verify<Node: RawSyntaxNodeProtocol>(_ raw: RawSyntax?, as _: Node.Type, file: StaticString = #file, line: UInt = #line) {
28-
assert(raw != nil, file: file, line: line)
29-
assert(Node.isKindOf(raw!), file: file, line: line)
27+
enum ValidationError: CustomStringConvertible {
28+
case expectedNonNil(expectedKind: RawSyntaxNodeProtocol.Type, file: StaticString, line: UInt)
29+
case kindMismatch(expectedKind: RawSyntaxNodeProtocol.Type, actualKind: SyntaxKind, file: StaticString, line: UInt)
30+
31+
var description: String {
32+
switch self {
33+
case .expectedNonNil(expectedKind: let expectedKind, file: _, line: _):
34+
return "Expected non-nil node of type \(expectedKind) but received nil"
35+
case .kindMismatch(expectedKind: let expectedKind, actualKind: let actualKind, file: _, line: _):
36+
return "Expected node of type \(expectedKind) but received \(actualKind)"
37+
}
38+
}
39+
40+
var fileAndLine: (StaticString, UInt) {
41+
switch self {
42+
case .expectedNonNil(expectedKind: _, file: let file, line: let line):
43+
return (file, line)
44+
case .kindMismatch(expectedKind: _, actualKind: _, file: let file, line: let line):
45+
return (file, line)
46+
}
47+
}
3048
}
31-
func _verify<Node: RawSyntaxNodeProtocol>(_ raw: RawSyntax?, as _: Node?.Type, file: StaticString = #file, line: UInt = #line) {
32-
raw.map { assert(Node.isKindOf($0), file: file, line: line) }
49+
50+
func verify<Node: RawSyntaxNodeProtocol>(_ raw: RawSyntax?, as _: Node.Type, file: StaticString = #file, line: UInt = #line) -> ValidationError? {
51+
guard let raw = raw else {
52+
return .expectedNonNil(expectedKind: Node.self, file: file, line: line)
53+
}
54+
guard Node.isKindOf(raw) else {
55+
return .kindMismatch(expectedKind: Node.self, actualKind: raw.kind, file: file, line: line)
56+
}
57+
return nil
3358
}
59+
60+
func verify<Node: RawSyntaxNodeProtocol>(_ raw: RawSyntax?, as _: Node?.Type, file: StaticString = #file, line: UInt = #line) -> ValidationError? {
61+
if raw != nil {
62+
return verify(raw, as: Node.self, file: file, line: line)
63+
}
64+
return nil
65+
}
66+
67+
func assertNoError(_ nodeKind: SyntaxKind, _ index: Int, _ error: ValidationError?) {
68+
if let error = error {
69+
let (file, line) = error.fileAndLine
70+
assertionFailure("""
71+
Error validating child at index \(index) of \(nodeKind):
72+
\(error.description)
73+
""", file: file, line: line)
74+
_ = 1
75+
}
76+
}
77+
78+
func assertAnyHasNoError(_ nodeKind: SyntaxKind, _ index: Int, _ errors: [ValidationError?]) {
79+
let nonNilErrors = errors.compactMap({ $0 })
80+
if nonNilErrors.count == errors.count, let firstError = nonNilErrors.first {
81+
let (file, line) = firstError.fileAndLine
82+
assertionFailure("""
83+
Error validating child at index \(index) of \(nodeKind):
84+
Node did not satisfy any node choice requirement.
85+
Validation failures:
86+
\(nonNilErrors.map({ "- \($0.description)" }).joined(separator: "\n") )
87+
""", file: file, line: line)
88+
_ = 1
89+
}
90+
}
91+
3492
switch kind {
3593
case .unknown:
3694
break
@@ -41,11 +99,19 @@ func validateLayout(layout: RawSyntaxBuffer, as kind: SyntaxKind) {
4199
% if node.is_buildable() or node.is_missing():
42100
assert(layout.count == ${len(node.children)})
43101
% for (idx, child) in enumerate(node.children):
44-
_verify(layout[${idx}], as: Raw${child.type_name}${"?" if child.is_optional else ""}.self)
102+
% if child.node_choices:
103+
assertAnyHasNoError(kind, ${idx}, [
104+
% for node_choice in child.node_choices:
105+
verify(layout[${idx}], as: Raw${child.type_name}${"?" if child.is_optional else ""}.self),
106+
% end
107+
])
108+
% else:
109+
assertNoError(kind, ${idx}, verify(layout[${idx}], as: Raw${child.type_name}${"?" if child.is_optional else ""}.self))
110+
% end
45111
% end
46112
% elif node.is_syntax_collection():
47-
for element in layout {
48-
_verify(element, as: Raw${node.collection_element_type}.self)
113+
for (index, element) in layout.enumerated() {
114+
assertNoError(kind, index, verify(element, as: Raw${node.collection_element_type}.self))
49115
}
50116
% end
51117
break

0 commit comments

Comments
 (0)