use alloc::collections::BTreeMap;
use alloc::string::String;
use alloc::vec::Vec;
use strum::{EnumDiscriminants, IntoStaticStr};
use crate::NodeId;
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct FileStructData {
pub node: NodeId,
pub kind: String,
pub data: FileStructValue,
pub node_range: Option<Vec<NodeId>>,
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum FileStructValue {
MethodName(String),
Children(BTreeMap<String, FileStructData>),
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct FileInstanceData {
pub object: String,
pub source: String,
pub source_type: String,
}
#[derive(Clone, PartialEq, Eq, Debug, EnumDiscriminants)]
#[strum_discriminants(name(SyntaxKind), derive(PartialOrd, Ord, Hash, IntoStaticStr))]
pub enum SyntaxNode {
Annotation {
name: String,
arguments_id: Option<NodeId>,
},
Argument,
ArgumentList,
ArrayInitializer,
Assignment {
variable_id: NodeId,
value_id: Option<NodeId>,
operator: Option<String>,
},
Attribute {
name: String,
},
AwaitExpression {
expression_id: NodeId,
},
BinaryOperation {
operator: String,
left_id: Option<NodeId>,
right_id: Option<NodeId>,
},
Break,
CatchClause {
block_id: Option<NodeId>,
catch_declaration: Option<NodeId>,
},
CatchDeclaration,
Class {
name: String,
block_id: Option<NodeId>,
modifiers_id: Option<NodeId>,
inherited_class: Option<String>,
access_modifiers: Option<String>,
},
ClassBody,
Comment {
comment: String,
},
Continue,
Declaration {
name: String,
block_id: Option<NodeId>,
access_modifiers: Option<String>,
},
DeclarationBlock,
Default,
DoStatement {
block_id: NodeId,
condition_id: NodeId,
},
ElementAccess {
expression_id: NodeId,
arguments_id: Option<NodeId>,
},
ElseClause {
block_id: NodeId,
},
ExecutionBlock,
Export {
expression: Option<String>,
declaration_id: Option<NodeId>,
},
ExpressionStatement,
File,
FinallyClause {
block_id: Option<NodeId>,
},
ForEachStatement {
variable_id: NodeId,
iterable_item_id: NodeId,
block_id: Option<NodeId>,
},
ForStatement {
block_id: NodeId,
initializer_id: Option<NodeId>,
condition_id: Option<NodeId>,
update_id: Option<NodeId>,
},
If {
condition_id: NodeId,
true_id: Option<NodeId>,
false_id: Option<NodeId>,
initializer: Option<NodeId>,
},
Import {
expression: Option<String>,
alias: Option<String>,
method_name: Option<String>,
import_type: Option<String>,
},
JsxElement,
LambdaFunctionType,
Literal {
value: String,
value_type: String,
},
MemberAccess {
member: String,
expression: String,
expression_id: NodeId,
symbol_scope: Option<NodeId>,
},
Metadata {
path: String,
structure: BTreeMap<String, FileStructData>,
instances: BTreeMap<String, BTreeMap<String, FileInstanceData>>,
imports: Vec<String>,
package: Option<String>,
},
MethodDeclaration {
name: Option<String>,
access_modifiers: Option<String>,
block_id: Option<NodeId>,
modifiers_id: Option<NodeId>,
parameters_id: Option<NodeId>,
},
MethodInvocation {
expression: String,
object: Option<String>,
symbol_scope: Option<NodeId>,
expression_id: Option<NodeId>,
arguments_id: Option<NodeId>,
object_id: Option<NodeId>,
block_id: Option<NodeId>,
receiver_type_fqn: Option<String>,
},
MissingNode {
node_type: String,
},
Modifiers,
ModuleImport {
expression: String,
alias: Option<String>,
},
NamedArgument {
value_id: NodeId,
argument_name: Option<String>,
},
Namespace {
name: String,
block_id: Option<NodeId>,
},
NewExpression {
constructor_id: NodeId,
arguments_id: Option<NodeId>,
},
Object {
name: Option<String>,
tf_reference: Option<String>,
},
ObjectCreation {
name: String,
arguments_id: Option<NodeId>,
initializer_id: Option<NodeId>,
},
Pair {
key_id: NodeId,
value_id: NodeId,
},
Parameter {
variable: Option<String>,
variable_type: Option<String>,
value_id: Option<NodeId>,
parameter_mode: Option<String>,
},
ParameterList,
ParenthesizedExpression,
ReservedWord {
value: String,
},
RestPattern {
value_id: NodeId,
},
Return {
value_id: Option<NodeId>,
},
Selector {
selector_name: Option<String>,
},
SpreadElement {
value_id: NodeId,
},
SwitchBody,
SwitchSection {
case_expression: String,
},
SwitchStatement {
block_id: NodeId,
value_id: NodeId,
},
SymbolLookup {
symbol: String,
symbol_scope: Option<NodeId>,
value: Option<String>,
},
TernaryOperation {
condition_id: NodeId,
true_id: NodeId,
false_id: NodeId,
},
This {
value: String,
},
ThrowStatement {
expression_id: Option<NodeId>,
},
TryStatement {
block_id: NodeId,
resources_id: Option<NodeId>,
},
UnaryExpression {
operator: String,
operand_id: NodeId,
},
UsingStatement {
block_id: NodeId,
declaration_id: Option<NodeId>,
},
VariableDeclaration {
variable: String,
variable_type: Option<String>,
value_id: Option<NodeId>,
variable_id: Option<NodeId>,
access_modifier: Option<String>,
},
WhileStatement {
block_id: NodeId,
condition_id: Option<NodeId>,
},
}
impl SyntaxNode {
#[must_use]
pub const fn arguments_id(&self) -> Option<NodeId> {
match self {
Self::Annotation { arguments_id, .. }
| Self::ElementAccess { arguments_id, .. }
| Self::MethodInvocation { arguments_id, .. }
| Self::NewExpression { arguments_id, .. }
| Self::ObjectCreation { arguments_id, .. } => *arguments_id,
_ => None,
}
}
#[must_use]
pub const fn block_id(&self) -> Option<NodeId> {
match self {
Self::CatchClause { block_id, .. }
| Self::Class { block_id, .. }
| Self::Declaration { block_id, .. }
| Self::FinallyClause { block_id }
| Self::ForEachStatement { block_id, .. }
| Self::MethodDeclaration { block_id, .. }
| Self::MethodInvocation { block_id, .. }
| Self::Namespace { block_id, .. } => *block_id,
Self::DoStatement { block_id, .. }
| Self::ElseClause { block_id }
| Self::ForStatement { block_id, .. }
| Self::SwitchStatement { block_id, .. }
| Self::TryStatement { block_id, .. }
| Self::UsingStatement { block_id, .. }
| Self::WhileStatement { block_id, .. } => Some(*block_id),
_ => None,
}
}
#[must_use]
pub fn name(&self) -> Option<&str> {
match self {
Self::Annotation { name, .. }
| Self::Attribute { name }
| Self::Class { name, .. }
| Self::Declaration { name, .. }
| Self::Namespace { name, .. }
| Self::ObjectCreation { name, .. } => Some(name.as_str()),
Self::MethodDeclaration { name, .. } | Self::Object { name, .. } => name.as_deref(),
_ => None,
}
}
#[must_use]
pub fn variable(&self) -> Option<&str> {
match self {
Self::VariableDeclaration { variable, .. } => Some(variable.as_str()),
Self::Parameter { variable, .. } => variable.as_deref(),
_ => None,
}
}
#[must_use]
pub const fn symbol(&self) -> Option<&str> {
match self {
Self::SymbolLookup { symbol, .. } => Some(symbol.as_str()),
_ => None,
}
}
#[must_use]
pub fn expression(&self) -> Option<&str> {
match self {
Self::MemberAccess { expression, .. }
| Self::MethodInvocation { expression, .. }
| Self::ModuleImport { expression, .. } => Some(expression.as_str()),
Self::Export { expression, .. } | Self::Import { expression, .. } => {
expression.as_deref()
}
_ => None,
}
}
#[must_use]
pub fn alias(&self) -> Option<&str> {
match self {
Self::Import { alias, .. } | Self::ModuleImport { alias, .. } => alias.as_deref(),
_ => None,
}
}
#[must_use]
pub fn value(&self) -> Option<&str> {
match self {
Self::Literal { value, .. } | Self::ReservedWord { value } | Self::This { value } => {
Some(value.as_str())
}
Self::SymbolLookup { value, .. } => value.as_deref(),
_ => None,
}
}
#[must_use]
pub const fn object_id(&self) -> Option<NodeId> {
match self {
Self::MethodInvocation { object_id, .. } => *object_id,
_ => None,
}
}
#[must_use]
pub const fn value_id(&self) -> Option<NodeId> {
match self {
Self::Assignment { value_id, .. }
| Self::Parameter { value_id, .. }
| Self::Return { value_id }
| Self::VariableDeclaration { value_id, .. } => *value_id,
Self::NamedArgument { value_id, .. }
| Self::Pair { value_id, .. }
| Self::RestPattern { value_id }
| Self::SpreadElement { value_id }
| Self::SwitchStatement { value_id, .. } => Some(*value_id),
_ => None,
}
}
#[must_use]
pub const fn variable_id(&self) -> Option<NodeId> {
match self {
Self::VariableDeclaration { variable_id, .. } => *variable_id,
Self::Assignment { variable_id, .. } | Self::ForEachStatement { variable_id, .. } => {
Some(*variable_id)
}
_ => None,
}
}
#[must_use]
pub(crate) const fn symbol_scope(&self) -> Option<NodeId> {
match self {
Self::MemberAccess { symbol_scope, .. }
| Self::MethodInvocation { symbol_scope, .. }
| Self::SymbolLookup { symbol_scope, .. } => *symbol_scope,
_ => None,
}
}
#[must_use]
pub fn label_type(&self) -> &'static str {
SyntaxKind::from(self).into()
}
}
#[cfg(test)]
mod tests {
use super::SyntaxNode;
use crate::NodeId;
use alloc::borrow::ToOwned;
use alloc::vec;
const BLOCK: NodeId = NodeId(42);
#[test]
#[allow(
clippy::too_many_lines,
reason = "the variant list is the point of the test"
)]
fn every_variant_that_owns_a_body_exposes_it_as_its_block_id() {
let nodes_with_body = vec![
SyntaxNode::CatchClause {
block_id: Some(BLOCK),
catch_declaration: None,
},
SyntaxNode::Class {
name: "Foo".to_owned(),
block_id: Some(BLOCK),
modifiers_id: None,
inherited_class: None,
access_modifiers: None,
},
SyntaxNode::Declaration {
name: "Foo".to_owned(),
block_id: Some(BLOCK),
access_modifiers: None,
},
SyntaxNode::FinallyClause {
block_id: Some(BLOCK),
},
SyntaxNode::ForEachStatement {
variable_id: NodeId(1),
iterable_item_id: NodeId(2),
block_id: Some(BLOCK),
},
SyntaxNode::MethodDeclaration {
name: None,
access_modifiers: None,
block_id: Some(BLOCK),
modifiers_id: None,
parameters_id: None,
},
SyntaxNode::MethodInvocation {
expression: "foo".to_owned(),
object: None,
symbol_scope: None,
expression_id: None,
arguments_id: None,
object_id: None,
block_id: Some(BLOCK),
receiver_type_fqn: None,
},
SyntaxNode::Namespace {
name: "ns".to_owned(),
block_id: Some(BLOCK),
},
SyntaxNode::DoStatement {
block_id: BLOCK,
condition_id: NodeId(1),
},
SyntaxNode::ElseClause { block_id: BLOCK },
SyntaxNode::ForStatement {
block_id: BLOCK,
initializer_id: None,
condition_id: None,
update_id: None,
},
SyntaxNode::SwitchStatement {
block_id: BLOCK,
value_id: NodeId(1),
},
SyntaxNode::TryStatement {
block_id: BLOCK,
resources_id: None,
},
SyntaxNode::UsingStatement {
block_id: BLOCK,
declaration_id: None,
},
SyntaxNode::WhileStatement {
block_id: BLOCK,
condition_id: None,
},
];
for node in nodes_with_body {
assert_eq!(
node.block_id(),
Some(BLOCK),
"{} must expose its body",
node.label_type()
);
}
}
#[test]
fn a_variant_without_a_body_reports_no_block_id() {
assert_eq!(SyntaxNode::Break.block_id(), None);
assert_eq!(
SyntaxNode::FinallyClause { block_id: None }.block_id(),
None
);
}
}