Skip to main content

syn_canon/
blocks.rs

1//! Borrowed discovery and independent normalization of lexical contexts.
2
3use alloc::collections::BTreeMap;
4use alloc::string::{String, ToString};
5use alloc::vec;
6use alloc::vec::Vec;
7
8use proc_macro2::{Ident, Span, TokenTree};
9use quote::ToTokens;
10use syn::visit::Visit;
11use syn::visit_mut::VisitMut;
12
13use crate::alpha::{
14    Renamer, is_unit_path, item_binding, resolve_target_path, single_segment_type_name,
15};
16use crate::form::CanonicalForm;
17use crate::scope::{
18    Binding, BindingId, Domain, FirstSeg, Frame, Inherited, Ns, Origin, Target, TargetSeg,
19};
20
21/// The kind of lexical scope a context candidate covers.
22#[derive(Clone, Copy, Debug, PartialEq, Eq)]
23pub enum ContextKind {
24    /// A function, method, or trait default body.
25    FunctionBody,
26    /// An explicit block, a loop body, or an `if` branch.
27    Block,
28    /// A complete match arm, pattern and guard included.
29    Arm,
30    /// A complete closure, inputs and body included.
31    Closure,
32}
33
34/// The metadata of one emitted context candidate.
35pub struct Context<'a> {
36    /// The kind of lexical scope covered.
37    pub kind: ContextKind,
38    /// The candidate span from the supplied syntax tree, outer delimiters included.
39    pub span: Span,
40    /// The item containing the candidate, relative to the caller's module prefix.
41    pub item: &'a str,
42}
43
44/// The measured work of a `contexts` walk.
45#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
46pub struct ContextWork {
47    /// The candidates emitted.
48    pub contexts: usize,
49    /// The raw leaf tokens the independent normalizations consumed, nested content repeated per containing candidate.
50    pub input_tokens: usize,
51}
52
53/// Emit each original lexical context once, normalized against its visible declarations.
54///
55/// ```
56/// let forms = |source: &str| {
57///     let mut forms = Vec::new();
58///     syn_canon::contexts(&syn::parse_str(source).unwrap(), &mut |_, form| forms.push(form));
59///     forms
60/// };
61/// assert_eq!(forms("fn f(x: u32) -> u32 { x + x }"), forms("fn g(y: u32) -> u32 { y + y }"));
62/// ```
63pub fn contexts(file: &syn::File, emit: &mut dyn FnMut(Context<'_>, CanonicalForm)) -> ContextWork {
64    let mut walker = Walker {
65        frames: vec![Frame::default()],
66        mod_frames: BTreeMap::new(),
67        mod_origins: BTreeMap::new(),
68        targets: Vec::new(),
69        item_path: Vec::new(),
70        inline_depth: 0,
71        id: 0,
72        work: ContextWork::default(),
73        emit,
74    };
75    walker.visit_file(file);
76    walker.work
77}
78
79/// The original delimiter-inclusive span and raw leaf token count.
80fn source_span_and_leaf_tokens(node: &impl ToTokens) -> (Span, usize) {
81    let mut first = None;
82    let mut last = None;
83    let mut count = 0;
84    for tree in node.to_token_stream() {
85        if first.is_none() {
86            first = Some(tree.span());
87        }
88        last = Some(tree.span());
89        count += leaf_count(&tree);
90    }
91    (
92        match (first, last) {
93            (Some(first), Some(last)) => first.join(last).unwrap_or_else(Span::call_site),
94            _ => Span::call_site(),
95        },
96        count,
97    )
98}
99
100/// The leaf tokens of one tree, its inner groups counted.
101fn leaf_count(tree: &TokenTree) -> usize {
102    match tree {
103        TokenTree::Group(group) => group
104            .stream()
105            .into_iter()
106            .map(|inner| leaf_count(&inner))
107            .sum(),
108        _ => 1,
109    }
110}
111
112/// Push the names bound by `pat` to `out`, in traversal order, without rewriting it.
113fn pat_names(pat: &syn::Pat, out: &mut Vec<String>) {
114    match pat {
115        syn::Pat::Ident(id) => {
116            if !is_unit_path(id) {
117                out.push(id.ident.to_string());
118            }
119            if let Some((_, sub)) = &id.subpat {
120                pat_names(sub, out);
121            }
122        }
123        syn::Pat::Tuple(syn::PatTuple { elems, .. })
124        | syn::Pat::Slice(syn::PatSlice { elems, .. })
125        | syn::Pat::TupleStruct(syn::PatTupleStruct { elems, .. }) => {
126            for pat in elems {
127                pat_names(pat, out);
128            }
129        }
130        syn::Pat::Struct(r#struct) => {
131            for field in &r#struct.fields {
132                pat_names(&field.pat, out);
133            }
134        }
135        syn::Pat::Or(r#or) => {
136            for pat in &r#or.cases {
137                pat_names(pat, out);
138            }
139        }
140        syn::Pat::Reference(syn::PatReference { pat, .. })
141        | syn::Pat::Type(syn::PatType { pat, .. })
142        | syn::Pat::Paren(syn::PatParen { pat, .. })
143        | syn::Pat::Guard(syn::PatGuard { pat, .. }) => pat_names(pat, out),
144        _ => {}
145    }
146}
147
148/// The names bound by the function parameters of `inputs`.
149fn fn_input_names<'a>(inputs: impl Iterator<Item = &'a syn::FnArg>) -> Vec<String> {
150    let mut out = Vec::new();
151    for input in inputs {
152        if let syn::FnArg::Typed(pat_type) = input {
153            pat_names(&pat_type.pat, &mut out);
154        }
155    }
156    out
157}
158
159/// Prebind items and module frames so references may precede declarations.
160fn prebind_items<'a>(
161    frames: &mut Vec<Frame>,
162    mod_frames: &mut BTreeMap<BindingId, Frame>,
163    mod_origins: &mut BTreeMap<*const syn::ItemMod, BindingId>,
164    id: &mut usize,
165    items: impl Iterator<Item = &'a syn::Item>,
166) {
167    for item in items {
168        let (ns, ident, standard, origin) = if let syn::Item::ExternCrate(item) = item {
169            (
170                Ns::Type,
171                item.rename.as_ref().map_or(&item.ident, |(_, alias)| alias),
172                crate::macros::MacroResolution::external_crate(item),
173                Origin::Use,
174            )
175        } else if let Some((ns, ident)) = item_binding(item) {
176            (ns, ident, None, Origin::Item)
177        } else {
178            continue;
179        };
180        let binding_id = BindingId(*id);
181        *id += 1;
182        let name = ident.to_string();
183        frames.last_mut().expect("a scope frame").bind(
184            ns,
185            &name,
186            Binding {
187                id: binding_id,
188                canon: String::new(),
189                origin,
190                prim: None,
191                port: 0,
192                value_port: None,
193                target: None,
194                raw: false,
195                helper: None,
196                macro_resolution: standard,
197            },
198        );
199        if let syn::Item::Mod(
200            module @ syn::ItemMod {
201                content: Some((_, sub_items)),
202                ..
203            },
204        ) = item
205        {
206            mod_origins.insert(core::ptr::from_ref(module), binding_id);
207            frames.push(Frame::default());
208            prebind_items(frames, mod_frames, mod_origins, id, sub_items.iter());
209            let frame = frames.pop().expect("a scope frame");
210            mod_frames.insert(binding_id, frame);
211        }
212    }
213}
214
215/// The written self type of an impl, as the item path's owner.
216fn self_type_string(self_ty: &syn::Type) -> String {
217    self_ty.to_token_stream().to_string()
218}
219
220/// The borrowed walk that records candidates and the lexical environment around them.
221struct Walker<'w> {
222    /// The scope stack, the file frame first.
223    frames: Vec<Frame>,
224    /// The item frames of local modules, by module binding id.
225    mod_frames: BTreeMap<BindingId, Frame>,
226    /// The binding id of each inline module declaration by address, apart for `cfg`-exclusive
227    /// declarations sharing one name.
228    mod_origins: BTreeMap<*const syn::ItemMod, BindingId>,
229    /// The interned alias targets.
230    targets: Vec<Target>,
231    /// The item path segments, outermost first.
232    item_path: Vec<String>,
233    /// The inline modules around the walk, whose frames stay on the stack.
234    inline_depth: usize,
235    /// The next binding id.
236    id: usize,
237    /// The measured work.
238    work: ContextWork,
239    /// The candidate emitter.
240    emit: &'w mut dyn FnMut(Context<'_>, CanonicalForm),
241}
242
243impl Walker<'_> {
244    /// Bind `name` in the current frame and return its binding id.
245    fn bind(&mut self, ns: Ns, name: &str, origin: Origin) -> BindingId {
246        let binding_id = BindingId(self.id);
247        self.id += 1;
248        self.top_bind(
249            ns,
250            name,
251            Binding {
252                id: binding_id,
253                canon: String::new(),
254                origin,
255                prim: None,
256                port: 0,
257                value_port: None,
258                target: None,
259                raw: false,
260                helper: None,
261                macro_resolution: None,
262            },
263        );
264        binding_id
265    }
266
267    /// Bind `name` to `binding` in the current frame.
268    fn top_bind(&mut self, ns: Ns, name: &str, binding: Binding) {
269        self.frames
270            .last_mut()
271            .expect("a scope frame")
272            .bind(ns, name, binding);
273    }
274
275    /// Bind `name` in the value and type namespaces to one binding id, a `use` alias form.
276    fn bind_value_and_type(
277        &mut self,
278        name: &str,
279        target: Option<usize>,
280        origin: Origin,
281        macro_resolution: Option<crate::macros::MacroResolution>,
282    ) -> BindingId {
283        let binding_id = BindingId(self.id);
284        self.id += 1;
285        let binding = Binding {
286            id: binding_id,
287            canon: String::new(),
288            origin,
289            prim: None,
290            port: 0,
291            value_port: None,
292            target,
293            raw: false,
294            helper: None,
295            macro_resolution,
296        };
297        self.top_bind(Ns::Value, name, binding.clone());
298        if origin == Origin::Use {
299            self.top_bind(Ns::Macro, name, binding.clone());
300        }
301        self.top_bind(Ns::Type, name, binding);
302        binding_id
303    }
304
305    /// Nearest-binding lookup, namespace priority first.
306    fn lookup(&self, ns: &[Ns], name: &str) -> Option<&Binding> {
307        for ns in ns {
308            for frame in self.frames.iter().rev() {
309                if let Some(binding) = frame.lookup(*ns, name) {
310                    return Some(binding);
311                }
312            }
313        }
314        None
315    }
316
317    /// Reuse a prebound item's binding id when its declaration is visited.
318    fn bind_item_name(&mut self, ns: Ns, ident: &Ident) -> BindingId {
319        let name = ident.to_string();
320        match self.frames.last().and_then(|frame| frame.lookup(ns, &name)) {
321            Some(binding) => binding.id,
322            None => self.bind(ns, &name, Origin::Item),
323        }
324    }
325
326    /// Bind one scope-level `use` item, block-wide.
327    fn bind_use_item(&mut self, item: &syn::ItemUse) {
328        self.bind_use_tree(
329            item.leading_colon.is_some(),
330            &[],
331            &item.tree,
332            crate::context::blocking_attr(&item.attrs),
333        );
334    }
335
336    /// Bind the block-wide `use` leaves of a scope.
337    fn bind_scope_uses<'a>(&mut self, uses: impl Iterator<Item = &'a syn::ItemUse>) {
338        for use_item in uses {
339            self.bind_use_item(use_item);
340        }
341    }
342
343    /// Bind the leaf aliases of one `use` tree, resolving each written target.
344    fn bind_use_tree(
345        &mut self,
346        rooted: bool,
347        prefix: &[String],
348        tree: &syn::UseTree,
349        opaque: bool,
350    ) {
351        match tree {
352            syn::UseTree::Path(path) => {
353                let mut prefix = prefix.to_vec();
354                prefix.push(path.ident.to_string());
355                self.bind_use_tree(rooted, &prefix, &path.tree, opaque);
356            }
357            syn::UseTree::Name(name) if name.ident == "self" => {
358                // `a::{self}` imports the module itself under its own name.
359                if let Some(alias) = prefix.last() {
360                    let segments: Vec<&str> = prefix.iter().map(String::as_str).collect();
361                    self.frames.last_mut().expect("a scope frame").std_rebound |=
362                        crate::drift::import_rebound(&segments, alias, alias);
363                    let (rooted, segments) =
364                        resolve_target_path(&|name| self.resolve_first_seg(name), rooted, prefix);
365                    self.bind_alias_target(rooted, segments, alias, opaque);
366                }
367            }
368            syn::UseTree::Name(name) => {
369                self.bind_alias_leaf(rooted, prefix, &name.ident, &name.ident, opaque);
370            }
371            syn::UseTree::Rename(rename) => {
372                self.bind_alias_leaf(rooted, prefix, &rename.ident, &rename.rename, opaque);
373            }
374            // A glob outside `std` and the inline modules in view can bring in a macro under
375            // any std name.
376            syn::UseTree::Glob(_) => {
377                self.frames.last_mut().expect("a scope frame").glob = true;
378                let segments: Vec<&str> = prefix.iter().map(String::as_str).collect();
379                if crate::drift::macro_glob(&segments, self.inline_depth) {
380                    self.frames.last_mut().expect("a scope frame").std_rebound =
381                        crate::drift::ALL_STD_NAMES;
382                }
383            }
384            syn::UseTree::Group(group) => {
385                for tree in &group.items {
386                    self.bind_use_tree(rooted, prefix, tree, opaque);
387                }
388            }
389        }
390    }
391
392    /// Bind one written target under `alias`.
393    fn bind_alias_target(
394        &mut self,
395        rooted: bool,
396        segments: Vec<TargetSeg>,
397        alias: &str,
398        opaque: bool,
399    ) {
400        let target = Target { rooted, segments };
401        let opaque = opaque
402            || self
403                .frames
404                .last()
405                .and_then(|frame| frame.lookup(Ns::Macro, alias))
406                .is_some()
407            || self.lookup(&[Ns::Macro], alias).is_some_and(|binding| {
408                binding.origin == Origin::MacroRule
409                    || binding.macro_resolution == Some(crate::macros::MacroResolution::Opaque)
410            });
411        let resolution = if opaque {
412            Some(crate::macros::MacroResolution::Opaque)
413        } else {
414            crate::macros::MacroResolution::external_target(&target)
415        };
416        let index = self.targets.len();
417        self.targets.push(target);
418        self.bind_value_and_type(alias, Some(index), Origin::Use, resolution);
419    }
420
421    /// Bind one `use` leaf that imports `ident` under `alias`.
422    fn bind_alias_leaf(
423        &mut self,
424        rooted: bool,
425        prefix: &[String],
426        ident: &Ident,
427        alias: &Ident,
428        opaque: bool,
429    ) {
430        let segments: Vec<&str> = prefix.iter().map(String::as_str).collect();
431        self.frames.last_mut().expect("a scope frame").std_rebound |= crate::drift::import_rebound(
432            &segments,
433            &crate::scope::ident_name(ident),
434            &crate::scope::ident_name(alias),
435        );
436        let mut path = prefix.to_vec();
437        path.push(ident.to_string());
438        let (rooted, segments) =
439            resolve_target_path(&|name| self.resolve_first_seg(name), rooted, &path);
440        self.bind_alias_target(rooted, segments, &alias.to_string(), opaque);
441    }
442
443    /// Resolve the first-segment question of a written `use` prefix.
444    fn resolve_first_seg(&self, name: &str) -> FirstSeg<'_> {
445        match self.lookup(&[Ns::Type], name) {
446            Some(binding) if binding.target.is_some() => {
447                let index = binding.target.expect("a target index");
448                FirstSeg::Alias(self.targets.get(index).expect("a target index"))
449            }
450            Some(binding) => FirstSeg::Module(Domain::Inherited, binding.id),
451            None => FirstSeg::Free,
452        }
453    }
454
455    /// Prebind generic names without repeating the caller's syntax walk.
456    fn begin_generics(&mut self, generics: &syn::Generics) {
457        self.frames.push(Frame::default());
458        for param in &generics.params {
459            match param {
460                syn::GenericParam::Lifetime(lifetime) => {
461                    self.bind(
462                        Ns::Lifetime,
463                        &lifetime.lifetime.ident.to_string(),
464                        Origin::Lifetime,
465                    );
466                }
467                syn::GenericParam::Type(ty_param) => {
468                    self.bind(Ns::Type, &ty_param.ident.to_string(), Origin::Generic);
469                }
470                syn::GenericParam::Const(r#const) => {
471                    // A bare reference in `Foo<N>` reads as a type argument.
472                    self.bind_value_and_type(
473                        &r#const.ident.to_string(),
474                        None,
475                        Origin::Generic,
476                        None,
477                    );
478                }
479            }
480        }
481    }
482
483    /// Walk a function's signature and body, emitting the body candidate.
484    fn visit_fn(&mut self, sig: &syn::Signature, body: &syn::Block) {
485        self.begin_generics(&sig.generics);
486        self.frames.push(Frame::default());
487        for name in fn_input_names(sig.inputs.iter()) {
488            self.bind(Ns::Value, &name, Origin::Parameter);
489        }
490        syn::visit::visit_signature(self, sig);
491        let (span, input_tokens) = source_span_and_leaf_tokens(body);
492        let inherited = Inherited {
493            frames: &self.frames,
494            mod_frames: &self.mod_frames,
495            targets: &self.targets,
496        };
497        let form = build_function_body_form(inherited, body, sig.asyncness.is_none());
498        self.emit_candidate(ContextKind::FunctionBody, span, input_tokens, form);
499        self.visit_block(body);
500        self.frames.pop();
501        self.frames.pop();
502    }
503
504    /// Walk a condition, binding each `let` after its operand, `&&`-only.
505    fn walk_let_cond(&mut self, cond: &syn::Expr) {
506        match cond {
507            syn::Expr::Binary(binary) => {
508                self.walk_let_cond(&binary.left);
509                self.walk_let_cond(&binary.right);
510            }
511            syn::Expr::Let(let_expr) => {
512                syn::visit::visit_expr(self, &let_expr.expr);
513                let mut names = Vec::new();
514                pat_names(&let_expr.pat, &mut names);
515                for name in &names {
516                    self.bind(Ns::Value, name, Origin::Let);
517                }
518            }
519            other => syn::visit::visit_expr(self, other),
520        }
521    }
522
523    /// Emit one block-slot candidate, a loop body or an `if` branch.
524    fn emit_block_slot(&mut self, block: &syn::Block) {
525        let (span, input_tokens) = source_span_and_leaf_tokens(block);
526        let inherited = Inherited {
527            frames: &self.frames,
528            mod_frames: &self.mod_frames,
529            targets: &self.targets,
530        };
531        let form = build_block_slot_form(inherited, block);
532        self.emit_candidate(ContextKind::Block, span, input_tokens, form);
533    }
534
535    /// Emit one block-expression candidate, the label and block included.
536    fn emit_block_expr(&mut self, expr: syn::Expr) {
537        let (span, input_tokens) = source_span_and_leaf_tokens(&expr);
538        let inherited = Inherited {
539            frames: &self.frames,
540            mod_frames: &self.mod_frames,
541            targets: &self.targets,
542        };
543        let form = build_block_expr_form(inherited, expr);
544        self.emit_candidate(ContextKind::Block, span, input_tokens, form);
545    }
546
547    /// Emit one candidate with its measured input work.
548    fn emit_candidate(
549        &mut self,
550        kind: ContextKind,
551        span: Span,
552        input_tokens: usize,
553        form: CanonicalForm,
554    ) {
555        let item = self.item_path.join("::");
556        (self.emit)(
557            Context {
558                kind,
559                span,
560                item: item.as_str(),
561            },
562            form,
563        );
564        self.work.contexts += 1;
565        self.work.input_tokens += input_tokens;
566    }
567
568    /// Push one item path segment.
569    fn push_item_path(&mut self, ident: &Ident) {
570        self.item_path.push(ident.to_string());
571    }
572
573    /// Push one written item path segment.
574    fn push_item_path_str(&mut self, name: String) {
575        self.item_path.push(name);
576    }
577
578    /// Pop one item path segment.
579    fn pop_item_path(&mut self) {
580        self.item_path.pop();
581    }
582
583    fn observe(&mut self, attrs: &[syn::Attribute]) -> bool {
584        let frame = self.frames.last_mut().expect("a scope frame");
585        let previous = frame.observed;
586        frame.observed |= crate::context::live_attr(attrs);
587        previous
588    }
589
590    fn restore_observation(&mut self, observed: bool) {
591        self.frames.last_mut().expect("a scope frame").observed = observed;
592    }
593}
594
595impl Visit<'_> for Walker<'_> {
596    fn visit_file(&mut self, file: &syn::File) {
597        self.observe(&file.attrs);
598        prebind_items(
599            &mut self.frames,
600            &mut self.mod_frames,
601            &mut self.mod_origins,
602            &mut self.id,
603            file.items.iter(),
604        );
605        // A `macro_rules!` of a std name or a `#[macro_use] extern crate` can reach any call.
606        self.frames.last_mut().expect("a scope frame").std_rebound |=
607            crate::drift::file_rebound(file);
608        self.bind_scope_uses(file.items.iter().filter_map(|item| match item {
609            syn::Item::Use(u) => Some(u),
610            _ => None,
611        }));
612        syn::visit::visit_file(self, file);
613    }
614
615    fn visit_item(&mut self, item: &syn::Item) {
616        let observed = self.observe(crate::helpers::item_attrs(item));
617        syn::visit::visit_item(self, item);
618        self.restore_observation(observed);
619    }
620
621    fn visit_expr(&mut self, expr: &syn::Expr) {
622        let observed = self.observe(crate::drift::expr_attrs(expr));
623        syn::visit::visit_expr(self, expr);
624        self.restore_observation(observed);
625    }
626
627    fn visit_block(&mut self, block: &syn::Block) {
628        self.frames.push(Frame::default());
629        prebind_items(
630            &mut self.frames,
631            &mut self.mod_frames,
632            &mut self.mod_origins,
633            &mut self.id,
634            block.stmts.iter().filter_map(|stmt| match stmt {
635                syn::Stmt::Item(item) => Some(item),
636                _ => None,
637            }),
638        );
639        self.bind_scope_uses(block.stmts.iter().filter_map(|stmt| match stmt {
640            syn::Stmt::Item(syn::Item::Use(u)) => Some(u),
641            _ => None,
642        }));
643        syn::visit::visit_block(self, block);
644        self.frames.pop();
645    }
646
647    fn visit_local(&mut self, local: &syn::Local) {
648        // The initializer resolves under the outer bindings.
649        if let Some(init) = &local.init {
650            syn::visit::visit_expr(self, &init.expr);
651            if let Some(diverge) = &init.diverge {
652                syn::visit::visit_expr(self, &diverge.1);
653            }
654        }
655        let mut names = Vec::new();
656        pat_names(&local.pat, &mut names);
657        for name in &names {
658            self.bind(Ns::Value, name, Origin::Let);
659        }
660        syn::visit::visit_pat(self, &local.pat);
661    }
662
663    // Imports are bound by their enclosing scope.
664    fn visit_item_use(&mut self, _: &syn::ItemUse) {}
665
666    fn visit_item_fn(&mut self, item: &syn::ItemFn) {
667        self.push_item_path(&item.sig.ident);
668        self.visit_fn(&item.sig, &item.block);
669        self.pop_item_path();
670    }
671
672    fn visit_item_mod(&mut self, item: &syn::ItemMod) {
673        self.push_item_path(&item.ident);
674        if let Some((_, items)) = &item.content {
675            let id = *self
676                .mod_origins
677                .get(&core::ptr::from_ref(item))
678                .expect("a module is prebound");
679            let frame = self.mod_frames.remove(&id).unwrap_or_default();
680            self.frames.push(frame);
681            self.inline_depth += 1;
682            self.bind_scope_uses(items.iter().filter_map(|item| match item {
683                syn::Item::Use(u) => Some(u),
684                _ => None,
685            }));
686            for item in items {
687                self.visit_item(item);
688            }
689            self.inline_depth -= 1;
690            let frame = self.frames.pop().expect("a scope frame");
691            self.mod_frames.insert(id, frame);
692        }
693        self.pop_item_path();
694    }
695
696    fn visit_item_impl(&mut self, item: &syn::ItemImpl) {
697        self.frames.push(Frame::default());
698        if let Some(name) = single_segment_type_name(&item.self_ty)
699            && let Some(binding) = self.lookup(&[Ns::Type], &name)
700        {
701            self.top_bind(Ns::Type, "Self", binding.clone());
702        }
703        self.push_item_path_str(self_type_string(&item.self_ty));
704        let has_generics = item.generics.lt_token.is_some();
705        if has_generics {
706            self.begin_generics(&item.generics);
707        }
708        syn::visit::visit_item_impl(self, item);
709        if has_generics {
710            self.frames.pop();
711        }
712        self.frames.pop();
713        self.pop_item_path();
714    }
715
716    fn visit_item_trait(&mut self, item: &syn::ItemTrait) {
717        self.push_item_path(&item.ident);
718        let id = self.bind_item_name(Ns::Type, &item.ident);
719        self.begin_generics(&item.generics);
720        self.top_bind(
721            Ns::Type,
722            "Self",
723            Binding {
724                id,
725                canon: String::new(),
726                origin: Origin::SelfTy,
727                prim: None,
728                port: 0,
729                value_port: None,
730                target: None,
731                raw: false,
732                helper: None,
733                macro_resolution: None,
734            },
735        );
736        syn::visit::visit_item_trait(self, item);
737        self.frames.pop();
738        self.pop_item_path();
739    }
740
741    fn visit_item_struct(&mut self, item: &syn::ItemStruct) {
742        self.push_item_path(&item.ident);
743        self.bind_item_name(Ns::Type, &item.ident);
744        self.begin_generics(&item.generics);
745        syn::visit::visit_item_struct(self, item);
746        self.frames.pop();
747        self.pop_item_path();
748    }
749
750    fn visit_item_enum(&mut self, item: &syn::ItemEnum) {
751        self.push_item_path(&item.ident);
752        self.bind_item_name(Ns::Type, &item.ident);
753        self.begin_generics(&item.generics);
754        syn::visit::visit_item_enum(self, item);
755        self.frames.pop();
756        self.pop_item_path();
757    }
758
759    fn visit_item_union(&mut self, item: &syn::ItemUnion) {
760        self.push_item_path(&item.ident);
761        self.bind_item_name(Ns::Type, &item.ident);
762        self.begin_generics(&item.generics);
763        syn::visit::visit_item_union(self, item);
764        self.frames.pop();
765        self.pop_item_path();
766    }
767
768    fn visit_item_type(&mut self, item: &syn::ItemType) {
769        self.push_item_path(&item.ident);
770        self.bind_item_name(Ns::Type, &item.ident);
771        self.begin_generics(&item.generics);
772        syn::visit::visit_item_type(self, item);
773        self.frames.pop();
774        self.pop_item_path();
775    }
776
777    fn visit_item_trait_alias(&mut self, item: &syn::ItemTraitAlias) {
778        self.push_item_path(&item.ident);
779        self.bind_item_name(Ns::Type, &item.ident);
780        self.begin_generics(&item.generics);
781        syn::visit::visit_item_trait_alias(self, item);
782        self.frames.pop();
783        self.pop_item_path();
784    }
785
786    fn visit_item_const(&mut self, item: &syn::ItemConst) {
787        self.push_item_path(&item.ident);
788        self.bind_item_name(Ns::Value, &item.ident);
789        syn::visit::visit_item_const(self, item);
790        self.pop_item_path();
791    }
792
793    fn visit_item_static(&mut self, item: &syn::ItemStatic) {
794        self.push_item_path(&item.ident);
795        self.bind_item_name(Ns::Value, &item.ident);
796        syn::visit::visit_item_static(self, item);
797        self.pop_item_path();
798    }
799
800    fn visit_item_macro(&mut self, item: &syn::ItemMacro) {
801        // `macro_rules!` binds from its declaration onward.
802        if let Some(ident) = &item.ident {
803            let name = ident.to_string();
804            if self
805                .frames
806                .last()
807                .and_then(|frame| frame.lookup(Ns::Macro, &name))
808                .is_none_or(|binding| binding.origin != Origin::MacroRule)
809            {
810                self.bind(Ns::Macro, &name, Origin::MacroRule);
811            }
812        }
813        syn::visit::visit_item_macro(self, item);
814    }
815
816    fn visit_foreign_item_fn(&mut self, item: &syn::ForeignItemFn) {
817        self.push_item_path(&item.sig.ident);
818        self.begin_generics(&item.sig.generics);
819        syn::visit::visit_foreign_item_fn(self, item);
820        self.frames.pop();
821        self.pop_item_path();
822    }
823
824    fn visit_impl_item(&mut self, item: &syn::ImplItem) {
825        let attrs: &[syn::Attribute] = match item {
826            syn::ImplItem::Fn(item) => &item.attrs,
827            syn::ImplItem::Const(item) => &item.attrs,
828            syn::ImplItem::Type(item) => &item.attrs,
829            syn::ImplItem::Macro(item) => &item.attrs,
830            _ => &[],
831        };
832        let observed = self.observe(attrs);
833        match item {
834            syn::ImplItem::Fn(f) => {
835                self.push_item_path(&f.sig.ident);
836                self.visit_fn(&f.sig, &f.block);
837                self.pop_item_path();
838            }
839            syn::ImplItem::Const(c) => {
840                self.push_item_path(&c.ident);
841                syn::visit::visit_impl_item_const(self, c);
842                self.pop_item_path();
843            }
844            syn::ImplItem::Type(t) => {
845                self.push_item_path(&t.ident);
846                syn::visit::visit_impl_item_type(self, t);
847                self.pop_item_path();
848            }
849            syn::ImplItem::Macro(m) => {
850                syn::visit::visit_impl_item_macro(self, m);
851            }
852            syn::ImplItem::Verbatim(_) | _ => {}
853        }
854        self.restore_observation(observed);
855    }
856
857    fn visit_trait_item(&mut self, item: &syn::TraitItem) {
858        let attrs: &[syn::Attribute] = match item {
859            syn::TraitItem::Fn(item) => &item.attrs,
860            syn::TraitItem::Const(item) => &item.attrs,
861            syn::TraitItem::Type(item) => &item.attrs,
862            syn::TraitItem::Macro(item) => &item.attrs,
863            _ => &[],
864        };
865        let observed = self.observe(attrs);
866        match item {
867            syn::TraitItem::Fn(f) => {
868                self.push_item_path(&f.sig.ident);
869                if let Some(body) = &f.default {
870                    self.visit_fn(&f.sig, body);
871                } else {
872                    self.begin_generics(&f.sig.generics);
873                    syn::visit::visit_signature(self, &f.sig);
874                    self.frames.pop();
875                }
876                self.pop_item_path();
877            }
878            syn::TraitItem::Const(c) => {
879                self.push_item_path(&c.ident);
880                syn::visit::visit_trait_item_const(self, c);
881                self.pop_item_path();
882            }
883            syn::TraitItem::Type(t) => {
884                self.push_item_path(&t.ident);
885                syn::visit::visit_trait_item_type(self, t);
886                self.pop_item_path();
887            }
888            syn::TraitItem::Macro(m) => {
889                syn::visit::visit_trait_item_macro(self, m);
890            }
891            syn::TraitItem::Verbatim(_) | _ => {}
892        }
893        self.restore_observation(observed);
894    }
895
896    fn visit_expr_closure(&mut self, closure: &syn::ExprClosure) {
897        let (span, input_tokens) = source_span_and_leaf_tokens(closure);
898        let inherited = Inherited {
899            frames: &self.frames,
900            mod_frames: &self.mod_frames,
901            targets: &self.targets,
902        };
903        let form = build_closure_form(inherited, closure);
904        self.emit_candidate(ContextKind::Closure, span, input_tokens, form);
905        self.frames.push(Frame::default());
906        if let Some(bound) = &closure.lifetimes {
907            syn::visit::visit_bound_lifetimes(self, bound);
908        }
909        let mut names = Vec::new();
910        for input in &closure.inputs {
911            pat_names(input, &mut names);
912        }
913        for name in &names {
914            self.bind(Ns::Value, name, Origin::Closure);
915        }
916        for input in &closure.inputs {
917            syn::visit::visit_pat(self, input);
918        }
919        syn::visit::visit_return_type(self, &closure.output);
920        syn::visit::visit_expr(self, &closure.body);
921        self.frames.pop();
922    }
923
924    fn visit_expr_block(&mut self, expr: &syn::ExprBlock) {
925        self.emit_block_expr(syn::Expr::Block(expr.clone()));
926        if let Some(label) = &expr.label {
927            self.bind(Ns::Label, &label.name.ident.to_string(), Origin::Label);
928        }
929        self.visit_block(&expr.block);
930    }
931
932    fn visit_expr_unsafe(&mut self, expr: &syn::ExprUnsafe) {
933        self.emit_block_expr(syn::Expr::Unsafe(expr.clone()));
934        syn::visit::visit_expr_unsafe(self, expr);
935    }
936
937    fn visit_expr_async(&mut self, expr: &syn::ExprAsync) {
938        self.emit_block_expr(syn::Expr::Async(expr.clone()));
939        syn::visit::visit_expr_async(self, expr);
940    }
941
942    fn visit_expr_try_block(&mut self, expr: &syn::ExprTryBlock) {
943        self.emit_block_expr(syn::Expr::TryBlock(expr.clone()));
944        syn::visit::visit_expr_try_block(self, expr);
945    }
946
947    fn visit_expr_const(&mut self, expr: &syn::ExprConst) {
948        self.emit_block_expr(syn::Expr::Const(expr.clone()));
949        syn::visit::visit_expr_const(self, expr);
950    }
951
952    fn visit_expr_if(&mut self, expr: &syn::ExprIf) {
953        // Condition bindings are visible in the then branch only.
954        self.frames.push(Frame::default());
955        self.walk_let_cond(&expr.cond);
956        self.emit_block_slot(&expr.then_branch);
957        self.visit_block(&expr.then_branch);
958        self.frames.pop();
959        if let Some((_, otherwise)) = &expr.else_branch {
960            syn::visit::visit_expr(self, otherwise);
961        }
962    }
963
964    fn visit_expr_for_loop(&mut self, for_loop: &syn::ExprForLoop) {
965        // The iterable resolves under the outer bindings.
966        syn::visit::visit_expr(self, &for_loop.expr);
967        self.frames.push(Frame::default());
968        if let Some(label) = &for_loop.label {
969            self.bind(Ns::Label, &label.name.ident.to_string(), Origin::Label);
970        }
971        let mut names = Vec::new();
972        pat_names(&for_loop.pat, &mut names);
973        for name in &names {
974            self.bind(Ns::Value, name, Origin::Let);
975        }
976        self.emit_block_slot(&for_loop.body);
977        self.visit_block(&for_loop.body);
978        self.frames.pop();
979    }
980
981    fn visit_expr_while(&mut self, expr: &syn::ExprWhile) {
982        self.frames.push(Frame::default());
983        if let Some(label) = &expr.label {
984            self.bind(Ns::Label, &label.name.ident.to_string(), Origin::Label);
985        }
986        self.walk_let_cond(&expr.cond);
987        self.emit_block_slot(&expr.body);
988        self.visit_block(&expr.body);
989        self.frames.pop();
990    }
991
992    fn visit_expr_loop(&mut self, expr: &syn::ExprLoop) {
993        self.frames.push(Frame::default());
994        if let Some(label) = &expr.label {
995            self.bind(Ns::Label, &label.name.ident.to_string(), Origin::Label);
996        }
997        self.emit_block_slot(&expr.body);
998        self.visit_block(&expr.body);
999        self.frames.pop();
1000    }
1001
1002    fn visit_arm(&mut self, arm: &syn::Arm) {
1003        let (span, input_tokens) = source_span_and_leaf_tokens(arm);
1004        let inherited = Inherited {
1005            frames: &self.frames,
1006            mod_frames: &self.mod_frames,
1007            targets: &self.targets,
1008        };
1009        let form = build_arm_form(inherited, arm);
1010        self.emit_candidate(ContextKind::Arm, span, input_tokens, form);
1011        self.frames.push(Frame::default());
1012        let mut names = Vec::new();
1013        pat_names(&arm.pat, &mut names);
1014        for name in &names {
1015            self.bind(Ns::Value, name, Origin::Let);
1016        }
1017        syn::visit::visit_arm(self, arm);
1018        self.frames.pop();
1019    }
1020
1021    fn visit_pat_guard(&mut self, node: &syn::PatGuard) {
1022        syn::visit::visit_pat(self, &node.pat);
1023        self.walk_let_cond(&node.guard);
1024    }
1025
1026    fn visit_bound_lifetimes(&mut self, node: &syn::BoundLifetimes) {
1027        for param in &node.lifetimes {
1028            if let syn::GenericParam::Lifetime(lifetime) = param {
1029                self.bind(
1030                    Ns::Lifetime,
1031                    &lifetime.lifetime.ident.to_string(),
1032                    Origin::Lifetime,
1033                );
1034            }
1035        }
1036        syn::visit::visit_bound_lifetimes(self, node);
1037    }
1038
1039    fn visit_trait_bound(&mut self, node: &syn::TraitBound) {
1040        // Same owner scoping for `for<'a>` on `dyn` and `impl` bounds.
1041        self.frames.push(Frame::default());
1042        syn::visit::visit_trait_bound(self, node);
1043        self.frames.pop();
1044    }
1045
1046    fn visit_type_fn_ptr(&mut self, node: &syn::TypeFnPtr) {
1047        // Higher-ranked lifetimes cannot capture outer loop labels.
1048        self.frames.push(Frame::default());
1049        syn::visit::visit_type_fn_ptr(self, node);
1050        self.frames.pop();
1051    }
1052}
1053
1054/// Normalize one cloned function body against the inherited environment, reading early
1055/// returns before the tail as `else` branches when `early`.
1056fn build_function_body_form(
1057    inherited: Inherited<'_>,
1058    body: &syn::Block,
1059    early: bool,
1060) -> CanonicalForm {
1061    let mut block = body.clone();
1062    let frames = inherited.frames;
1063    let mut renamer = Renamer::contextual(inherited);
1064    if !renamer.observes_tokens() {
1065        crate::drift::normalize_block(&mut block, frames);
1066    }
1067    renamer.normalize_return_body(&mut block, early);
1068    renamer.visit_block_mut(&mut block);
1069    let facts = renamer.into_reference_facts();
1070    crate::form::from_node(&block, &facts)
1071}
1072
1073/// Normalize one cloned block slot, a loop body or an `if` branch.
1074fn build_block_slot_form(inherited: Inherited<'_>, block: &syn::Block) -> CanonicalForm {
1075    let mut block = block.clone();
1076    let frames = inherited.frames;
1077    let mut renamer = Renamer::contextual(inherited);
1078    if !renamer.observes_tokens() {
1079        crate::drift::normalize_block(&mut block, frames);
1080    }
1081    renamer.visit_block_mut(&mut block);
1082    let facts = renamer.into_reference_facts();
1083    crate::form::from_node(&block, &facts)
1084}
1085
1086/// Normalize one cloned block expression, the label and block included.
1087fn build_block_expr_form(inherited: Inherited<'_>, expr: syn::Expr) -> CanonicalForm {
1088    let mut expr = expr;
1089    let frames = inherited.frames;
1090    let mut renamer = Renamer::contextual(inherited);
1091    if !renamer.observes_tokens() {
1092        crate::drift::normalize_expr(&mut expr, frames);
1093    }
1094    renamer.visit_expr_mut(&mut expr);
1095    let facts = renamer.into_reference_facts();
1096    crate::form::from_node(&expr, &facts)
1097}
1098
1099/// Normalize one cloned match arm.
1100fn build_arm_form(inherited: Inherited<'_>, arm: &syn::Arm) -> CanonicalForm {
1101    let mut arm = arm.clone();
1102    let frames = inherited.frames;
1103    let mut renamer = Renamer::contextual(inherited);
1104    if !renamer.observes_tokens() {
1105        crate::drift::normalize_arm(&mut arm, frames);
1106    }
1107    renamer.visit_arm_mut(&mut arm);
1108    let facts = renamer.into_reference_facts();
1109    crate::form::from_node(&arm, &facts)
1110}
1111
1112/// Normalize one cloned closure, inputs and body included.
1113fn build_closure_form(inherited: Inherited<'_>, closure: &syn::ExprClosure) -> CanonicalForm {
1114    let mut expr = syn::Expr::Closure(closure.clone());
1115    let frames = inherited.frames;
1116    let mut renamer = Renamer::contextual(inherited);
1117    if !renamer.observes_tokens() {
1118        crate::drift::normalize_expr(&mut expr, frames);
1119    }
1120    renamer.visit_expr_mut(&mut expr);
1121    let facts = renamer.into_reference_facts();
1122    crate::form::from_node(&expr, &facts)
1123}