use super::*;
use std::ops::ControlFlow;
solar_macros::declare_visitors! {
pub trait Visit<'hir> {
type BreakValue;
fn hir(&self) -> &'hir Hir<'hir>;
fn visit_nested_source(&mut self, id: SourceId) -> ControlFlow<Self::BreakValue>{
visit_nested_items(self, self.hir().source(id).items)
}
fn visit_nested_item(&mut self, id: ItemId) -> ControlFlow<Self::BreakValue> {
match id {
ItemId::Contract(id) => self.visit_nested_contract(id),
ItemId::Function(id) => self.visit_nested_function(id),
ItemId::Struct(id) => self.visit_nested_struct(id),
ItemId::Enum(id) => self.visit_nested_enum(id),
ItemId::Udvt(id) => self.visit_nested_udvt(id),
ItemId::Error(id) => self.visit_nested_error(id),
ItemId::Event(id) => self.visit_nested_event(id),
ItemId::Variable(id) => self.visit_nested_var(id),
}
}
fn visit_item(&mut self, item: Item<'hir, 'hir>) -> ControlFlow<Self::BreakValue> {
match item {
Item::Contract(item) => self.visit_contract(item),
Item::Function(item) => self.visit_function(item),
Item::Struct(item) => self.visit_struct(item),
Item::Enum(item) => self.visit_enum(item),
Item::Udvt(item) => self.visit_udvt(item),
Item::Error(item) => self.visit_error(item),
Item::Event(item) => self.visit_event(item),
Item::Variable(item) => self.visit_var(item),
}
}
fn visit_nested_contract(&mut self, id: ContractId) -> ControlFlow<Self::BreakValue> {
self.visit_contract(self.hir().contract(id))
}
fn visit_contract(&mut self, contract: &'hir Contract<'hir>) -> ControlFlow<Self::BreakValue> {
for base in contract.bases_args {
self.visit_modifier(base)?;
}
visit_nested_items(self, contract.items)
}
fn visit_nested_function(&mut self, id: FunctionId) -> ControlFlow<Self::BreakValue> {
self.visit_function(self.hir().function(id))
}
fn visit_function(&mut self, func: &'hir Function<'hir>) -> ControlFlow<Self::BreakValue> {
let Function { source: _, doc: _, contract: _, span: _, name: _, kind: _, is_yul: _, visibility: _, state_mutability: _, modifiers, marked_virtual: _, virtual_: _, override_: _, overrides: _, parameters, returns, body, body_span: _, gettee: _ } = func;
for ¶m in parameters.iter() {
self.visit_nested_var(param)?;
}
for modifier in modifiers.iter() {
self.visit_modifier(modifier)?;
}
for &ret in returns.iter() {
self.visit_nested_var(ret)?;
}
if let Some(body) = body {
for stmt in body.iter() {
self.visit_stmt(stmt)?;
}
}
ControlFlow::Continue(())
}
fn visit_modifier(&mut self, modifier: &'hir Modifier<'hir>) -> ControlFlow<Self::BreakValue> {
let Modifier { span: _, id: _, args } = modifier;
self.visit_call_args(args)
}
fn visit_nested_struct(&mut self, id: StructId) -> ControlFlow<Self::BreakValue> {
self.visit_struct(self.hir().strukt(id))
}
fn visit_struct(&mut self, strukt: &'hir Struct<'hir>) -> ControlFlow<Self::BreakValue> {
for &field in strukt.fields {
self.visit_nested_var(field)?;
}
ControlFlow::Continue(())
}
fn visit_nested_enum(&mut self, id: EnumId) -> ControlFlow<Self::BreakValue> {
self.visit_enum(self.hir().enumm(id))
}
fn visit_enum(&mut self, _enumm: &'hir Enum<'hir>) -> ControlFlow<Self::BreakValue> {
ControlFlow::Continue(())
}
fn visit_nested_udvt(&mut self, id: UdvtId) -> ControlFlow<Self::BreakValue> {
self.visit_udvt(self.hir().udvt(id))
}
fn visit_udvt(&mut self, udvt: &'hir Udvt<'hir>) -> ControlFlow<Self::BreakValue> {
self.visit_ty(&udvt.ty)
}
fn visit_nested_error(&mut self, id: ErrorId) -> ControlFlow<Self::BreakValue> {
self.visit_error(self.hir().error(id))
}
fn visit_error(&mut self, error: &'hir Error<'hir>) -> ControlFlow<Self::BreakValue> {
for ¶m in error.parameters {
self.visit_nested_var(param)?;
}
ControlFlow::Continue(())
}
fn visit_nested_event(&mut self, id: EventId) -> ControlFlow<Self::BreakValue> {
self.visit_event(self.hir().event(id))
}
fn visit_event(&mut self, event: &'hir Event<'hir>) -> ControlFlow<Self::BreakValue> {
for ¶m in event.parameters {
self.visit_nested_var(param)?;
}
ControlFlow::Continue(())
}
fn visit_nested_var(&mut self, id: VariableId) -> ControlFlow<Self::BreakValue> {
self.visit_var(self.hir().variable(id))
}
fn visit_var(&mut self, var: &'hir Variable<'hir>) -> ControlFlow<Self::BreakValue> {
self.visit_ty(&var.ty)?;
if let Some(expr) = var.initializer {
self.visit_expr(expr)?;
}
ControlFlow::Continue(())
}
fn visit_expr(&mut self, expr: &'hir Expr<'hir>) -> ControlFlow<Self::BreakValue> {
match expr.kind {
ExprKind::Call(expr, ref args, opts) => {
self.visit_expr(expr)?;
if let Some(opts) = opts {
for arg in opts.args {
self.visit_expr(&arg.value)?;
}
}
self.visit_call_args(args)?;
}
ExprKind::Delete(expr)
| ExprKind::Member(expr, _)
| ExprKind::Payable(expr)
| ExprKind::Unary(_, expr)
| ExprKind::YulMember(expr, _) => self.visit_expr(expr)?,
ExprKind::Assign(lhs, _, rhs) | ExprKind::Binary(lhs, _, rhs) => {
self.visit_expr(lhs)?;
self.visit_expr(rhs)?;
}
ExprKind::Index(expr, index) => {
self.visit_expr(expr)?;
if let Some(index) = index {
self.visit_expr(index)?;
}
}
ExprKind::Slice(expr, start, end) => {
self.visit_expr(expr)?;
if let Some(start) = start {
self.visit_expr(start)?;
}
if let Some(end) = end {
self.visit_expr(end)?;
}
}
ExprKind::Ternary(cond, true_, false_) => {
self.visit_expr(cond)?;
self.visit_expr(true_)?;
self.visit_expr(false_)?;
}
ExprKind::Array(exprs) => {
for expr in exprs {
self.visit_expr(expr)?;
}
}
ExprKind::Tuple(exprs) => {
exprs.iter().copied().flatten().try_for_each(|expr| self.visit_expr(expr))?;
}
ExprKind::Ident(_) => {}
ExprKind::Lit(_) => {}
ExprKind::New(ref ty) | ExprKind::TypeCall(ref ty) | ExprKind::Type(ref ty) => {
self.visit_ty(ty)?;
}
ExprKind::Err(_guar) => {}
}
ControlFlow::Continue(())
}
fn visit_call_args(&mut self, args: &'hir CallArgs<'hir>) -> ControlFlow<Self::BreakValue> {
let CallArgs { span: _, kind } = args;
for expr in kind.exprs() {
self.visit_expr(expr)?;
}
ControlFlow::Continue(())
}
fn visit_stmt(&mut self, stmt: &'hir Stmt<'hir>) -> ControlFlow<Self::BreakValue> {
match stmt.kind {
StmtKind::DeclSingle(var) => self.visit_nested_var(var)?,
StmtKind::DeclMulti(vars, expr) => {
for &var in vars {
if let Some(var) = var {
self.visit_nested_var(var)?;
}
}
self.visit_expr(expr)?;
}
StmtKind::Block(block)
| StmtKind::UncheckedBlock(block)
| StmtKind::AssemblyBlock(block)
| StmtKind::Loop(block, _) => {
for stmt in block.stmts {
self.visit_stmt(stmt)?;
}
}
StmtKind::Emit(expr) => self.visit_expr(expr)?,
StmtKind::Revert(expr) => self.visit_expr(expr)?,
StmtKind::Return(expr) => {
if let Some(expr) = expr {
self.visit_expr(expr)?;
}
}
StmtKind::Break => {}
StmtKind::Continue => {}
StmtKind::If(cond, true_, false_) => {
self.visit_expr(cond)?;
self.visit_stmt(true_)?;
if let Some(false_) = false_ {
self.visit_stmt(false_)?;
}
}
StmtKind::Switch(switch) => {
self.visit_expr(switch.selector)?;
for case in switch.cases {
for stmt in case.body.iter() {
self.visit_stmt(stmt)?;
}
}
}
StmtKind::Try(try_) => {
self.visit_expr(&try_.expr)?;
for clause in try_.clauses {
for &var in clause.args {
self.visit_nested_var(var)?;
}
for stmt in clause.block.iter() {
self.visit_stmt(stmt)?;
}
}
}
StmtKind::Expr(expr) => self.visit_expr(expr)?,
StmtKind::Placeholder => {}
StmtKind::Err(_guar) => {}
}
ControlFlow::Continue(())
}
fn visit_ty(&mut self, ty: &'hir Type<'hir>) -> ControlFlow<Self::BreakValue> {
match ty.kind {
TypeKind::Elementary(_) => {}
TypeKind::Array(arr) => {
self.visit_ty(&arr.element)?;
if let Some(len) = arr.size {
self.visit_expr(len)?;
}
}
TypeKind::Function(func) => {
for ¶m in func.parameters {
self.visit_nested_var(param)?;
}
for &ret in func.returns {
self.visit_nested_var(ret)?;
}
}
TypeKind::Mapping(map) => {
self.visit_ty(&map.key)?;
self.visit_ty(&map.value)?;
}
TypeKind::Custom(_) => {}
TypeKind::Err(_guar) => {}
}
ControlFlow::Continue(())
}
}
}
fn visit_nested_items<'hir, V: Visit<'hir> + ?Sized>(
v: &mut V,
ids: &[ItemId],
) -> ControlFlow<V::BreakValue> {
ids.iter().try_for_each(|&id| v.visit_nested_item(id))
}