1use rowan::WalkEvent;
25use tracing::trace;
26use wdl_ast::AstNode;
27use wdl_ast::AstToken;
28use wdl_ast::Comment;
29use wdl_ast::SupportedVersion;
30use wdl_ast::SyntaxKind;
31use wdl_ast::SyntaxNode;
32use wdl_ast::VersionStatement;
33use wdl_ast::Whitespace;
34use wdl_ast::v1::BoundDecl;
35use wdl_ast::v1::CallStatement;
36use wdl_ast::v1::CommandSection;
37use wdl_ast::v1::CommandText;
38use wdl_ast::v1::ConditionalStatement;
39use wdl_ast::v1::Expr;
40use wdl_ast::v1::ImportStatement;
41use wdl_ast::v1::InputSection;
42use wdl_ast::v1::MetadataArray;
43use wdl_ast::v1::MetadataObject;
44use wdl_ast::v1::MetadataObjectItem;
45use wdl_ast::v1::MetadataSection;
46use wdl_ast::v1::OutputSection;
47use wdl_ast::v1::ParameterMetadataSection;
48use wdl_ast::v1::Placeholder;
49use wdl_ast::v1::RequirementsSection;
50use wdl_ast::v1::RuntimeItem;
51use wdl_ast::v1::RuntimeSection;
52use wdl_ast::v1::ScatterStatement;
53use wdl_ast::v1::StringText;
54use wdl_ast::v1::StructDefinition;
55use wdl_ast::v1::TaskDefinition;
56use wdl_ast::v1::TaskHintsSection;
57use wdl_ast::v1::UnboundDecl;
58use wdl_ast::v1::WorkflowDefinition;
59use wdl_ast::v1::WorkflowHintsSection;
60
61use crate::Diagnostics;
62use crate::document::Document as AnalysisDocument;
63
64#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
69pub enum VisitReason {
70 Enter,
72 Exit,
74}
75
76#[allow(unused_variables)]
82pub trait Visitor {
83 fn reset(&mut self);
88
89 fn document(
91 &mut self,
92 diagnostics: &mut Diagnostics,
93 reason: VisitReason,
94 doc: &AnalysisDocument,
95 version: SupportedVersion,
96 ) {
97 }
98
99 fn whitespace(&mut self, diagnostics: &mut Diagnostics, whitespace: &Whitespace) {}
101
102 fn comment(&mut self, diagnostics: &mut Diagnostics, comment: &Comment) {}
104
105 fn version_statement(
107 &mut self,
108 diagnostics: &mut Diagnostics,
109 reason: VisitReason,
110 stmt: &VersionStatement,
111 ) {
112 }
113
114 fn import_statement(
116 &mut self,
117 diagnostics: &mut Diagnostics,
118 reason: VisitReason,
119 stmt: &ImportStatement,
120 ) {
121 }
122
123 fn struct_definition(
125 &mut self,
126 diagnostics: &mut Diagnostics,
127 reason: VisitReason,
128 def: &StructDefinition,
129 ) {
130 }
131
132 fn task_definition(
134 &mut self,
135 diagnostics: &mut Diagnostics,
136 reason: VisitReason,
137 task: &TaskDefinition,
138 ) {
139 }
140
141 fn workflow_definition(
143 &mut self,
144 diagnostics: &mut Diagnostics,
145 reason: VisitReason,
146 workflow: &WorkflowDefinition,
147 ) {
148 }
149
150 fn input_section(
152 &mut self,
153 diagnostics: &mut Diagnostics,
154 reason: VisitReason,
155 section: &InputSection,
156 ) {
157 }
158
159 fn output_section(
161 &mut self,
162 diagnostics: &mut Diagnostics,
163 reason: VisitReason,
164 section: &OutputSection,
165 ) {
166 }
167
168 fn command_section(
170 &mut self,
171 diagnostics: &mut Diagnostics,
172 reason: VisitReason,
173 section: &CommandSection,
174 ) {
175 }
176
177 fn command_text(&mut self, diagnostics: &mut Diagnostics, text: &CommandText) {}
179
180 fn requirements_section(
182 &mut self,
183 diagnostics: &mut Diagnostics,
184 reason: VisitReason,
185 section: &RequirementsSection,
186 ) {
187 }
188
189 fn task_hints_section(
191 &mut self,
192 diagnostics: &mut Diagnostics,
193 reason: VisitReason,
194 section: &TaskHintsSection,
195 ) {
196 }
197
198 fn workflow_hints_section(
200 &mut self,
201 diagnostics: &mut Diagnostics,
202 reason: VisitReason,
203 section: &WorkflowHintsSection,
204 ) {
205 }
206
207 fn runtime_section(
209 &mut self,
210 diagnostics: &mut Diagnostics,
211 reason: VisitReason,
212 section: &RuntimeSection,
213 ) {
214 }
215
216 fn runtime_item(
218 &mut self,
219 diagnostics: &mut Diagnostics,
220 reason: VisitReason,
221 item: &RuntimeItem,
222 ) {
223 }
224
225 fn metadata_section(
227 &mut self,
228 diagnostics: &mut Diagnostics,
229 reason: VisitReason,
230 section: &MetadataSection,
231 ) {
232 }
233
234 fn parameter_metadata_section(
236 &mut self,
237 diagnostics: &mut Diagnostics,
238 reason: VisitReason,
239 section: &ParameterMetadataSection,
240 ) {
241 }
242
243 fn metadata_object(
245 &mut self,
246 diagnostics: &mut Diagnostics,
247 reason: VisitReason,
248 object: &MetadataObject,
249 ) {
250 }
251
252 fn metadata_object_item(
254 &mut self,
255 diagnostics: &mut Diagnostics,
256 reason: VisitReason,
257 item: &MetadataObjectItem,
258 ) {
259 }
260
261 fn metadata_array(
264 &mut self,
265 diagnostics: &mut Diagnostics,
266 reason: VisitReason,
267 item: &MetadataArray,
268 ) {
269 }
270
271 fn unbound_decl(
273 &mut self,
274 diagnostics: &mut Diagnostics,
275 reason: VisitReason,
276 decl: &UnboundDecl,
277 ) {
278 }
279
280 fn bound_decl(&mut self, diagnostics: &mut Diagnostics, reason: VisitReason, decl: &BoundDecl) {
282 }
283
284 fn expr(&mut self, diagnostics: &mut Diagnostics, reason: VisitReason, expr: &Expr) {}
286
287 fn string_text(&mut self, diagnostics: &mut Diagnostics, text: &StringText) {}
289
290 fn placeholder(
292 &mut self,
293 diagnostics: &mut Diagnostics,
294 reason: VisitReason,
295 placeholder: &Placeholder,
296 ) {
297 }
298
299 fn conditional_statement(
301 &mut self,
302 diagnostics: &mut Diagnostics,
303 reason: VisitReason,
304 stmt: &ConditionalStatement,
305 ) {
306 }
307
308 fn scatter_statement(
310 &mut self,
311 diagnostics: &mut Diagnostics,
312 reason: VisitReason,
313 stmt: &ScatterStatement,
314 ) {
315 }
316
317 fn call_statement(
319 &mut self,
320 diagnostics: &mut Diagnostics,
321 reason: VisitReason,
322 stmt: &CallStatement,
323 ) {
324 }
325}
326
327pub(crate) fn visit<V: Visitor>(
330 document: &AnalysisDocument,
331 diagnostics: &mut Diagnostics,
332 visitor: &mut V,
333) {
334 trace!(
335 uri = %document.uri(),
336 "beginning document traversal",
337 );
338 for event in document.root().inner().preorder_with_tokens() {
339 let (reason, element) = match event {
340 WalkEvent::Enter(node) => (VisitReason::Enter, node),
341 WalkEvent::Leave(node) => (VisitReason::Exit, node),
342 };
343 trace!(uri = %document.uri(), ?reason, element = ?element.kind());
344 match element.kind() {
345 SyntaxKind::RootNode => visitor.document(
346 diagnostics,
347 reason,
348 document,
349 document
350 .version()
351 .expect("visited document must have a version"),
352 ),
353 SyntaxKind::VersionStatementNode => visitor.version_statement(
354 diagnostics,
355 reason,
356 &VersionStatement::cast(element.into_node().unwrap()).expect("should cast"),
357 ),
358 SyntaxKind::ImportStatementNode => visitor.import_statement(
359 diagnostics,
360 reason,
361 &ImportStatement::cast(element.into_node().unwrap()).expect("should cast"),
362 ),
363 SyntaxKind::ImportAliasNode => {
364 }
366 SyntaxKind::StructDefinitionNode => visitor.struct_definition(
367 diagnostics,
368 reason,
369 &StructDefinition::cast(element.into_node().unwrap()).expect("should cast"),
370 ),
371 SyntaxKind::TaskDefinitionNode => visitor.task_definition(
372 diagnostics,
373 reason,
374 &TaskDefinition::cast(element.into_node().unwrap()).expect("should cast"),
375 ),
376 SyntaxKind::WorkflowDefinitionNode => visitor.workflow_definition(
377 diagnostics,
378 reason,
379 &WorkflowDefinition::cast(element.into_node().unwrap()).expect("should cast"),
380 ),
381 SyntaxKind::UnboundDeclNode => visitor.unbound_decl(
382 diagnostics,
383 reason,
384 &UnboundDecl::cast(element.into_node().unwrap()).expect("should cast"),
385 ),
386 SyntaxKind::BoundDeclNode => visitor.bound_decl(
387 diagnostics,
388 reason,
389 &BoundDecl::cast(element.into_node().unwrap()).expect("should cast"),
390 ),
391 SyntaxKind::PrimitiveTypeNode
392 | SyntaxKind::MapTypeNode
393 | SyntaxKind::ArrayTypeNode
394 | SyntaxKind::PairTypeNode
395 | SyntaxKind::ObjectTypeNode
396 | SyntaxKind::TypeRefNode => {
397 }
399 SyntaxKind::InputSectionNode => visitor.input_section(
400 diagnostics,
401 reason,
402 &InputSection::cast(element.into_node().unwrap()).expect("should cast"),
403 ),
404 SyntaxKind::OutputSectionNode => visitor.output_section(
405 diagnostics,
406 reason,
407 &OutputSection::cast(element.into_node().unwrap()).expect("should cast"),
408 ),
409 SyntaxKind::CommandSectionNode => visitor.command_section(
410 diagnostics,
411 reason,
412 &CommandSection::cast(element.into_node().unwrap()).expect("should cast"),
413 ),
414 SyntaxKind::RequirementsSectionNode => visitor.requirements_section(
415 diagnostics,
416 reason,
417 &RequirementsSection::cast(element.into_node().unwrap()).expect("should cast"),
418 ),
419 SyntaxKind::TaskHintsSectionNode => visitor.task_hints_section(
420 diagnostics,
421 reason,
422 &TaskHintsSection::cast(element.into_node().unwrap()).expect("should cast"),
423 ),
424 SyntaxKind::WorkflowHintsSectionNode => visitor.workflow_hints_section(
425 diagnostics,
426 reason,
427 &WorkflowHintsSection::cast(element.into_node().unwrap()).expect("should cast"),
428 ),
429 SyntaxKind::TaskHintsItemNode | SyntaxKind::WorkflowHintsItemNode => {
430 }
432 SyntaxKind::RequirementsItemNode => {
433 }
435 SyntaxKind::RuntimeSectionNode => visitor.runtime_section(
436 diagnostics,
437 reason,
438 &RuntimeSection::cast(element.into_node().unwrap()).expect("should cast"),
439 ),
440 SyntaxKind::RuntimeItemNode => visitor.runtime_item(
441 diagnostics,
442 reason,
443 &RuntimeItem::cast(element.into_node().unwrap()).expect("should cast"),
444 ),
445 SyntaxKind::MetadataSectionNode => visitor.metadata_section(
446 diagnostics,
447 reason,
448 &MetadataSection::cast(element.into_node().unwrap()).expect("should cast"),
449 ),
450 SyntaxKind::ParameterMetadataSectionNode => visitor.parameter_metadata_section(
451 diagnostics,
452 reason,
453 &ParameterMetadataSection::cast(element.into_node().unwrap()).expect("should cast"),
454 ),
455 SyntaxKind::MetadataObjectNode => visitor.metadata_object(
456 diagnostics,
457 reason,
458 &MetadataObject::cast(element.into_node().unwrap()).expect("should cast"),
459 ),
460 SyntaxKind::MetadataObjectItemNode => visitor.metadata_object_item(
461 diagnostics,
462 reason,
463 &MetadataObjectItem::cast(element.into_node().unwrap()).expect("should cast"),
464 ),
465 SyntaxKind::MetadataArrayNode => visitor.metadata_array(
466 diagnostics,
467 reason,
468 &MetadataArray::cast(element.into_node().unwrap()).expect("should cast"),
469 ),
470 SyntaxKind::LiteralNullNode => {
471 }
473 k if Expr::<SyntaxNode>::can_cast(k) => {
474 visitor.expr(
475 diagnostics,
476 reason,
477 &Expr::cast(element.into_node().expect(
478 "any element that is able to be turned into an expr should be a node",
479 ))
480 .expect("expr should be built"),
481 )
482 }
483 SyntaxKind::LiteralMapItemNode
484 | SyntaxKind::LiteralObjectItemNode
485 | SyntaxKind::LiteralStructItemNode
486 | SyntaxKind::LiteralHintsItemNode
487 | SyntaxKind::LiteralInputItemNode
488 | SyntaxKind::LiteralOutputItemNode => {
489 }
491 k @ (SyntaxKind::LiteralIntegerNode
492 | SyntaxKind::LiteralFloatNode
493 | SyntaxKind::LiteralBooleanNode
494 | SyntaxKind::LiteralNoneNode
495 | SyntaxKind::LiteralStringNode
496 | SyntaxKind::LiteralPairNode
497 | SyntaxKind::LiteralArrayNode
498 | SyntaxKind::LiteralMapNode
499 | SyntaxKind::LiteralObjectNode
500 | SyntaxKind::LiteralStructNode
501 | SyntaxKind::LiteralHintsNode
502 | SyntaxKind::LiteralInputNode
503 | SyntaxKind::LiteralOutputNode
504 | SyntaxKind::ParenthesizedExprNode
505 | SyntaxKind::NameRefExprNode
506 | SyntaxKind::IfExprNode
507 | SyntaxKind::LogicalNotExprNode
508 | SyntaxKind::NegationExprNode
509 | SyntaxKind::LogicalOrExprNode
510 | SyntaxKind::LogicalAndExprNode
511 | SyntaxKind::EqualityExprNode
512 | SyntaxKind::InequalityExprNode
513 | SyntaxKind::LessExprNode
514 | SyntaxKind::LessEqualExprNode
515 | SyntaxKind::GreaterExprNode
516 | SyntaxKind::GreaterEqualExprNode
517 | SyntaxKind::AdditionExprNode
518 | SyntaxKind::SubtractionExprNode
519 | SyntaxKind::MultiplicationExprNode
520 | SyntaxKind::DivisionExprNode
521 | SyntaxKind::ModuloExprNode
522 | SyntaxKind::CallExprNode
523 | SyntaxKind::IndexExprNode
524 | SyntaxKind::AccessExprNode) => {
525 unreachable!("`{k:?}` should be handled by `Expr::can_cast`")
526 }
527 SyntaxKind::PlaceholderNode => visitor.placeholder(
528 diagnostics,
529 reason,
530 &Placeholder::cast(element.into_node().unwrap()).expect("should cast"),
531 ),
532 SyntaxKind::PlaceholderSepOptionNode
533 | SyntaxKind::PlaceholderDefaultOptionNode
534 | SyntaxKind::PlaceholderTrueFalseOptionNode => {
535 }
537 SyntaxKind::ConditionalStatementNode => visitor.conditional_statement(
538 diagnostics,
539 reason,
540 &ConditionalStatement::cast(element.into_node().unwrap()).expect("should cast"),
541 ),
542 SyntaxKind::ScatterStatementNode => visitor.scatter_statement(
543 diagnostics,
544 reason,
545 &ScatterStatement::cast(element.into_node().unwrap()).expect("should cast"),
546 ),
547 SyntaxKind::CallStatementNode => visitor.call_statement(
548 diagnostics,
549 reason,
550 &CallStatement::cast(element.into_node().unwrap()).expect("should cast"),
551 ),
552 SyntaxKind::CallTargetNode
553 | SyntaxKind::CallAliasNode
554 | SyntaxKind::CallAfterNode
555 | SyntaxKind::CallInputItemNode => {
556 }
558 SyntaxKind::Abandoned | SyntaxKind::MAX => {
559 unreachable!("node should not exist in the tree")
560 }
561 SyntaxKind::Whitespace if reason == VisitReason::Enter => visitor.whitespace(
562 diagnostics,
563 &Whitespace::cast(element.into_token().unwrap()).expect("should cast"),
564 ),
565 SyntaxKind::Comment if reason == VisitReason::Enter => visitor.comment(
566 diagnostics,
567 &Comment::cast(element.into_token().unwrap()).expect("should cast"),
568 ),
569 SyntaxKind::LiteralStringText if reason == VisitReason::Enter => visitor.string_text(
570 diagnostics,
571 &StringText::cast(element.into_token().unwrap()).expect("should cast"),
572 ),
573 SyntaxKind::LiteralCommandText if reason == VisitReason::Enter => visitor.command_text(
574 diagnostics,
575 &CommandText::cast(element.into_token().unwrap()).expect("should cast"),
576 ),
577 _ => {
578 }
580 }
581 }
582}