@@ -32,18 +32,39 @@ let rawSyntaxValidationFile = try! SourceFileSyntax(leadingTrivia: copyrightHead
3232 condition: ExprSyntax ( " DEBUG " ) ,
3333 elements: . statements(
3434 try CodeBlockItemListSyntax {
35+ DeclSyntax (
36+ #"""
37+ enum TokenChoice: CustomStringConvertible {
38+ case keyword(StaticString)
39+ case tokenKind(RawTokenKind)
40+
41+ var description: String {
42+ switch self {
43+ case .keyword(let keyword):
44+ return "keyword('\(keyword)')"
45+ case .tokenKind(let kind):
46+ return "\(kind)"
47+ }
48+ }
49+ }
50+ """#
51+ )
52+
3553 DeclSyntax (
3654 #"""
3755 enum ValidationError: CustomStringConvertible {
3856 case expectedNonNil(expectedKind: RawSyntaxNodeProtocol.Type, file: StaticString, line: UInt)
3957 case kindMismatch(expectedKind: RawSyntaxNodeProtocol.Type, actualKind: SyntaxKind, file: StaticString, line: UInt)
58+ case tokenMismatch(expectedTokenChoices: [TokenChoice], actualKind: RawTokenKind, actualText: SyntaxText, file: StaticString, line: UInt)
4059
4160 var description: String {
4261 switch self {
4362 case .expectedNonNil(expectedKind: let expectedKind, file: _, line: _):
4463 return "Expected non-nil node of type \(expectedKind) but received nil"
4564 case .kindMismatch(expectedKind: let expectedKind, actualKind: let actualKind, file: _, line: _):
4665 return "Expected node of type \(expectedKind) but received \(actualKind)"
66+ case .tokenMismatch(expectedTokenChoices: let tokenChoices, actualKind: let actualKind, actualText: let actualText, file: _, line: _):
67+ return "Expected token with one of \(tokenChoices) but received \(actualKind) with text '\(actualText)'"
4768 }
4869 }
4970
@@ -53,6 +74,8 @@ let rawSyntaxValidationFile = try! SourceFileSyntax(leadingTrivia: copyrightHead
5374 return (file, line)
5475 case .kindMismatch(expectedKind: _, actualKind: _, file: let file, line: let line):
5576 return (file, line)
77+ case .tokenMismatch(expectedTokenChoices: _, actualKind: _, actualText: _, file: let file, line: let line):
78+ return (file, line)
5679 }
5780 }
5881 }
@@ -84,6 +107,61 @@ let rawSyntaxValidationFile = try! SourceFileSyntax(leadingTrivia: copyrightHead
84107 """
85108 )
86109
110+ DeclSyntax (
111+ """
112+ func verify(_ raw: RawSyntax?, as _: RawTokenSyntax?.Type, tokenChoices: [TokenChoice], file: StaticString = #file, line: UInt = #line) -> ValidationError? {
113+ // Validation of token choice is currently causing assertion failures where
114+ // the list of expected token choices in the syntax tree doesn't match those
115+ // the parser generates. Disable the verification for now until all issues
116+ // regarding it are fixed.
117+ #if VALIDATE_TOKEN_CHOICES
118+ if raw != nil {
119+ return verify(raw, as: RawTokenSyntax.self, tokenChoices: tokenChoices, file: file, line: line)
120+ }
121+ return nil
122+ #else
123+ return verify(raw, as: RawTokenSyntax?.self)
124+ #endif
125+ }
126+ """
127+ )
128+
129+ DeclSyntax (
130+ """
131+ func verify(_ raw: RawSyntax?, as _: RawTokenSyntax.Type, tokenChoices: [TokenChoice], file: StaticString = #file, line: UInt = #line) -> ValidationError? {
132+ // Validation of token choice is currently causing assertion failures where
133+ // the list of expected token choices in the syntax tree doesn't match those
134+ // the parser generates. Disable the verification for now until all issues
135+ // regarding it are fixed.
136+ #if VALIDATE_TOKEN_CHOICES
137+ guard let raw = raw else {
138+ return .expectedNonNil(expectedKind: RawTokenSyntax.self, file: file, line: line)
139+ }
140+ if let error = verify(raw, as: RawTokenSyntax?.self) {
141+ return error
142+ }
143+ let tokenView = raw.tokenView!
144+ for tokenChoice in tokenChoices {
145+ switch tokenChoice {
146+ case .tokenKind(let tokenKind):
147+ if raw.tokenView?.rawKind == tokenKind {
148+ return nil
149+ }
150+ case .keyword(let keyword):
151+ if tokenView.rawKind == .keyword && tokenView.rawText == SyntaxText(keyword) {
152+ return nil
153+ }
154+ }
155+ }
156+ return ValidationError.tokenMismatch(expectedTokenChoices: tokenChoices, actualKind: tokenView.rawKind, actualText: tokenView.rawText, file: file, line: line)
157+ #else
158+ return verify(raw, as: RawTokenSyntax.self)
159+ #endif
160+ }
161+
162+ """
163+ )
164+
87165 DeclSyntax (
88166 #"""
89167 func assertNoError(_ nodeKind: SyntaxKind, _ index: Int, _ error: ValidationError?) {
@@ -139,6 +217,19 @@ let rawSyntaxValidationFile = try! SourceFileSyntax(leadingTrivia: copyrightHead
139217 }
140218
141219 ExprSyntax ( " assertAnyHasNoError(kind, \( raw: index) , \( verifiedChoices) ) " )
220+ case . token( choices: let choices, requiresLeadingSpace: _, requiresTrailingSpace: _) :
221+ let choices = ArrayExprSyntax {
222+ for choice in choices {
223+ switch choice {
224+ case . keyword( text: let text) :
225+ ArrayElementSyntax ( expression: ExprSyntax ( #".keyword(" \#( raw: text) ")"# ) )
226+ case . token( tokenKind: let tokenKind) :
227+ ArrayElementSyntax ( expression: ExprSyntax ( " .tokenKind(. \( raw: SYNTAX_TOKEN_MAP [ tokenKind] !. swiftKind) ) " ) )
228+ }
229+ }
230+ }
231+ let verifyCall = ExprSyntax ( " verify(layout[ \( raw: index) ], as: Raw \( raw: child. type. buildable) .self, tokenChoices: \( choices) ) " )
232+ ExprSyntax ( " assertNoError(kind, \( raw: index) , \( verifyCall) ) " )
142233 default :
143234 ExprSyntax ( " assertNoError(kind, \( raw: index) , verify(layout[ \( raw: index) ], as: Raw \( raw: child. type. buildable) .self)) " )
144235 }
0 commit comments