use super::{ AdtExpr, AdtExprBase, Arm, Block, ClosureExpr, Expr, ExprKind, InlineAsmExpr, InlineAsmOperand, Pat, PatKind, Stmt, StmtKind, Thir, }; use crate::thir::LoopMatchMatchData; /// Every `walk_*` method uses deconstruction to access fields of structs and /// enums. This will result in a compile error if a field is added, which makes /// it more likely the appropriate visit call will be added for it. pub trait Visitor<'thir, 'tcx: 'thir>: Sized { fn thir(&self) -> &'thir Thir<'tcx>; fn visit_expr(&mut self, expr: &'thir Expr<'tcx>) { walk_expr(self, expr); } fn visit_stmt(&mut self, stmt: &'thir Stmt<'tcx>) { walk_stmt(self, stmt); } fn visit_block(&mut self, block: &'thir Block) { walk_block(self, block); } fn visit_arm(&mut self, arm: &'thir Arm<'tcx>) { walk_arm(self, arm); } fn visit_pat(&mut self, pat: &'thir Pat<'tcx>) { walk_pat(self, pat); } // Note: We don't have visitors for `ty::Const` and `mir::Const` // (even though these types occur in THIR) for consistency and to reduce confusion, // since the lazy creation of constants during thir construction causes most // 'constants' to not be of type `ty::Const` or `mir::Const` at that // stage (they are mostly still identified by `DefId` or `hir::Lit`, see // the variants `Literal`, `NonHirLiteral` and `NamedConst` in `thir::ExprKind`). // You have to manually visit `ty::Const` and `mir::Const` through the // other `visit*` functions. } pub fn walk_expr<'thir, 'tcx: 'thir, V: Visitor<'thir, 'tcx>>( visitor: &mut V, expr: &'thir Expr<'tcx>, ) { use ExprKind::*; let Expr { kind, ty: _, temp_lifetime: _, span: _ } = expr; match *kind { Scope { value, region_scope: _, lint_level: _ } => { visitor.visit_expr(&visitor.thir()[value]) } Box { value } => visitor.visit_expr(&visitor.thir()[value]), If { cond, then, else_opt, if_then_scope: _ } => { visitor.visit_expr(&visitor.thir()[cond]); visitor.visit_expr(&visitor.thir()[then]); if let Some(else_expr) = else_opt { visitor.visit_expr(&visitor.thir()[else_expr]); } } Call { fun, ref args, ty: _, from_hir_call: _, fn_span: _ } => { visitor.visit_expr(&visitor.thir()[fun]); for &arg in &**args { visitor.visit_expr(&visitor.thir()[arg]); } } ByUse { expr, span: _ } => { visitor.visit_expr(&visitor.thir()[expr]); } Deref { arg } => visitor.visit_expr(&visitor.thir()[arg]), Binary { lhs, rhs, op: _ } | LogicalOp { lhs, rhs, op: _ } => { visitor.visit_expr(&visitor.thir()[lhs]); visitor.visit_expr(&visitor.thir()[rhs]); } Unary { arg, op: _ } => visitor.visit_expr(&visitor.thir()[arg]), Cast { source } => visitor.visit_expr(&visitor.thir()[source]), Use { source } => visitor.visit_expr(&visitor.thir()[source]), NeverToAny { source } => visitor.visit_expr(&visitor.thir()[source]), PointerCoercion { source, cast: _, is_from_as_cast: _ } => { visitor.visit_expr(&visitor.thir()[source]) } Let { expr, ref pat } => { visitor.visit_expr(&visitor.thir()[expr]); visitor.visit_pat(pat); } Loop { body } => visitor.visit_expr(&visitor.thir()[body]), LoopMatch { match_data: box LoopMatchMatchData { scrutinee, ref arms, .. }, .. } | Match { scrutinee, ref arms, .. } => { visitor.visit_expr(&visitor.thir()[scrutinee]); for &arm in &**arms { visitor.visit_arm(&visitor.thir()[arm]); } } Block { block } => visitor.visit_block(&visitor.thir()[block]), Assign { lhs, rhs } | AssignOp { lhs, rhs, op: _ } => { visitor.visit_expr(&visitor.thir()[lhs]); visitor.visit_expr(&visitor.thir()[rhs]); } Field { lhs, variant_index: _, name: _ } => visitor.visit_expr(&visitor.thir()[lhs]), Index { lhs, index } => { visitor.visit_expr(&visitor.thir()[lhs]); visitor.visit_expr(&visitor.thir()[index]); } VarRef { id: _ } | UpvarRef { closure_def_id: _, var_hir_id: _ } => {} Borrow { arg, borrow_kind: _ } => visitor.visit_expr(&visitor.thir()[arg]), RawBorrow { arg, mutability: _ } => visitor.visit_expr(&visitor.thir()[arg]), Break { value, label: _ } => { if let Some(value) = value { visitor.visit_expr(&visitor.thir()[value]) } } Continue { label: _ } => {} ConstContinue { value, label: _ } => visitor.visit_expr(&visitor.thir()[value]), Return { value } => { if let Some(value) = value { visitor.visit_expr(&visitor.thir()[value]) } } Become { value } => visitor.visit_expr(&visitor.thir()[value]), ConstBlock { did: _, args: _ } => {} Repeat { value, count: _ } => { visitor.visit_expr(&visitor.thir()[value]); } Array { ref fields } | Tuple { ref fields } => { for &field in &**fields { visitor.visit_expr(&visitor.thir()[field]); } } Adt(box AdtExpr { ref fields, ref base, adt_def: _, variant_index: _, args: _, user_ty: _, }) => { for field in &**fields { visitor.visit_expr(&visitor.thir()[field.expr]); } if let AdtExprBase::Base(base) = base { visitor.visit_expr(&visitor.thir()[base.base]); } } PlaceTypeAscription { source, user_ty: _, user_ty_span: _ } | ValueTypeAscription { source, user_ty: _, user_ty_span: _ } => { visitor.visit_expr(&visitor.thir()[source]) } PlaceUnwrapUnsafeBinder { source } | ValueUnwrapUnsafeBinder { source } | WrapUnsafeBinder { source } => visitor.visit_expr(&visitor.thir()[source]), Closure(box ClosureExpr { closure_id: _, args: _, upvars: _, movability: _, fake_reads: _, }) => {} Literal { lit: _, neg: _ } => {} NonHirLiteral { lit: _, user_ty: _ } => {} ZstLiteral { user_ty: _ } => {} NamedConst { def_id: _, args: _, user_ty: _ } => {} ConstParam { param: _, def_id: _ } => {} StaticRef { alloc_id: _, ty: _, def_id: _ } => {} InlineAsm(box InlineAsmExpr { asm_macro: _, ref operands, template: _, options: _, line_spans: _, }) => { for op in &**operands { use InlineAsmOperand::*; match op { In { expr, reg: _ } | Out { expr: Some(expr), reg: _, late: _ } | InOut { expr, reg: _, late: _ } => visitor.visit_expr(&visitor.thir()[*expr]), SplitInOut { in_expr, out_expr, reg: _, late: _ } => { visitor.visit_expr(&visitor.thir()[*in_expr]); if let Some(out_expr) = out_expr { visitor.visit_expr(&visitor.thir()[*out_expr]); } } Out { expr: None, reg: _, late: _ } | Const { value: _, span: _ } | SymFn { value: _ } | SymStatic { def_id: _ } => {} Label { block } => visitor.visit_block(&visitor.thir()[*block]), } } } OffsetOf { container: _, fields: _ } => {} ThreadLocalRef(_) => {} Yield { value } => visitor.visit_expr(&visitor.thir()[value]), } } pub fn walk_stmt<'thir, 'tcx: 'thir, V: Visitor<'thir, 'tcx>>( visitor: &mut V, stmt: &'thir Stmt<'tcx>, ) { let Stmt { kind } = stmt; match kind { StmtKind::Expr { expr, scope: _ } => visitor.visit_expr(&visitor.thir()[*expr]), StmtKind::Let { initializer, remainder_scope: _, init_scope: _, pattern, lint_level: _, else_block, span: _, } => { if let Some(init) = initializer { visitor.visit_expr(&visitor.thir()[*init]); } visitor.visit_pat(pattern); if let Some(block) = else_block { visitor.visit_block(&visitor.thir()[*block]) } } } } pub fn walk_block<'thir, 'tcx: 'thir, V: Visitor<'thir, 'tcx>>( visitor: &mut V, block: &'thir Block, ) { let Block { stmts, expr, targeted_by_break: _, region_scope: _, span: _, safety_mode: _ } = block; for &stmt in &*stmts { visitor.visit_stmt(&visitor.thir()[stmt]); } if let Some(expr) = expr { visitor.visit_expr(&visitor.thir()[*expr]); } } pub fn walk_arm<'thir, 'tcx: 'thir, V: Visitor<'thir, 'tcx>>( visitor: &mut V, arm: &'thir Arm<'tcx>, ) { let Arm { guard, pattern, body, lint_level: _, span: _, scope: _ } = arm; if let Some(expr) = guard { visitor.visit_expr(&visitor.thir()[*expr]) } visitor.visit_pat(pattern); visitor.visit_expr(&visitor.thir()[*body]); } pub fn walk_pat<'thir, 'tcx: 'thir, V: Visitor<'thir, 'tcx>>( visitor: &mut V, pat: &'thir Pat<'tcx>, ) { for_each_immediate_subpat(pat, |p| visitor.visit_pat(p)); } /// Invokes `callback` on each immediate subpattern of `pat`, if any. /// A building block for assembling THIR pattern visitors. pub(crate) fn for_each_immediate_subpat<'a, 'tcx>( pat: &'a Pat<'tcx>, mut callback: impl FnMut(&'a Pat<'tcx>), ) { let Pat { kind, ty: _, span: _ } = pat; match kind { PatKind::Missing | PatKind::Wild | PatKind::Binding { subpattern: None, .. } | PatKind::Constant { value: _ } | PatKind::Range(_) | PatKind::Never | PatKind::Error(_) => {} PatKind::AscribeUserType { subpattern, .. } | PatKind::Binding { subpattern: Some(subpattern), .. } | PatKind::Deref { subpattern } | PatKind::DerefPattern { subpattern, .. } | PatKind::ExpandedConstant { subpattern, .. } => callback(subpattern), PatKind::Variant { subpatterns, .. } | PatKind::Leaf { subpatterns } => { for field_pat in subpatterns { callback(&field_pat.pattern); } } PatKind::Slice { prefix, slice, suffix } | PatKind::Array { prefix, slice, suffix } => { for pat in prefix.iter().chain(slice.as_deref()).chain(suffix) { callback(pat); } } PatKind::Or { pats } => { for pat in pats { callback(pat); } } } }