use super::{ASTFlags, ASTNode, BinaryExpr, Expr, FnCallExpr, Ident};
use crate::engine::{KEYWORD_EVAL, OP_EQUALS};
use crate::func::StraightHashMap;
use crate::tokenizer::Token;
use crate::types::dynamic::Union;
use crate::types::Span;
use crate::{calc_fn_hash, Dynamic, FnArgsVec, Position, StaticVec, INT};
#[cfg(feature = "no_std")]
use std::prelude::v1::*;
use std::{
borrow::Borrow,
fmt,
hash::{Hash, Hasher},
mem,
num::NonZeroUsize,
ops::{Range, RangeInclusive},
};
#[derive(Clone, PartialEq, Hash)]
pub struct OpAssignment {
hash_op_assign: u64,
hash_op: u64,
op_assign: Token,
op_assign_syntax: &'static str,
op: Token,
op_syntax: &'static str,
pos: Position,
}
impl OpAssignment {
#[must_use]
#[inline(always)]
pub const fn new_assignment(pos: Position) -> Self {
Self {
hash_op_assign: 0,
hash_op: 0,
op_assign: Token::Equals,
op_assign_syntax: OP_EQUALS,
op: Token::Equals,
op_syntax: OP_EQUALS,
pos,
}
}
#[must_use]
#[inline(always)]
pub const fn is_op_assignment(&self) -> bool {
!matches!(self.op, Token::Equals)
}
#[must_use]
#[inline]
pub const fn get_op_assignment_info(
&self,
) -> Option<(u64, u64, &Token, &'static str, &Token, &'static str)> {
if self.is_op_assignment() {
Some((
self.hash_op_assign,
self.hash_op,
&self.op_assign,
self.op_assign_syntax,
&self.op,
self.op_syntax,
))
} else {
None
}
}
#[must_use]
#[inline(always)]
pub const fn position(&self) -> Position {
self.pos
}
#[must_use]
#[inline(always)]
pub fn new_op_assignment(name: &str, pos: Position) -> Self {
let op = Token::lookup_symbol_from_syntax(name)
.unwrap_or_else(|| panic!("{} is not an op-assignment operator", name));
Self::new_op_assignment_from_token(op, pos)
}
#[must_use]
pub fn new_op_assignment_from_token(op_assign: Token, pos: Position) -> Self {
let op = op_assign
.get_base_op_from_assignment()
.unwrap_or_else(|| panic!("{:?} is not an op-assignment operator", op_assign));
let op_assign_syntax = op_assign.literal_syntax();
let op_syntax = op.literal_syntax();
Self {
hash_op_assign: calc_fn_hash(None, op_assign_syntax, 2),
hash_op: calc_fn_hash(None, op_syntax, 2),
op_assign,
op_assign_syntax,
op,
op_syntax,
pos,
}
}
#[must_use]
#[inline(always)]
pub fn new_op_assignment_from_base(name: &str, pos: Position) -> Self {
let op = Token::lookup_symbol_from_syntax(name)
.unwrap_or_else(|| panic!("{} cannot be converted into an op-operator", name));
Self::new_op_assignment_from_base_token(&op, pos)
}
#[inline(always)]
#[must_use]
pub fn new_op_assignment_from_base_token(op: &Token, pos: Position) -> Self {
Self::new_op_assignment_from_token(
op.convert_to_op_assignment()
.unwrap_or_else(|| panic!("{:?} cannot be converted into an op-operator", op)),
pos,
)
}
}
impl fmt::Debug for OpAssignment {
#[cold]
#[inline(never)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.is_op_assignment() {
f.debug_struct("OpAssignment")
.field("hash_op_assign", &self.hash_op_assign)
.field("hash_op", &self.hash_op)
.field("op_assign", &self.op_assign)
.field("op_assign_syntax", &self.op_assign_syntax)
.field("op", &self.op)
.field("op_syntax", &self.op_syntax)
.field("pos", &self.pos)
.finish()
} else {
write!(f, "{} @ {:?}", Token::Equals, self.pos)
}
}
}
#[derive(Clone, Hash)]
pub enum RangeCase {
ExclusiveInt(Range<INT>, usize),
InclusiveInt(RangeInclusive<INT>, usize),
}
impl fmt::Debug for RangeCase {
#[cold]
#[inline(never)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::ExclusiveInt(r, n) => write!(f, "{}..{} => {n}", r.start, r.end),
Self::InclusiveInt(r, n) => write!(f, "{}..={} => {n}", *r.start(), *r.end()),
}
}
}
impl From<Range<INT>> for RangeCase {
#[inline(always)]
fn from(value: Range<INT>) -> Self {
Self::ExclusiveInt(value, usize::MAX)
}
}
impl From<RangeInclusive<INT>> for RangeCase {
#[inline(always)]
fn from(value: RangeInclusive<INT>) -> Self {
Self::InclusiveInt(value, usize::MAX)
}
}
impl IntoIterator for RangeCase {
type Item = INT;
type IntoIter = Box<dyn Iterator<Item = Self::Item>>;
#[inline]
fn into_iter(self) -> Self::IntoIter {
match self {
Self::ExclusiveInt(r, ..) => Box::new(r),
Self::InclusiveInt(r, ..) => Box::new(r),
}
}
}
impl RangeCase {
#[inline]
#[must_use]
pub fn is_empty(&self) -> bool {
match self {
Self::ExclusiveInt(r, ..) => r.is_empty(),
Self::InclusiveInt(r, ..) => r.is_empty(),
}
}
#[inline]
#[must_use]
pub fn len(&self) -> INT {
match self {
Self::ExclusiveInt(r, ..) if r.is_empty() => 0,
Self::ExclusiveInt(r, ..) => r.end - r.start,
Self::InclusiveInt(r, ..) if r.is_empty() => 0,
Self::InclusiveInt(r, ..) => *r.end() - *r.start() + 1,
}
}
#[inline]
#[must_use]
pub fn contains(&self, value: &Dynamic) -> bool {
match value {
Dynamic(Union::Int(v, ..)) => self.contains_int(*v),
#[cfg(not(feature = "no_float"))]
Dynamic(Union::Float(v, ..)) => self.contains_float(**v),
#[cfg(feature = "decimal")]
Dynamic(Union::Decimal(v, ..)) => self.contains_decimal(**v),
_ => false,
}
}
#[inline]
#[must_use]
pub fn contains_int(&self, n: INT) -> bool {
match self {
Self::ExclusiveInt(r, ..) => r.contains(&n),
Self::InclusiveInt(r, ..) => r.contains(&n),
}
}
#[cfg(not(feature = "no_float"))]
#[inline]
#[must_use]
pub fn contains_float(&self, n: crate::FLOAT) -> bool {
use crate::FLOAT;
match self {
Self::ExclusiveInt(r, ..) => ((r.start as FLOAT)..(r.end as FLOAT)).contains(&n),
Self::InclusiveInt(r, ..) => ((*r.start() as FLOAT)..=(*r.end() as FLOAT)).contains(&n),
}
}
#[cfg(feature = "decimal")]
#[inline]
#[must_use]
pub fn contains_decimal(&self, n: rust_decimal::Decimal) -> bool {
use rust_decimal::Decimal;
match self {
Self::ExclusiveInt(r, ..) => {
(Into::<Decimal>::into(r.start)..Into::<Decimal>::into(r.end)).contains(&n)
}
Self::InclusiveInt(r, ..) => {
(Into::<Decimal>::into(*r.start())..=Into::<Decimal>::into(*r.end())).contains(&n)
}
}
}
#[inline(always)]
#[must_use]
pub const fn is_inclusive(&self) -> bool {
match self {
Self::ExclusiveInt(..) => false,
Self::InclusiveInt(..) => true,
}
}
#[inline(always)]
#[must_use]
pub const fn index(&self) -> usize {
match self {
Self::ExclusiveInt(.., n) | Self::InclusiveInt(.., n) => *n,
}
}
#[inline(always)]
pub fn set_index(&mut self, index: usize) {
match self {
Self::ExclusiveInt(.., n) | Self::InclusiveInt(.., n) => *n = index,
}
}
}
pub type CaseBlocksList = smallvec::SmallVec<[usize; 2]>;
#[derive(Debug, Clone)]
pub struct SwitchCasesCollection {
pub expressions: FnArgsVec<BinaryExpr>,
pub cases: StraightHashMap<CaseBlocksList>,
pub ranges: StaticVec<RangeCase>,
pub def_case: Option<usize>,
}
impl Hash for SwitchCasesCollection {
#[inline(always)]
fn hash<H: Hasher>(&self, state: &mut H) {
self.expressions.hash(state);
self.cases.len().hash(state);
self.cases.iter().for_each(|kv| kv.hash(state));
self.ranges.hash(state);
self.def_case.hash(state);
}
}
#[cfg(not(feature = "no_std"))]
const STMT_BLOCK_INLINE_SIZE: usize = 8;
#[cfg(not(feature = "no_std"))]
pub type StmtBlockContainer = smallvec::SmallVec<[Stmt; STMT_BLOCK_INLINE_SIZE]>;
#[cfg(feature = "no_std")]
pub type StmtBlockContainer = crate::StaticVec<Stmt>;
#[derive(Clone, Hash, Default)]
pub struct StmtBlock {
block: StmtBlockContainer,
span: Span,
}
impl StmtBlock {
pub const NONE: Self = Self::empty(Position::NONE);
#[inline(always)]
#[must_use]
pub fn new(
statements: impl IntoIterator<Item = Stmt>,
start_pos: Position,
end_pos: Position,
) -> Self {
Self::new_with_span(statements, Span::new(start_pos, end_pos))
}
#[must_use]
pub fn new_with_span(statements: impl IntoIterator<Item = Stmt>, span: Span) -> Self {
let mut statements: smallvec::SmallVec<_> = statements.into_iter().collect();
statements.shrink_to_fit();
Self {
block: statements,
span,
}
}
#[inline(always)]
#[must_use]
pub const fn empty(pos: Position) -> Self {
Self {
block: StmtBlockContainer::new_const(),
span: Span::new(pos, pos),
}
}
#[inline(always)]
#[must_use]
pub fn is_empty(&self) -> bool {
self.block.is_empty()
}
#[inline(always)]
#[must_use]
pub fn len(&self) -> usize {
self.block.len()
}
#[inline(always)]
#[must_use]
pub fn statements(&self) -> &[Stmt] {
&self.block
}
#[inline(always)]
#[must_use]
pub fn statements_mut(&mut self) -> &mut StmtBlockContainer {
&mut self.block
}
#[inline(always)]
pub fn iter(&self) -> impl Iterator<Item = &Stmt> {
self.block.iter()
}
#[inline(always)]
#[must_use]
pub const fn position(&self) -> Position {
(self.span).start()
}
#[inline(always)]
#[must_use]
pub const fn end_position(&self) -> Position {
(self.span).end()
}
#[inline(always)]
#[must_use]
pub const fn span(&self) -> Span {
self.span
}
#[inline(always)]
#[must_use]
pub const fn span_or_else(&self, def_start_pos: Position, def_end_pos: Position) -> Span {
Span::new(
(self.span).start().or_else(def_start_pos),
(self.span).end().or_else(def_end_pos),
)
}
#[inline(always)]
pub fn set_position(&mut self, start_pos: Position, end_pos: Position) {
self.span = Span::new(start_pos, end_pos);
}
}
impl Borrow<[Stmt]> for StmtBlock {
#[inline(always)]
fn borrow(&self) -> &[Stmt] {
&self.block
}
}
impl AsRef<[Stmt]> for StmtBlock {
#[inline(always)]
fn as_ref(&self) -> &[Stmt] {
&self.block
}
}
impl AsMut<[Stmt]> for StmtBlock {
#[inline(always)]
fn as_mut(&mut self) -> &mut [Stmt] {
&mut self.block
}
}
impl fmt::Debug for StmtBlock {
#[cold]
#[inline(never)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("Block")?;
fmt::Debug::fmt(&self.block, f)?;
if !self.span.is_none() {
write!(f, " @ {:?}", self.span())?;
}
Ok(())
}
}
impl From<Stmt> for StmtBlock {
#[inline]
fn from(stmt: Stmt) -> Self {
match stmt {
Stmt::Block(block) => *block,
Stmt::Noop(pos) => Self {
block: StmtBlockContainer::new_const(),
span: Span::new(pos, pos),
},
_ => {
let pos = stmt.position();
Self {
block: vec![stmt].into(),
span: Span::new(pos, Position::NONE),
}
}
}
}
}
impl IntoIterator for StmtBlock {
type Item = Stmt;
#[cfg(not(feature = "no_std"))]
type IntoIter = smallvec::IntoIter<[Stmt; STMT_BLOCK_INLINE_SIZE]>;
#[cfg(feature = "no_std")]
type IntoIter = smallvec::IntoIter<[Stmt; crate::STATIC_VEC_INLINE_SIZE]>;
#[inline(always)]
fn into_iter(self) -> Self::IntoIter {
self.block.into_iter()
}
}
impl<'a> IntoIterator for &'a StmtBlock {
type Item = &'a Stmt;
type IntoIter = std::slice::Iter<'a, Stmt>;
#[inline(always)]
fn into_iter(self) -> Self::IntoIter {
self.block.iter()
}
}
impl Extend<Stmt> for StmtBlock {
#[inline(always)]
fn extend<T: IntoIterator<Item = Stmt>>(&mut self, iter: T) {
self.block.extend(iter);
}
}
#[derive(Debug, Clone, Hash)]
pub struct FlowControl {
pub expr: Expr,
pub body: StmtBlock,
pub branch: StmtBlock,
}
#[derive(Debug, Clone, Hash)]
#[non_exhaustive]
#[allow(clippy::type_complexity)]
pub enum Stmt {
Noop(Position),
If(Box<FlowControl>, Position),
Switch(Box<(Expr, SwitchCasesCollection)>, Position),
While(Box<FlowControl>, Position),
Do(Box<FlowControl>, ASTFlags, Position),
For(Box<(Ident, Option<Ident>, FlowControl)>, Position),
Var(Box<(Ident, Expr, Option<NonZeroUsize>)>, ASTFlags, Position),
Assignment(Box<(OpAssignment, BinaryExpr)>),
FnCall(Box<FnCallExpr>, Position),
Block(Box<StmtBlock>),
TryCatch(Box<FlowControl>, Position),
Expr(Box<Expr>),
BreakLoop(Option<Box<Expr>>, ASTFlags, Position),
Return(Option<Box<Expr>>, ASTFlags, Position),
#[cfg(not(feature = "no_module"))]
Import(Box<(Expr, Ident)>, Position),
#[cfg(not(feature = "no_module"))]
Export(Box<(Ident, Ident)>, Position),
#[cfg(not(feature = "no_closure"))]
Share(Box<crate::FnArgsVec<(Ident, Option<NonZeroUsize>)>>),
}
impl Default for Stmt {
#[inline(always)]
fn default() -> Self {
Self::Noop(Position::NONE)
}
}
impl Stmt {
#[inline(always)]
#[must_use]
pub const fn is_noop(&self) -> bool {
matches!(self, Self::Noop(..))
}
#[inline]
#[must_use]
pub const fn options(&self) -> ASTFlags {
match self {
Self::Do(_, options, _)
| Self::Var(_, options, _)
| Self::BreakLoop(_, options, _)
| Self::Return(_, options, _) => *options,
Self::Noop(..)
| Self::If(..)
| Self::Switch(..)
| Self::Block(..)
| Self::Expr(..)
| Self::FnCall(..)
| Self::While(..)
| Self::For(..)
| Self::TryCatch(..)
| Self::Assignment(..) => ASTFlags::empty(),
#[cfg(not(feature = "no_module"))]
Self::Import(..) | Self::Export(..) => ASTFlags::empty(),
#[cfg(not(feature = "no_closure"))]
Self::Share(..) => ASTFlags::empty(),
}
}
#[must_use]
pub fn position(&self) -> Position {
match self {
Self::Noop(pos)
| Self::BreakLoop(.., pos)
| Self::FnCall(.., pos)
| Self::If(.., pos)
| Self::Switch(.., pos)
| Self::While(.., pos)
| Self::Do(.., pos)
| Self::For(.., pos)
| Self::Return(.., pos)
| Self::Var(.., pos)
| Self::TryCatch(.., pos) => *pos,
Self::Assignment(x) => x.0.pos,
Self::Block(x) => x.position(),
Self::Expr(x) => x.start_position(),
#[cfg(not(feature = "no_module"))]
Self::Import(.., pos) => *pos,
#[cfg(not(feature = "no_module"))]
Self::Export(.., pos) => *pos,
#[cfg(not(feature = "no_closure"))]
Self::Share(x) => x[0].0.pos,
}
}
pub fn set_position(&mut self, new_pos: Position) -> &mut Self {
match self {
Self::Noop(pos)
| Self::BreakLoop(.., pos)
| Self::FnCall(.., pos)
| Self::If(.., pos)
| Self::Switch(.., pos)
| Self::While(.., pos)
| Self::Do(.., pos)
| Self::For(.., pos)
| Self::Return(.., pos)
| Self::Var(.., pos)
| Self::TryCatch(.., pos) => *pos = new_pos,
Self::Assignment(x) => x.0.pos = new_pos,
Self::Block(x) => x.set_position(new_pos, x.end_position()),
Self::Expr(x) => {
x.set_position(new_pos);
}
#[cfg(not(feature = "no_module"))]
Self::Import(.., pos) => *pos = new_pos,
#[cfg(not(feature = "no_module"))]
Self::Export(.., pos) => *pos = new_pos,
#[cfg(not(feature = "no_closure"))]
Self::Share(x) => x.iter_mut().for_each(|(x, _)| x.pos = new_pos),
}
self
}
#[must_use]
pub const fn returns_value(&self) -> bool {
match self {
Self::If(..)
| Self::Switch(..)
| Self::Block(..)
| Self::Expr(..)
| Self::FnCall(..) => true,
Self::Noop(..)
| Self::While(..)
| Self::Do(..)
| Self::For(..)
| Self::TryCatch(..) => false,
Self::Var(..) | Self::Assignment(..) | Self::BreakLoop(..) | Self::Return(..) => false,
#[cfg(not(feature = "no_module"))]
Self::Import(..) | Self::Export(..) => false,
#[cfg(not(feature = "no_closure"))]
Self::Share(..) => false,
}
}
#[must_use]
pub const fn is_self_terminated(&self) -> bool {
match self {
Self::If(..)
| Self::Switch(..)
| Self::While(..)
| Self::For(..)
| Self::Block(..)
| Self::TryCatch(..) => true,
Self::Noop(..) => false,
Self::Expr(e) => match &**e {
#[cfg(not(feature = "no_custom_syntax"))]
Expr::Custom(x, ..) if x.is_self_terminated() => true,
_ => false,
},
Self::Var(..)
| Self::Assignment(..)
| Self::FnCall(..)
| Self::Do(..)
| Self::BreakLoop(..)
| Self::Return(..) => false,
#[cfg(not(feature = "no_module"))]
Self::Import(..) | Self::Export(..) => false,
#[cfg(not(feature = "no_closure"))]
Self::Share(..) => false,
}
}
#[must_use]
pub fn is_pure(&self) -> bool {
match self {
Self::Noop(..) => true,
Self::Expr(expr) => expr.is_pure(),
Self::If(x, ..) => {
x.expr.is_pure()
&& x.body.iter().all(Self::is_pure)
&& x.branch.iter().all(Self::is_pure)
}
Self::Switch(x, ..) => {
let (expr, sw) = &**x;
expr.is_pure()
&& sw.cases.values().flat_map(|cases| cases.iter()).all(|&c| {
let block = &sw.expressions[c];
block.lhs.is_pure() && block.rhs.is_pure()
})
&& sw.ranges.iter().all(|r| {
let block = &sw.expressions[r.index()];
block.lhs.is_pure() && block.rhs.is_pure()
})
&& sw.def_case.is_some()
&& sw.expressions[sw.def_case.unwrap()].rhs.is_pure()
}
Self::While(x, ..) if matches!(x.expr, Expr::BoolConstant(false, ..)) => true,
Self::Do(x, options, ..) if matches!(x.expr, Expr::BoolConstant(..)) => match x.expr {
Expr::BoolConstant(cond, ..) if cond == options.intersects(ASTFlags::NEGATED) => {
x.body.iter().all(Self::is_pure)
}
_ => false,
},
Self::While(..) | Self::Do(..) => false,
Self::For(x, ..) => x.2.expr.is_pure() && x.2.body.iter().all(Self::is_pure),
Self::Var(..) | Self::Assignment(..) | Self::FnCall(..) => false,
Self::Block(block, ..) => block.iter().all(Self::is_pure),
Self::BreakLoop(..) | Self::Return(..) => false,
Self::TryCatch(x, ..) => {
x.expr.is_pure()
&& x.body.iter().all(Self::is_pure)
&& x.branch.iter().all(Self::is_pure)
}
#[cfg(not(feature = "no_module"))]
Self::Import(..) => false,
#[cfg(not(feature = "no_module"))]
Self::Export(..) => false,
#[cfg(not(feature = "no_closure"))]
Self::Share(..) => false,
}
}
#[inline]
#[must_use]
pub fn is_block_dependent(&self) -> bool {
match self {
Self::Var(..) => true,
Self::Expr(e) => match &**e {
Expr::Stmt(s) => s.iter().all(Self::is_block_dependent),
#[cfg(not(feature = "no_module"))]
Expr::FnCall(x, ..) if x.is_qualified() => false,
Expr::FnCall(x, ..) => x.name == KEYWORD_EVAL,
_ => false,
},
#[cfg(not(feature = "no_module"))]
Self::FnCall(x, ..) if x.is_qualified() => false,
Self::FnCall(x, ..) => x.name == KEYWORD_EVAL,
#[cfg(not(feature = "no_module"))]
Self::Import(..) | Self::Export(..) => true,
_ => false,
}
}
#[inline]
#[must_use]
pub fn is_internally_pure(&self) -> bool {
match self {
Self::Var(x, ..) => x.1.is_pure(),
Self::Expr(e) => match &**e {
Expr::Stmt(s) => s.iter().all(Self::is_internally_pure),
_ => self.is_pure(),
},
#[cfg(not(feature = "no_module"))]
Self::Import(x, ..) => x.0.is_pure(),
#[cfg(not(feature = "no_module"))]
Self::Export(..) => true,
_ => self.is_pure(),
}
}
#[inline]
#[must_use]
pub const fn is_control_flow_break(&self) -> bool {
matches!(self, Self::Return(..) | Self::BreakLoop(..))
}
#[inline(always)]
#[must_use]
pub fn take(&mut self) -> Self {
mem::take(self)
}
pub fn walk<'a>(
&'a self,
path: &mut Vec<ASTNode<'a>>,
on_node: &mut (impl FnMut(&[ASTNode]) -> bool + ?Sized),
) -> bool {
path.push(self.into());
if !on_node(path) {
return false;
}
match self {
Self::Var(x, ..) => {
if !x.1.walk(path, on_node) {
return false;
}
}
Self::If(x, ..) => {
if !x.expr.walk(path, on_node) {
return false;
}
for s in &x.body {
if !s.walk(path, on_node) {
return false;
}
}
for s in &x.branch {
if !s.walk(path, on_node) {
return false;
}
}
}
Self::Switch(x, ..) => {
let (expr, sw) = &**x;
if !expr.walk(path, on_node) {
return false;
}
for (.., blocks) in &sw.cases {
for &b in blocks {
let block = &sw.expressions[b];
if !block.lhs.walk(path, on_node) {
return false;
}
if !block.rhs.walk(path, on_node) {
return false;
}
}
}
for r in &sw.ranges {
let block = &sw.expressions[r.index()];
if !block.lhs.walk(path, on_node) {
return false;
}
if !block.rhs.walk(path, on_node) {
return false;
}
}
if let Some(index) = sw.def_case {
if !sw.expressions[index].lhs.walk(path, on_node) {
return false;
}
}
}
Self::While(x, ..) | Self::Do(x, ..) => {
if !x.expr.walk(path, on_node) {
return false;
}
for s in x.body.statements() {
if !s.walk(path, on_node) {
return false;
}
}
}
Self::For(x, ..) => {
if !x.2.expr.walk(path, on_node) {
return false;
}
for s in &x.2.body {
if !s.walk(path, on_node) {
return false;
}
}
}
Self::Assignment(x, ..) => {
if !x.1.lhs.walk(path, on_node) {
return false;
}
if !x.1.rhs.walk(path, on_node) {
return false;
}
}
Self::FnCall(x, ..) => {
for s in &*x.args {
if !s.walk(path, on_node) {
return false;
}
}
}
Self::Block(x, ..) => {
for s in x.statements() {
if !s.walk(path, on_node) {
return false;
}
}
}
Self::TryCatch(x, ..) => {
for s in &x.body {
if !s.walk(path, on_node) {
return false;
}
}
for s in &x.branch {
if !s.walk(path, on_node) {
return false;
}
}
}
Self::Expr(e) => {
if !e.walk(path, on_node) {
return false;
}
}
Self::Return(Some(e), ..) => {
if !e.walk(path, on_node) {
return false;
}
}
#[cfg(not(feature = "no_module"))]
Self::Import(x, ..) => {
if !x.0.walk(path, on_node) {
return false;
}
}
_ => (),
}
path.pop().unwrap();
true
}
}