Skip to content

Commit f46d629

Browse files
committed
Move raw syntax validation from gyb to code gen
1 parent c53ad54 commit f46d629

File tree

6 files changed

+290
-260
lines changed

6 files changed

+290
-260
lines changed

CodeGeneration/Sources/generate-swiftsyntax/GenerateSwiftSyntax.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ struct GenerateSwiftSyntax: ParsableCommand {
7373
TemplateSpec(sourceFile: keywordFile, module: swiftSyntaxDir, filename: "Keyword.swift"),
7474
TemplateSpec(sourceFile: miscFile, module: swiftSyntaxDir, filename: "Misc.swift"),
7575
TemplateSpec(sourceFile: rawSyntaxNodesFile, module: swiftSyntaxDir, filename: "raw/RawSyntaxNodes.swift"),
76+
TemplateSpec(sourceFile: rawSyntaxValidationFile, module: swiftSyntaxDir, filename: "raw/RawSyntaxValidation.swift"),
7677
TemplateSpec(sourceFile: syntaxAnyVisitorFile, module: swiftSyntaxDir, filename: "SyntaxAnyVisitor.swift"),
7778
TemplateSpec(sourceFile: syntaxBaseNodesFile, module: swiftSyntaxDir, filename: "SyntaxBaseNodes.swift"),
7879
TemplateSpec(sourceFile: syntaxCollectionsFile, module: swiftSyntaxDir, filename: "SyntaxCollections.swift"),
Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2022 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
import SwiftSyntax
14+
import SwiftSyntaxBuilder
15+
import SyntaxSupport
16+
import Utils
17+
18+
let rawSyntaxValidationFile = try! SourceFileSyntax(leadingTrivia: "\(generateCopyrightHeader(for: "generate-swiftsyntax"))" + .newline) {
19+
try FunctionDeclSyntax(
20+
"""
21+
/// Check that the `layout` is valid for the given 'SyntaxKind'.
22+
///
23+
/// Note that this only validates the immediate children.
24+
/// Results in an assertion failure if the layout is invalid.
25+
func validateLayout(layout: RawSyntaxBuffer, as kind: SyntaxKind)
26+
"""
27+
) {
28+
IfConfigDeclSyntax(
29+
clauses: IfConfigClauseListSyntax {
30+
IfConfigClauseSyntax(
31+
poundKeyword: .poundIfKeyword(),
32+
condition: ExprSyntax("DEBUG"),
33+
elements: .statements(
34+
CodeBlockItemListSyntax {
35+
DeclSyntax(
36+
#"""
37+
enum ValidationError: CustomStringConvertible {
38+
case expectedNonNil(expectedKind: RawSyntaxNodeProtocol.Type, file: StaticString, line: UInt)
39+
case kindMismatch(expectedKind: RawSyntaxNodeProtocol.Type, actualKind: SyntaxKind, file: StaticString, line: UInt)
40+
41+
var description: String {
42+
switch self {
43+
case .expectedNonNil(expectedKind: let expectedKind, file: _, line: _):
44+
return "Expected non-nil node of type \(expectedKind) but received nil"
45+
case .kindMismatch(expectedKind: let expectedKind, actualKind: let actualKind, file: _, line: _):
46+
return "Expected node of type \(expectedKind) but received \(actualKind)"
47+
}
48+
}
49+
50+
var fileAndLine: (StaticString, UInt) {
51+
switch self {
52+
case .expectedNonNil(expectedKind: _, file: let file, line: let line):
53+
return (file, line)
54+
case .kindMismatch(expectedKind: _, actualKind: _, file: let file, line: let line):
55+
return (file, line)
56+
}
57+
}
58+
}
59+
"""#
60+
)
61+
62+
DeclSyntax(
63+
"""
64+
func verify<Node: RawSyntaxNodeProtocol>(_ raw: RawSyntax?, as _: Node.Type, file: StaticString = #file, line: UInt = #line) -> ValidationError? {
65+
guard let raw = raw else {
66+
return .expectedNonNil(expectedKind: Node.self, file: file, line: line)
67+
}
68+
guard Node.isKindOf(raw) else {
69+
return .kindMismatch(expectedKind: Node.self, actualKind: raw.kind, file: file, line: line)
70+
}
71+
return nil
72+
}
73+
"""
74+
)
75+
76+
DeclSyntax(
77+
"""
78+
func verify<Node: RawSyntaxNodeProtocol>(_ raw: RawSyntax?, as _: Node?.Type, file: StaticString = #file, line: UInt = #line) -> ValidationError? {
79+
if raw != nil {
80+
return verify(raw, as: Node.self, file: file, line: line)
81+
}
82+
return nil
83+
}
84+
"""
85+
)
86+
87+
let assertNoErrorAssertionMessage = StringLiteralExprSyntax(
88+
openQuote: .multilineStringQuoteToken(leadingTrivia: .newline, trailingTrivia: .newline),
89+
segments: StringLiteralSegmentsSyntax {
90+
StringSegmentSyntax(content: .stringSegment(#"Error validating child at index \(index) of \(nodeKind):"#))
91+
StringSegmentSyntax(content: .stringSegment(#"\(error.description)"#))
92+
},
93+
closeQuote: .multilineStringQuoteToken(leadingTrivia: .newline, trailingTrivia: .newline)
94+
)
95+
96+
DeclSyntax(
97+
#"""
98+
func assertNoError(_ nodeKind: SyntaxKind, _ index: Int, _ error: ValidationError?) {
99+
if let error = error {
100+
let (file, line) = error.fileAndLine
101+
assertionFailure(\#(assertNoErrorAssertionMessage), file: file, line: line)
102+
_ = 1
103+
}
104+
}
105+
"""#
106+
)
107+
108+
let assertAnyHasNoErrorAssertionMessage = StringLiteralExprSyntax(
109+
openQuote: .multilineStringQuoteToken(leadingTrivia: .newline, trailingTrivia: .newline),
110+
segments: StringLiteralSegmentsSyntax {
111+
StringSegmentSyntax(content: .stringSegment(#"Error validating child at index \(index) of \(nodeKind):"#))
112+
StringSegmentSyntax(content: .stringSegment(#"Node did not satisfy any node choice requirement."#))
113+
StringSegmentSyntax(content: .stringSegment(#"Validation failures:"#))
114+
StringSegmentSyntax(content: .stringSegment(#"\(nonNilErrors.map({ "- \($0.description)" }).joined(separator: "\n"))"#))
115+
},
116+
closeQuote: .multilineStringQuoteToken(leadingTrivia: .newline, trailingTrivia: .newline)
117+
)
118+
119+
DeclSyntax(
120+
#"""
121+
func assertAnyHasNoError(_ nodeKind: SyntaxKind, _ index: Int, _ errors: [ValidationError?]) {
122+
let nonNilErrors = errors.compactMap({ $0 })
123+
if nonNilErrors.count == errors.count, let firstError = nonNilErrors.first {
124+
let (file, line) = firstError.fileAndLine
125+
assertionFailure(\#(assertAnyHasNoErrorAssertionMessage), file: file, line: line)
126+
_ = 1
127+
}
128+
}
129+
"""#
130+
)
131+
132+
try! SwitchExprSyntax("switch kind") {
133+
SwitchCaseSyntax(
134+
"""
135+
case .token:
136+
assertionFailure("validateLayout for .token kind is not supported")
137+
"""
138+
)
139+
140+
for node in NON_BASE_SYNTAX_NODES {
141+
SwitchCaseSyntax("case .\(raw: node.swiftSyntaxKind):") {
142+
if node.isBuildable || node.isMissing {
143+
ExprSyntax("assert(layout.count == \(raw: node.children.count))")
144+
for (index, child) in node.children.enumerated() {
145+
switch child.kind {
146+
case .nodeChoices(let choices):
147+
let verifiedChoices = ArrayExprSyntax {
148+
ArrayElementSyntax(
149+
leadingTrivia: .newline,
150+
expression: ExprSyntax("verify(layout[\(raw: index)], as: Raw\(raw: child.type.buildable).self)")
151+
)
152+
}
153+
154+
ExprSyntax("assertAnyHasNoError(kind, \(raw: index), \(verifiedChoices))")
155+
default:
156+
ExprSyntax("assertNoError(kind, \(raw: index), verify(layout[\(raw: index)], as: Raw\(raw: child.type.buildable).self))")
157+
}
158+
}
159+
} else if node.isSyntaxCollection {
160+
try! ForInStmtSyntax("for (index, element) in layout.enumerated()") {
161+
if let collectionElementChoices = node.collectionElementChoices, !collectionElementChoices.isEmpty {
162+
let verifiedChoices = ArrayExprSyntax {
163+
for choiceName in node.collectionElementChoices! {
164+
let choice = SYNTAX_NODE_MAP[choiceName]!
165+
ArrayElementSyntax(
166+
leadingTrivia: .newline,
167+
expression: ExprSyntax("verify(element, as: Raw\(raw: choice.name).self)")
168+
)
169+
}
170+
}
171+
ExprSyntax("assertAnyHasNoError(kind, index, \(verifiedChoices))")
172+
} else {
173+
ExprSyntax("assertNoError(kind, index, verify(element, as: Raw\(node.collectionElementType.buildable).self))")
174+
}
175+
}
176+
}
177+
178+
BreakStmtSyntax()
179+
}
180+
}
181+
}
182+
}
183+
)
184+
)
185+
}
186+
)
187+
}
188+
}

Package.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,7 @@ let package = Package(
6464
name: "SwiftSyntax",
6565
dependencies: [],
6666
exclude: [
67-
"CMakeLists.txt",
68-
"Raw/RawSyntaxValidation.swift.gyb",
67+
"CMakeLists.txt"
6968
],
7069
swiftSettings: swiftSyntaxSwiftSettings
7170
),

Sources/SwiftSyntax/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ add_swift_host_library(SwiftSyntax
3030
Raw/RawSyntaxTokenView.swift
3131

3232
generated/raw/RawSyntaxNodes.swift
33-
Raw/gyb_generated/RawSyntaxValidation.swift
33+
generated/raw/RawSyntaxValidation.swift
3434

3535
generated/Keyword.swift
3636
generated/Misc.swift

Sources/SwiftSyntax/Raw/RawSyntaxValidation.swift.gyb

Lines changed: 0 additions & 129 deletions
This file was deleted.

0 commit comments

Comments
 (0)