rustc_middle/thir/
visit.rs

1use super::{
2    AdtExpr, AdtExprBase, Arm, Block, ClosureExpr, Expr, ExprKind, InlineAsmExpr, InlineAsmOperand,
3    Pat, PatKind, Stmt, StmtKind, Thir,
4};
5use crate::thir::LoopMatchMatchData;
6
7/// Every `walk_*` method uses deconstruction to access fields of structs and
8/// enums. This will result in a compile error if a field is added, which makes
9/// it more likely the appropriate visit call will be added for it.
10pub trait Visitor<'thir, 'tcx: 'thir>: Sized {
11    fn thir(&self) -> &'thir Thir<'tcx>;
12
13    fn visit_expr(&mut self, expr: &'thir Expr<'tcx>) {
14        walk_expr(self, expr);
15    }
16
17    fn visit_stmt(&mut self, stmt: &'thir Stmt<'tcx>) {
18        walk_stmt(self, stmt);
19    }
20
21    fn visit_block(&mut self, block: &'thir Block) {
22        walk_block(self, block);
23    }
24
25    fn visit_arm(&mut self, arm: &'thir Arm<'tcx>) {
26        walk_arm(self, arm);
27    }
28
29    fn visit_pat(&mut self, pat: &'thir Pat<'tcx>) {
30        walk_pat(self, pat);
31    }
32
33    // Note: We don't have visitors for `ty::Const` and `mir::Const`
34    // (even though these types occur in THIR) for consistency and to reduce confusion,
35    // since the lazy creation of constants during thir construction causes most
36    // 'constants' to not be of type `ty::Const` or `mir::Const` at that
37    // stage (they are mostly still identified by `DefId` or `hir::Lit`, see
38    // the variants `Literal`, `NonHirLiteral` and `NamedConst` in `thir::ExprKind`).
39    // You have to manually visit `ty::Const` and `mir::Const` through the
40    // other `visit*` functions.
41}
42
43pub fn walk_expr<'thir, 'tcx: 'thir, V: Visitor<'thir, 'tcx>>(
44    visitor: &mut V,
45    expr: &'thir Expr<'tcx>,
46) {
47    use ExprKind::*;
48    let Expr { kind, ty: _, temp_lifetime: _, span: _ } = expr;
49    match *kind {
50        Scope { value, region_scope: _, lint_level: _ } => {
51            visitor.visit_expr(&visitor.thir()[value])
52        }
53        Box { value } => visitor.visit_expr(&visitor.thir()[value]),
54        If { cond, then, else_opt, if_then_scope: _ } => {
55            visitor.visit_expr(&visitor.thir()[cond]);
56            visitor.visit_expr(&visitor.thir()[then]);
57            if let Some(else_expr) = else_opt {
58                visitor.visit_expr(&visitor.thir()[else_expr]);
59            }
60        }
61        Call { fun, ref args, ty: _, from_hir_call: _, fn_span: _ } => {
62            visitor.visit_expr(&visitor.thir()[fun]);
63            for &arg in &**args {
64                visitor.visit_expr(&visitor.thir()[arg]);
65            }
66        }
67        ByUse { expr, span: _ } => {
68            visitor.visit_expr(&visitor.thir()[expr]);
69        }
70        Deref { arg } => visitor.visit_expr(&visitor.thir()[arg]),
71        Binary { lhs, rhs, op: _ } | LogicalOp { lhs, rhs, op: _ } => {
72            visitor.visit_expr(&visitor.thir()[lhs]);
73            visitor.visit_expr(&visitor.thir()[rhs]);
74        }
75        Unary { arg, op: _ } => visitor.visit_expr(&visitor.thir()[arg]),
76        Cast { source } => visitor.visit_expr(&visitor.thir()[source]),
77        Use { source } => visitor.visit_expr(&visitor.thir()[source]),
78        NeverToAny { source } => visitor.visit_expr(&visitor.thir()[source]),
79        PointerCoercion { source, cast: _, is_from_as_cast: _ } => {
80            visitor.visit_expr(&visitor.thir()[source])
81        }
82        Let { expr, ref pat } => {
83            visitor.visit_expr(&visitor.thir()[expr]);
84            visitor.visit_pat(pat);
85        }
86        Loop { body } => visitor.visit_expr(&visitor.thir()[body]),
87        LoopMatch { match_data: box LoopMatchMatchData { scrutinee, ref arms, .. }, .. }
88        | Match { scrutinee, ref arms, .. } => {
89            visitor.visit_expr(&visitor.thir()[scrutinee]);
90            for &arm in &**arms {
91                visitor.visit_arm(&visitor.thir()[arm]);
92            }
93        }
94        Block { block } => visitor.visit_block(&visitor.thir()[block]),
95        Assign { lhs, rhs } | AssignOp { lhs, rhs, op: _ } => {
96            visitor.visit_expr(&visitor.thir()[lhs]);
97            visitor.visit_expr(&visitor.thir()[rhs]);
98        }
99        Field { lhs, variant_index: _, name: _ } => visitor.visit_expr(&visitor.thir()[lhs]),
100        Index { lhs, index } => {
101            visitor.visit_expr(&visitor.thir()[lhs]);
102            visitor.visit_expr(&visitor.thir()[index]);
103        }
104        VarRef { id: _ } | UpvarRef { closure_def_id: _, var_hir_id: _ } => {}
105        Borrow { arg, borrow_kind: _ } => visitor.visit_expr(&visitor.thir()[arg]),
106        RawBorrow { arg, mutability: _ } => visitor.visit_expr(&visitor.thir()[arg]),
107        Break { value, label: _ } => {
108            if let Some(value) = value {
109                visitor.visit_expr(&visitor.thir()[value])
110            }
111        }
112        Continue { label: _ } => {}
113        ConstContinue { value, label: _ } => visitor.visit_expr(&visitor.thir()[value]),
114        Return { value } => {
115            if let Some(value) = value {
116                visitor.visit_expr(&visitor.thir()[value])
117            }
118        }
119        Become { value } => visitor.visit_expr(&visitor.thir()[value]),
120        ConstBlock { did: _, args: _ } => {}
121        Repeat { value, count: _ } => {
122            visitor.visit_expr(&visitor.thir()[value]);
123        }
124        Array { ref fields } | Tuple { ref fields } => {
125            for &field in &**fields {
126                visitor.visit_expr(&visitor.thir()[field]);
127            }
128        }
129        Adt(box AdtExpr {
130            ref fields,
131            ref base,
132            adt_def: _,
133            variant_index: _,
134            args: _,
135            user_ty: _,
136        }) => {
137            for field in &**fields {
138                visitor.visit_expr(&visitor.thir()[field.expr]);
139            }
140            if let AdtExprBase::Base(base) = base {
141                visitor.visit_expr(&visitor.thir()[base.base]);
142            }
143        }
144        PlaceTypeAscription { source, user_ty: _, user_ty_span: _ }
145        | ValueTypeAscription { source, user_ty: _, user_ty_span: _ } => {
146            visitor.visit_expr(&visitor.thir()[source])
147        }
148        PlaceUnwrapUnsafeBinder { source }
149        | ValueUnwrapUnsafeBinder { source }
150        | WrapUnsafeBinder { source } => visitor.visit_expr(&visitor.thir()[source]),
151        Closure(box ClosureExpr {
152            closure_id: _,
153            args: _,
154            upvars: _,
155            movability: _,
156            fake_reads: _,
157        }) => {}
158        Literal { lit: _, neg: _ } => {}
159        NonHirLiteral { lit: _, user_ty: _ } => {}
160        ZstLiteral { user_ty: _ } => {}
161        NamedConst { def_id: _, args: _, user_ty: _ } => {}
162        ConstParam { param: _, def_id: _ } => {}
163        StaticRef { alloc_id: _, ty: _, def_id: _ } => {}
164        InlineAsm(box InlineAsmExpr {
165            asm_macro: _,
166            ref operands,
167            template: _,
168            options: _,
169            line_spans: _,
170        }) => {
171            for op in &**operands {
172                use InlineAsmOperand::*;
173                match op {
174                    In { expr, reg: _ }
175                    | Out { expr: Some(expr), reg: _, late: _ }
176                    | InOut { expr, reg: _, late: _ } => visitor.visit_expr(&visitor.thir()[*expr]),
177                    SplitInOut { in_expr, out_expr, reg: _, late: _ } => {
178                        visitor.visit_expr(&visitor.thir()[*in_expr]);
179                        if let Some(out_expr) = out_expr {
180                            visitor.visit_expr(&visitor.thir()[*out_expr]);
181                        }
182                    }
183                    Out { expr: None, reg: _, late: _ }
184                    | Const { value: _, span: _ }
185                    | SymFn { value: _ }
186                    | SymStatic { def_id: _ } => {}
187                    Label { block } => visitor.visit_block(&visitor.thir()[*block]),
188                }
189            }
190        }
191        OffsetOf { container: _, fields: _ } => {}
192        ThreadLocalRef(_) => {}
193        Yield { value } => visitor.visit_expr(&visitor.thir()[value]),
194    }
195}
196
197pub fn walk_stmt<'thir, 'tcx: 'thir, V: Visitor<'thir, 'tcx>>(
198    visitor: &mut V,
199    stmt: &'thir Stmt<'tcx>,
200) {
201    let Stmt { kind } = stmt;
202    match kind {
203        StmtKind::Expr { expr, scope: _ } => visitor.visit_expr(&visitor.thir()[*expr]),
204        StmtKind::Let {
205            initializer,
206            remainder_scope: _,
207            init_scope: _,
208            pattern,
209            lint_level: _,
210            else_block,
211            span: _,
212        } => {
213            if let Some(init) = initializer {
214                visitor.visit_expr(&visitor.thir()[*init]);
215            }
216            visitor.visit_pat(pattern);
217            if let Some(block) = else_block {
218                visitor.visit_block(&visitor.thir()[*block])
219            }
220        }
221    }
222}
223
224pub fn walk_block<'thir, 'tcx: 'thir, V: Visitor<'thir, 'tcx>>(
225    visitor: &mut V,
226    block: &'thir Block,
227) {
228    let Block { stmts, expr, targeted_by_break: _, region_scope: _, span: _, safety_mode: _ } =
229        block;
230    for &stmt in &*stmts {
231        visitor.visit_stmt(&visitor.thir()[stmt]);
232    }
233    if let Some(expr) = expr {
234        visitor.visit_expr(&visitor.thir()[*expr]);
235    }
236}
237
238pub fn walk_arm<'thir, 'tcx: 'thir, V: Visitor<'thir, 'tcx>>(
239    visitor: &mut V,
240    arm: &'thir Arm<'tcx>,
241) {
242    let Arm { guard, pattern, body, lint_level: _, span: _, scope: _ } = arm;
243    if let Some(expr) = guard {
244        visitor.visit_expr(&visitor.thir()[*expr])
245    }
246    visitor.visit_pat(pattern);
247    visitor.visit_expr(&visitor.thir()[*body]);
248}
249
250pub fn walk_pat<'thir, 'tcx: 'thir, V: Visitor<'thir, 'tcx>>(
251    visitor: &mut V,
252    pat: &'thir Pat<'tcx>,
253) {
254    for_each_immediate_subpat(pat, |p| visitor.visit_pat(p));
255}
256
257/// Invokes `callback` on each immediate subpattern of `pat`, if any.
258/// A building block for assembling THIR pattern visitors.
259pub(crate) fn for_each_immediate_subpat<'a, 'tcx>(
260    pat: &'a Pat<'tcx>,
261    mut callback: impl FnMut(&'a Pat<'tcx>),
262) {
263    let Pat { kind, ty: _, span: _ } = pat;
264    match kind {
265        PatKind::Missing
266        | PatKind::Wild
267        | PatKind::Binding { subpattern: None, .. }
268        | PatKind::Constant { value: _ }
269        | PatKind::Range(_)
270        | PatKind::Never
271        | PatKind::Error(_) => {}
272
273        PatKind::AscribeUserType { subpattern, .. }
274        | PatKind::Binding { subpattern: Some(subpattern), .. }
275        | PatKind::Deref { subpattern }
276        | PatKind::DerefPattern { subpattern, .. }
277        | PatKind::ExpandedConstant { subpattern, .. } => callback(subpattern),
278
279        PatKind::Variant { subpatterns, .. } | PatKind::Leaf { subpatterns } => {
280            for field_pat in subpatterns {
281                callback(&field_pat.pattern);
282            }
283        }
284
285        PatKind::Slice { prefix, slice, suffix } | PatKind::Array { prefix, slice, suffix } => {
286            for pat in prefix.iter().chain(slice.as_deref()).chain(suffix) {
287                callback(pat);
288            }
289        }
290
291        PatKind::Or { pats } => {
292            for pat in pats {
293                callback(pat);
294            }
295        }
296    }
297}