use rowan::WalkEvent;
use tracing::trace;
use wdl_ast::AstNode;
use wdl_ast::AstToken;
use wdl_ast::Comment;
use wdl_ast::SupportedVersion;
use wdl_ast::SyntaxKind;
use wdl_ast::SyntaxNode;
use wdl_ast::VersionStatement;
use wdl_ast::Whitespace;
use wdl_ast::v1::BoundDecl;
use wdl_ast::v1::CallStatement;
use wdl_ast::v1::CommandSection;
use wdl_ast::v1::CommandText;
use wdl_ast::v1::ConditionalStatement;
use wdl_ast::v1::EnumDefinition;
use wdl_ast::v1::Expr;
use wdl_ast::v1::ImportStatement;
use wdl_ast::v1::InputSection;
use wdl_ast::v1::MetadataArray;
use wdl_ast::v1::MetadataObject;
use wdl_ast::v1::MetadataObjectItem;
use wdl_ast::v1::MetadataSection;
use wdl_ast::v1::OutputSection;
use wdl_ast::v1::ParameterMetadataSection;
use wdl_ast::v1::Placeholder;
use wdl_ast::v1::RequirementsSection;
use wdl_ast::v1::RuntimeItem;
use wdl_ast::v1::RuntimeSection;
use wdl_ast::v1::ScatterStatement;
use wdl_ast::v1::StringText;
use wdl_ast::v1::StructDefinition;
use wdl_ast::v1::TaskDefinition;
use wdl_ast::v1::TaskHintsSection;
use wdl_ast::v1::UnboundDecl;
use wdl_ast::v1::WorkflowDefinition;
use wdl_ast::v1::WorkflowHintsSection;
use crate::Config;
use crate::Diagnostics;
use crate::document::Document as AnalysisDocument;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum VisitReason {
Enter,
Exit,
}
#[allow(unused_variables)]
pub trait Visitor {
fn register(&mut self, config: &Config) {}
fn reset(&mut self);
fn document(
&mut self,
diagnostics: &mut Diagnostics,
reason: VisitReason,
doc: &AnalysisDocument,
version: SupportedVersion,
) {
}
fn whitespace(&mut self, diagnostics: &mut Diagnostics, whitespace: &Whitespace) {}
fn comment(&mut self, diagnostics: &mut Diagnostics, comment: &Comment) {}
fn version_statement(
&mut self,
diagnostics: &mut Diagnostics,
reason: VisitReason,
stmt: &VersionStatement,
) {
}
fn import_statement(
&mut self,
diagnostics: &mut Diagnostics,
reason: VisitReason,
stmt: &ImportStatement,
) {
}
fn struct_definition(
&mut self,
diagnostics: &mut Diagnostics,
reason: VisitReason,
def: &StructDefinition,
) {
}
fn enum_definition(
&mut self,
diagnostics: &mut Diagnostics,
reason: VisitReason,
def: &EnumDefinition,
) {
}
fn task_definition(
&mut self,
diagnostics: &mut Diagnostics,
reason: VisitReason,
task: &TaskDefinition,
) {
}
fn workflow_definition(
&mut self,
diagnostics: &mut Diagnostics,
reason: VisitReason,
workflow: &WorkflowDefinition,
) {
}
fn input_section(
&mut self,
diagnostics: &mut Diagnostics,
reason: VisitReason,
section: &InputSection,
) {
}
fn output_section(
&mut self,
diagnostics: &mut Diagnostics,
reason: VisitReason,
section: &OutputSection,
) {
}
fn command_section(
&mut self,
diagnostics: &mut Diagnostics,
reason: VisitReason,
section: &CommandSection,
) {
}
fn command_text(&mut self, diagnostics: &mut Diagnostics, text: &CommandText) {}
fn requirements_section(
&mut self,
diagnostics: &mut Diagnostics,
reason: VisitReason,
section: &RequirementsSection,
) {
}
fn task_hints_section(
&mut self,
diagnostics: &mut Diagnostics,
reason: VisitReason,
section: &TaskHintsSection,
) {
}
fn workflow_hints_section(
&mut self,
diagnostics: &mut Diagnostics,
reason: VisitReason,
section: &WorkflowHintsSection,
) {
}
fn runtime_section(
&mut self,
diagnostics: &mut Diagnostics,
reason: VisitReason,
section: &RuntimeSection,
) {
}
fn runtime_item(
&mut self,
diagnostics: &mut Diagnostics,
reason: VisitReason,
item: &RuntimeItem,
) {
}
fn metadata_section(
&mut self,
diagnostics: &mut Diagnostics,
reason: VisitReason,
section: &MetadataSection,
) {
}
fn parameter_metadata_section(
&mut self,
diagnostics: &mut Diagnostics,
reason: VisitReason,
section: &ParameterMetadataSection,
) {
}
fn metadata_object(
&mut self,
diagnostics: &mut Diagnostics,
reason: VisitReason,
object: &MetadataObject,
) {
}
fn metadata_object_item(
&mut self,
diagnostics: &mut Diagnostics,
reason: VisitReason,
item: &MetadataObjectItem,
) {
}
fn metadata_array(
&mut self,
diagnostics: &mut Diagnostics,
reason: VisitReason,
item: &MetadataArray,
) {
}
fn unbound_decl(
&mut self,
diagnostics: &mut Diagnostics,
reason: VisitReason,
decl: &UnboundDecl,
) {
}
fn bound_decl(&mut self, diagnostics: &mut Diagnostics, reason: VisitReason, decl: &BoundDecl) {
}
fn expr(&mut self, diagnostics: &mut Diagnostics, reason: VisitReason, expr: &Expr) {}
fn string_text(&mut self, diagnostics: &mut Diagnostics, text: &StringText) {}
fn placeholder(
&mut self,
diagnostics: &mut Diagnostics,
reason: VisitReason,
placeholder: &Placeholder,
) {
}
fn conditional_statement(
&mut self,
diagnostics: &mut Diagnostics,
reason: VisitReason,
stmt: &ConditionalStatement,
) {
}
fn scatter_statement(
&mut self,
diagnostics: &mut Diagnostics,
reason: VisitReason,
stmt: &ScatterStatement,
) {
}
fn call_statement(
&mut self,
diagnostics: &mut Diagnostics,
reason: VisitReason,
stmt: &CallStatement,
) {
}
}
pub(crate) fn visit<V: Visitor>(
document: &AnalysisDocument,
diagnostics: &mut Diagnostics,
visitor: &mut V,
) {
trace!(
uri = %document.uri(),
"beginning document traversal",
);
for event in document.root().inner().preorder_with_tokens() {
let (reason, element) = match event {
WalkEvent::Enter(node) => (VisitReason::Enter, node),
WalkEvent::Leave(node) => (VisitReason::Exit, node),
};
trace!(uri = %document.uri(), ?reason, element = ?element.kind());
match element.kind() {
SyntaxKind::RootNode => visitor.document(
diagnostics,
reason,
document,
document
.version()
.expect("visited document must have a version"),
),
SyntaxKind::VersionStatementNode => visitor.version_statement(
diagnostics,
reason,
&VersionStatement::cast(element.into_node().unwrap()).expect("should cast"),
),
SyntaxKind::ImportStatementNode => visitor.import_statement(
diagnostics,
reason,
&ImportStatement::cast(element.into_node().unwrap()).expect("should cast"),
),
SyntaxKind::ImportAliasNode => {
}
SyntaxKind::StructDefinitionNode => visitor.struct_definition(
diagnostics,
reason,
&StructDefinition::cast(element.into_node().unwrap()).expect("should cast"),
),
SyntaxKind::EnumDefinitionNode => visitor.enum_definition(
diagnostics,
reason,
&EnumDefinition::cast(element.into_node().unwrap()).expect("should cast"),
),
SyntaxKind::TaskDefinitionNode => visitor.task_definition(
diagnostics,
reason,
&TaskDefinition::cast(element.into_node().unwrap()).expect("should cast"),
),
SyntaxKind::WorkflowDefinitionNode => visitor.workflow_definition(
diagnostics,
reason,
&WorkflowDefinition::cast(element.into_node().unwrap()).expect("should cast"),
),
SyntaxKind::UnboundDeclNode => visitor.unbound_decl(
diagnostics,
reason,
&UnboundDecl::cast(element.into_node().unwrap()).expect("should cast"),
),
SyntaxKind::BoundDeclNode => visitor.bound_decl(
diagnostics,
reason,
&BoundDecl::cast(element.into_node().unwrap()).expect("should cast"),
),
SyntaxKind::PrimitiveTypeNode
| SyntaxKind::MapTypeNode
| SyntaxKind::ArrayTypeNode
| SyntaxKind::PairTypeNode
| SyntaxKind::ObjectTypeNode
| SyntaxKind::TypeRefNode => {
}
SyntaxKind::InputSectionNode => visitor.input_section(
diagnostics,
reason,
&InputSection::cast(element.into_node().unwrap()).expect("should cast"),
),
SyntaxKind::OutputSectionNode => visitor.output_section(
diagnostics,
reason,
&OutputSection::cast(element.into_node().unwrap()).expect("should cast"),
),
SyntaxKind::CommandSectionNode => visitor.command_section(
diagnostics,
reason,
&CommandSection::cast(element.into_node().unwrap()).expect("should cast"),
),
SyntaxKind::RequirementsSectionNode => visitor.requirements_section(
diagnostics,
reason,
&RequirementsSection::cast(element.into_node().unwrap()).expect("should cast"),
),
SyntaxKind::TaskHintsSectionNode => visitor.task_hints_section(
diagnostics,
reason,
&TaskHintsSection::cast(element.into_node().unwrap()).expect("should cast"),
),
SyntaxKind::WorkflowHintsSectionNode => visitor.workflow_hints_section(
diagnostics,
reason,
&WorkflowHintsSection::cast(element.into_node().unwrap()).expect("should cast"),
),
SyntaxKind::TaskHintsItemNode | SyntaxKind::WorkflowHintsItemNode => {
}
SyntaxKind::RequirementsItemNode => {
}
SyntaxKind::RuntimeSectionNode => visitor.runtime_section(
diagnostics,
reason,
&RuntimeSection::cast(element.into_node().unwrap()).expect("should cast"),
),
SyntaxKind::RuntimeItemNode => visitor.runtime_item(
diagnostics,
reason,
&RuntimeItem::cast(element.into_node().unwrap()).expect("should cast"),
),
SyntaxKind::MetadataSectionNode => visitor.metadata_section(
diagnostics,
reason,
&MetadataSection::cast(element.into_node().unwrap()).expect("should cast"),
),
SyntaxKind::ParameterMetadataSectionNode => visitor.parameter_metadata_section(
diagnostics,
reason,
&ParameterMetadataSection::cast(element.into_node().unwrap()).expect("should cast"),
),
SyntaxKind::MetadataObjectNode => visitor.metadata_object(
diagnostics,
reason,
&MetadataObject::cast(element.into_node().unwrap()).expect("should cast"),
),
SyntaxKind::MetadataObjectItemNode => visitor.metadata_object_item(
diagnostics,
reason,
&MetadataObjectItem::cast(element.into_node().unwrap()).expect("should cast"),
),
SyntaxKind::MetadataArrayNode => visitor.metadata_array(
diagnostics,
reason,
&MetadataArray::cast(element.into_node().unwrap()).expect("should cast"),
),
SyntaxKind::LiteralNullNode => {
}
k if Expr::<SyntaxNode>::can_cast(k) => {
visitor.expr(
diagnostics,
reason,
&Expr::cast(element.into_node().expect(
"any element that is able to be turned into an expr should be a node",
))
.expect("expr should be built"),
)
}
SyntaxKind::LiteralMapItemNode
| SyntaxKind::LiteralObjectItemNode
| SyntaxKind::LiteralStructItemNode
| SyntaxKind::LiteralHintsItemNode
| SyntaxKind::LiteralInputItemNode
| SyntaxKind::LiteralOutputItemNode => {
}
k @ (SyntaxKind::LiteralIntegerNode
| SyntaxKind::LiteralFloatNode
| SyntaxKind::LiteralBooleanNode
| SyntaxKind::LiteralNoneNode
| SyntaxKind::LiteralStringNode
| SyntaxKind::LiteralPairNode
| SyntaxKind::LiteralArrayNode
| SyntaxKind::LiteralMapNode
| SyntaxKind::LiteralObjectNode
| SyntaxKind::LiteralStructNode
| SyntaxKind::LiteralHintsNode
| SyntaxKind::LiteralInputNode
| SyntaxKind::LiteralOutputNode
| SyntaxKind::ParenthesizedExprNode
| SyntaxKind::NameRefExprNode
| SyntaxKind::IfExprNode
| SyntaxKind::LogicalNotExprNode
| SyntaxKind::NegationExprNode
| SyntaxKind::LogicalOrExprNode
| SyntaxKind::LogicalAndExprNode
| SyntaxKind::EqualityExprNode
| SyntaxKind::InequalityExprNode
| SyntaxKind::LessExprNode
| SyntaxKind::LessEqualExprNode
| SyntaxKind::GreaterExprNode
| SyntaxKind::GreaterEqualExprNode
| SyntaxKind::AdditionExprNode
| SyntaxKind::SubtractionExprNode
| SyntaxKind::MultiplicationExprNode
| SyntaxKind::DivisionExprNode
| SyntaxKind::ModuloExprNode
| SyntaxKind::CallExprNode
| SyntaxKind::IndexExprNode
| SyntaxKind::AccessExprNode) => {
unreachable!("`{k:?}` should be handled by `Expr::can_cast`")
}
SyntaxKind::PlaceholderNode => visitor.placeholder(
diagnostics,
reason,
&Placeholder::cast(element.into_node().unwrap()).expect("should cast"),
),
SyntaxKind::PlaceholderSepOptionNode
| SyntaxKind::PlaceholderDefaultOptionNode
| SyntaxKind::PlaceholderTrueFalseOptionNode => {
}
SyntaxKind::ConditionalStatementNode => visitor.conditional_statement(
diagnostics,
reason,
&ConditionalStatement::cast(element.into_node().unwrap()).expect("should cast"),
),
SyntaxKind::ScatterStatementNode => visitor.scatter_statement(
diagnostics,
reason,
&ScatterStatement::cast(element.into_node().unwrap()).expect("should cast"),
),
SyntaxKind::CallStatementNode => visitor.call_statement(
diagnostics,
reason,
&CallStatement::cast(element.into_node().unwrap()).expect("should cast"),
),
SyntaxKind::CallTargetNode
| SyntaxKind::CallAliasNode
| SyntaxKind::CallAfterNode
| SyntaxKind::CallInputItemNode => {
}
SyntaxKind::Abandoned | SyntaxKind::MAX => {
unreachable!("node should not exist in the tree")
}
SyntaxKind::Whitespace if reason == VisitReason::Enter => visitor.whitespace(
diagnostics,
&Whitespace::cast(element.into_token().unwrap()).expect("should cast"),
),
SyntaxKind::Comment if reason == VisitReason::Enter => visitor.comment(
diagnostics,
&Comment::cast(element.into_token().unwrap()).expect("should cast"),
),
SyntaxKind::LiteralStringText if reason == VisitReason::Enter => visitor.string_text(
diagnostics,
&StringText::cast(element.into_token().unwrap()).expect("should cast"),
),
SyntaxKind::LiteralCommandText if reason == VisitReason::Enter => visitor.command_text(
diagnostics,
&CommandText::cast(element.into_token().unwrap()).expect("should cast"),
),
_ => {
}
}
}
}