use alloc::collections::BTreeMap;
use alloc::string::{String, ToString};
use alloc::vec;
use alloc::vec::Vec;
use proc_macro2::{Ident, Span, TokenTree};
use quote::ToTokens;
use syn::visit::Visit;
use syn::visit_mut::VisitMut;
use crate::alpha::{
Renamer, is_unit_path, item_binding, resolve_target_path, single_segment_type_name,
};
use crate::form::CanonicalForm;
use crate::scope::{
Binding, BindingId, Domain, FirstSeg, Frame, Inherited, Ns, Origin, Target, TargetSeg,
};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ContextKind {
FunctionBody,
Block,
Arm,
Closure,
}
pub struct Context<'a> {
pub kind: ContextKind,
pub span: Span,
pub item: &'a str,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct ContextWork {
pub contexts: usize,
pub input_tokens: usize,
}
pub fn contexts(file: &syn::File, emit: &mut dyn FnMut(Context<'_>, CanonicalForm)) -> ContextWork {
let mut walker = Walker {
frames: vec![Frame::default()],
mod_frames: BTreeMap::new(),
mod_origins: BTreeMap::new(),
targets: Vec::new(),
item_path: Vec::new(),
inline_depth: 0,
id: 0,
work: ContextWork::default(),
emit,
};
walker.visit_file(file);
walker.work
}
fn source_span_and_leaf_tokens(node: &impl ToTokens) -> (Span, usize) {
let mut first = None;
let mut last = None;
let mut count = 0;
for tree in node.to_token_stream() {
if first.is_none() {
first = Some(tree.span());
}
last = Some(tree.span());
count += leaf_count(&tree);
}
(
match (first, last) {
(Some(first), Some(last)) => first.join(last).unwrap_or_else(Span::call_site),
_ => Span::call_site(),
},
count,
)
}
fn leaf_count(tree: &TokenTree) -> usize {
match tree {
TokenTree::Group(group) => group
.stream()
.into_iter()
.map(|inner| leaf_count(&inner))
.sum(),
_ => 1,
}
}
fn pat_names(pat: &syn::Pat, out: &mut Vec<String>) {
match pat {
syn::Pat::Ident(id) => {
if !is_unit_path(id) {
out.push(id.ident.to_string());
}
if let Some((_, sub)) = &id.subpat {
pat_names(sub, out);
}
}
syn::Pat::Tuple(syn::PatTuple { elems, .. })
| syn::Pat::Slice(syn::PatSlice { elems, .. })
| syn::Pat::TupleStruct(syn::PatTupleStruct { elems, .. }) => {
for pat in elems {
pat_names(pat, out);
}
}
syn::Pat::Struct(r#struct) => {
for field in &r#struct.fields {
pat_names(&field.pat, out);
}
}
syn::Pat::Or(r#or) => {
for pat in &r#or.cases {
pat_names(pat, out);
}
}
syn::Pat::Reference(syn::PatReference { pat, .. })
| syn::Pat::Type(syn::PatType { pat, .. })
| syn::Pat::Paren(syn::PatParen { pat, .. })
| syn::Pat::Guard(syn::PatGuard { pat, .. }) => pat_names(pat, out),
_ => {}
}
}
fn fn_input_names<'a>(inputs: impl Iterator<Item = &'a syn::FnArg>) -> Vec<String> {
let mut out = Vec::new();
for input in inputs {
if let syn::FnArg::Typed(pat_type) = input {
pat_names(&pat_type.pat, &mut out);
}
}
out
}
fn prebind_items<'a>(
frames: &mut Vec<Frame>,
mod_frames: &mut BTreeMap<BindingId, Frame>,
mod_origins: &mut BTreeMap<*const syn::ItemMod, BindingId>,
id: &mut usize,
items: impl Iterator<Item = &'a syn::Item>,
) {
for item in items {
let (ns, ident, standard, origin) = if let syn::Item::ExternCrate(item) = item {
(
Ns::Type,
item.rename.as_ref().map_or(&item.ident, |(_, alias)| alias),
crate::macros::MacroResolution::external_crate(item),
Origin::Use,
)
} else if let Some((ns, ident)) = item_binding(item) {
(ns, ident, None, Origin::Item)
} else {
continue;
};
let binding_id = BindingId(*id);
*id += 1;
let name = ident.to_string();
frames.last_mut().expect("a scope frame").bind(
ns,
&name,
Binding {
id: binding_id,
canon: String::new(),
origin,
prim: None,
port: 0,
value_port: None,
target: None,
raw: false,
helper: None,
macro_resolution: standard,
},
);
if let syn::Item::Mod(
module @ syn::ItemMod {
content: Some((_, sub_items)),
..
},
) = item
{
mod_origins.insert(core::ptr::from_ref(module), binding_id);
frames.push(Frame::default());
prebind_items(frames, mod_frames, mod_origins, id, sub_items.iter());
let frame = frames.pop().expect("a scope frame");
mod_frames.insert(binding_id, frame);
}
}
}
fn self_type_string(self_ty: &syn::Type) -> String {
self_ty.to_token_stream().to_string()
}
struct Walker<'w> {
frames: Vec<Frame>,
mod_frames: BTreeMap<BindingId, Frame>,
mod_origins: BTreeMap<*const syn::ItemMod, BindingId>,
targets: Vec<Target>,
item_path: Vec<String>,
inline_depth: usize,
id: usize,
work: ContextWork,
emit: &'w mut dyn FnMut(Context<'_>, CanonicalForm),
}
impl Walker<'_> {
fn bind(&mut self, ns: Ns, name: &str, origin: Origin) -> BindingId {
let binding_id = BindingId(self.id);
self.id += 1;
self.top_bind(
ns,
name,
Binding {
id: binding_id,
canon: String::new(),
origin,
prim: None,
port: 0,
value_port: None,
target: None,
raw: false,
helper: None,
macro_resolution: None,
},
);
binding_id
}
fn top_bind(&mut self, ns: Ns, name: &str, binding: Binding) {
self.frames
.last_mut()
.expect("a scope frame")
.bind(ns, name, binding);
}
fn bind_value_and_type(
&mut self,
name: &str,
target: Option<usize>,
origin: Origin,
macro_resolution: Option<crate::macros::MacroResolution>,
) -> BindingId {
let binding_id = BindingId(self.id);
self.id += 1;
let binding = Binding {
id: binding_id,
canon: String::new(),
origin,
prim: None,
port: 0,
value_port: None,
target,
raw: false,
helper: None,
macro_resolution,
};
self.top_bind(Ns::Value, name, binding.clone());
if origin == Origin::Use {
self.top_bind(Ns::Macro, name, binding.clone());
}
self.top_bind(Ns::Type, name, binding);
binding_id
}
fn lookup(&self, ns: &[Ns], name: &str) -> Option<&Binding> {
for ns in ns {
for frame in self.frames.iter().rev() {
if let Some(binding) = frame.lookup(*ns, name) {
return Some(binding);
}
}
}
None
}
fn bind_item_name(&mut self, ns: Ns, ident: &Ident) -> BindingId {
let name = ident.to_string();
match self.frames.last().and_then(|frame| frame.lookup(ns, &name)) {
Some(binding) => binding.id,
None => self.bind(ns, &name, Origin::Item),
}
}
fn bind_use_item(&mut self, item: &syn::ItemUse) {
self.bind_use_tree(
item.leading_colon.is_some(),
&[],
&item.tree,
crate::context::blocking_attr(&item.attrs),
);
}
fn bind_scope_uses<'a>(&mut self, uses: impl Iterator<Item = &'a syn::ItemUse>) {
for use_item in uses {
self.bind_use_item(use_item);
}
}
fn bind_use_tree(
&mut self,
rooted: bool,
prefix: &[String],
tree: &syn::UseTree,
opaque: bool,
) {
match tree {
syn::UseTree::Path(path) => {
let mut prefix = prefix.to_vec();
prefix.push(path.ident.to_string());
self.bind_use_tree(rooted, &prefix, &path.tree, opaque);
}
syn::UseTree::Name(name) if name.ident == "self" => {
if let Some(alias) = prefix.last() {
let segments: Vec<&str> = prefix.iter().map(String::as_str).collect();
self.frames.last_mut().expect("a scope frame").std_rebound |=
crate::drift::import_rebound(&segments, alias, alias);
let (rooted, segments) =
resolve_target_path(&|name| self.resolve_first_seg(name), rooted, prefix);
self.bind_alias_target(rooted, segments, alias, opaque);
}
}
syn::UseTree::Name(name) => {
self.bind_alias_leaf(rooted, prefix, &name.ident, &name.ident, opaque);
}
syn::UseTree::Rename(rename) => {
self.bind_alias_leaf(rooted, prefix, &rename.ident, &rename.rename, opaque);
}
syn::UseTree::Glob(_) => {
self.frames.last_mut().expect("a scope frame").glob = true;
let segments: Vec<&str> = prefix.iter().map(String::as_str).collect();
if crate::drift::macro_glob(&segments, self.inline_depth) {
self.frames.last_mut().expect("a scope frame").std_rebound =
crate::drift::ALL_STD_NAMES;
}
}
syn::UseTree::Group(group) => {
for tree in &group.items {
self.bind_use_tree(rooted, prefix, tree, opaque);
}
}
}
}
fn bind_alias_target(
&mut self,
rooted: bool,
segments: Vec<TargetSeg>,
alias: &str,
opaque: bool,
) {
let target = Target { rooted, segments };
let opaque = opaque
|| self
.frames
.last()
.and_then(|frame| frame.lookup(Ns::Macro, alias))
.is_some()
|| self.lookup(&[Ns::Macro], alias).is_some_and(|binding| {
binding.origin == Origin::MacroRule
|| binding.macro_resolution == Some(crate::macros::MacroResolution::Opaque)
});
let resolution = if opaque {
Some(crate::macros::MacroResolution::Opaque)
} else {
crate::macros::MacroResolution::external_target(&target)
};
let index = self.targets.len();
self.targets.push(target);
self.bind_value_and_type(alias, Some(index), Origin::Use, resolution);
}
fn bind_alias_leaf(
&mut self,
rooted: bool,
prefix: &[String],
ident: &Ident,
alias: &Ident,
opaque: bool,
) {
let segments: Vec<&str> = prefix.iter().map(String::as_str).collect();
self.frames.last_mut().expect("a scope frame").std_rebound |= crate::drift::import_rebound(
&segments,
&crate::scope::ident_name(ident),
&crate::scope::ident_name(alias),
);
let mut path = prefix.to_vec();
path.push(ident.to_string());
let (rooted, segments) =
resolve_target_path(&|name| self.resolve_first_seg(name), rooted, &path);
self.bind_alias_target(rooted, segments, &alias.to_string(), opaque);
}
fn resolve_first_seg(&self, name: &str) -> FirstSeg<'_> {
match self.lookup(&[Ns::Type], name) {
Some(binding) if binding.target.is_some() => {
let index = binding.target.expect("a target index");
FirstSeg::Alias(self.targets.get(index).expect("a target index"))
}
Some(binding) => FirstSeg::Module(Domain::Inherited, binding.id),
None => FirstSeg::Free,
}
}
fn begin_generics(&mut self, generics: &syn::Generics) {
self.frames.push(Frame::default());
for param in &generics.params {
match param {
syn::GenericParam::Lifetime(lifetime) => {
self.bind(
Ns::Lifetime,
&lifetime.lifetime.ident.to_string(),
Origin::Lifetime,
);
}
syn::GenericParam::Type(ty_param) => {
self.bind(Ns::Type, &ty_param.ident.to_string(), Origin::Generic);
}
syn::GenericParam::Const(r#const) => {
self.bind_value_and_type(
&r#const.ident.to_string(),
None,
Origin::Generic,
None,
);
}
}
}
}
fn visit_fn(&mut self, sig: &syn::Signature, body: &syn::Block) {
self.begin_generics(&sig.generics);
self.frames.push(Frame::default());
for name in fn_input_names(sig.inputs.iter()) {
self.bind(Ns::Value, &name, Origin::Parameter);
}
syn::visit::visit_signature(self, sig);
let (span, input_tokens) = source_span_and_leaf_tokens(body);
let inherited = Inherited {
frames: &self.frames,
mod_frames: &self.mod_frames,
targets: &self.targets,
};
let form = build_function_body_form(inherited, body, sig.asyncness.is_none());
self.emit_candidate(ContextKind::FunctionBody, span, input_tokens, form);
self.visit_block(body);
self.frames.pop();
self.frames.pop();
}
fn walk_let_cond(&mut self, cond: &syn::Expr) {
match cond {
syn::Expr::Binary(binary) => {
self.walk_let_cond(&binary.left);
self.walk_let_cond(&binary.right);
}
syn::Expr::Let(let_expr) => {
syn::visit::visit_expr(self, &let_expr.expr);
let mut names = Vec::new();
pat_names(&let_expr.pat, &mut names);
for name in &names {
self.bind(Ns::Value, name, Origin::Let);
}
}
other => syn::visit::visit_expr(self, other),
}
}
fn emit_block_slot(&mut self, block: &syn::Block) {
let (span, input_tokens) = source_span_and_leaf_tokens(block);
let inherited = Inherited {
frames: &self.frames,
mod_frames: &self.mod_frames,
targets: &self.targets,
};
let form = build_block_slot_form(inherited, block);
self.emit_candidate(ContextKind::Block, span, input_tokens, form);
}
fn emit_block_expr(&mut self, expr: syn::Expr) {
let (span, input_tokens) = source_span_and_leaf_tokens(&expr);
let inherited = Inherited {
frames: &self.frames,
mod_frames: &self.mod_frames,
targets: &self.targets,
};
let form = build_block_expr_form(inherited, expr);
self.emit_candidate(ContextKind::Block, span, input_tokens, form);
}
fn emit_candidate(
&mut self,
kind: ContextKind,
span: Span,
input_tokens: usize,
form: CanonicalForm,
) {
let item = self.item_path.join("::");
(self.emit)(
Context {
kind,
span,
item: item.as_str(),
},
form,
);
self.work.contexts += 1;
self.work.input_tokens += input_tokens;
}
fn push_item_path(&mut self, ident: &Ident) {
self.item_path.push(ident.to_string());
}
fn push_item_path_str(&mut self, name: String) {
self.item_path.push(name);
}
fn pop_item_path(&mut self) {
self.item_path.pop();
}
fn observe(&mut self, attrs: &[syn::Attribute]) -> bool {
let frame = self.frames.last_mut().expect("a scope frame");
let previous = frame.observed;
frame.observed |= crate::context::live_attr(attrs);
previous
}
fn restore_observation(&mut self, observed: bool) {
self.frames.last_mut().expect("a scope frame").observed = observed;
}
}
impl Visit<'_> for Walker<'_> {
fn visit_file(&mut self, file: &syn::File) {
self.observe(&file.attrs);
prebind_items(
&mut self.frames,
&mut self.mod_frames,
&mut self.mod_origins,
&mut self.id,
file.items.iter(),
);
self.frames.last_mut().expect("a scope frame").std_rebound |=
crate::drift::file_rebound(file);
self.bind_scope_uses(file.items.iter().filter_map(|item| match item {
syn::Item::Use(u) => Some(u),
_ => None,
}));
syn::visit::visit_file(self, file);
}
fn visit_item(&mut self, item: &syn::Item) {
let observed = self.observe(crate::helpers::item_attrs(item));
syn::visit::visit_item(self, item);
self.restore_observation(observed);
}
fn visit_expr(&mut self, expr: &syn::Expr) {
let observed = self.observe(crate::drift::expr_attrs(expr));
syn::visit::visit_expr(self, expr);
self.restore_observation(observed);
}
fn visit_block(&mut self, block: &syn::Block) {
self.frames.push(Frame::default());
prebind_items(
&mut self.frames,
&mut self.mod_frames,
&mut self.mod_origins,
&mut self.id,
block.stmts.iter().filter_map(|stmt| match stmt {
syn::Stmt::Item(item) => Some(item),
_ => None,
}),
);
self.bind_scope_uses(block.stmts.iter().filter_map(|stmt| match stmt {
syn::Stmt::Item(syn::Item::Use(u)) => Some(u),
_ => None,
}));
syn::visit::visit_block(self, block);
self.frames.pop();
}
fn visit_local(&mut self, local: &syn::Local) {
if let Some(init) = &local.init {
syn::visit::visit_expr(self, &init.expr);
if let Some(diverge) = &init.diverge {
syn::visit::visit_expr(self, &diverge.1);
}
}
let mut names = Vec::new();
pat_names(&local.pat, &mut names);
for name in &names {
self.bind(Ns::Value, name, Origin::Let);
}
syn::visit::visit_pat(self, &local.pat);
}
fn visit_item_use(&mut self, _: &syn::ItemUse) {}
fn visit_item_fn(&mut self, item: &syn::ItemFn) {
self.push_item_path(&item.sig.ident);
self.visit_fn(&item.sig, &item.block);
self.pop_item_path();
}
fn visit_item_mod(&mut self, item: &syn::ItemMod) {
self.push_item_path(&item.ident);
if let Some((_, items)) = &item.content {
let id = *self
.mod_origins
.get(&core::ptr::from_ref(item))
.expect("a module is prebound");
let frame = self.mod_frames.remove(&id).unwrap_or_default();
self.frames.push(frame);
self.inline_depth += 1;
self.bind_scope_uses(items.iter().filter_map(|item| match item {
syn::Item::Use(u) => Some(u),
_ => None,
}));
for item in items {
self.visit_item(item);
}
self.inline_depth -= 1;
let frame = self.frames.pop().expect("a scope frame");
self.mod_frames.insert(id, frame);
}
self.pop_item_path();
}
fn visit_item_impl(&mut self, item: &syn::ItemImpl) {
self.frames.push(Frame::default());
if let Some(name) = single_segment_type_name(&item.self_ty)
&& let Some(binding) = self.lookup(&[Ns::Type], &name)
{
self.top_bind(Ns::Type, "Self", binding.clone());
}
self.push_item_path_str(self_type_string(&item.self_ty));
let has_generics = item.generics.lt_token.is_some();
if has_generics {
self.begin_generics(&item.generics);
}
syn::visit::visit_item_impl(self, item);
if has_generics {
self.frames.pop();
}
self.frames.pop();
self.pop_item_path();
}
fn visit_item_trait(&mut self, item: &syn::ItemTrait) {
self.push_item_path(&item.ident);
let id = self.bind_item_name(Ns::Type, &item.ident);
self.begin_generics(&item.generics);
self.top_bind(
Ns::Type,
"Self",
Binding {
id,
canon: String::new(),
origin: Origin::SelfTy,
prim: None,
port: 0,
value_port: None,
target: None,
raw: false,
helper: None,
macro_resolution: None,
},
);
syn::visit::visit_item_trait(self, item);
self.frames.pop();
self.pop_item_path();
}
fn visit_item_struct(&mut self, item: &syn::ItemStruct) {
self.push_item_path(&item.ident);
self.bind_item_name(Ns::Type, &item.ident);
self.begin_generics(&item.generics);
syn::visit::visit_item_struct(self, item);
self.frames.pop();
self.pop_item_path();
}
fn visit_item_enum(&mut self, item: &syn::ItemEnum) {
self.push_item_path(&item.ident);
self.bind_item_name(Ns::Type, &item.ident);
self.begin_generics(&item.generics);
syn::visit::visit_item_enum(self, item);
self.frames.pop();
self.pop_item_path();
}
fn visit_item_union(&mut self, item: &syn::ItemUnion) {
self.push_item_path(&item.ident);
self.bind_item_name(Ns::Type, &item.ident);
self.begin_generics(&item.generics);
syn::visit::visit_item_union(self, item);
self.frames.pop();
self.pop_item_path();
}
fn visit_item_type(&mut self, item: &syn::ItemType) {
self.push_item_path(&item.ident);
self.bind_item_name(Ns::Type, &item.ident);
self.begin_generics(&item.generics);
syn::visit::visit_item_type(self, item);
self.frames.pop();
self.pop_item_path();
}
fn visit_item_trait_alias(&mut self, item: &syn::ItemTraitAlias) {
self.push_item_path(&item.ident);
self.bind_item_name(Ns::Type, &item.ident);
self.begin_generics(&item.generics);
syn::visit::visit_item_trait_alias(self, item);
self.frames.pop();
self.pop_item_path();
}
fn visit_item_const(&mut self, item: &syn::ItemConst) {
self.push_item_path(&item.ident);
self.bind_item_name(Ns::Value, &item.ident);
syn::visit::visit_item_const(self, item);
self.pop_item_path();
}
fn visit_item_static(&mut self, item: &syn::ItemStatic) {
self.push_item_path(&item.ident);
self.bind_item_name(Ns::Value, &item.ident);
syn::visit::visit_item_static(self, item);
self.pop_item_path();
}
fn visit_item_macro(&mut self, item: &syn::ItemMacro) {
if let Some(ident) = &item.ident {
let name = ident.to_string();
if self
.frames
.last()
.and_then(|frame| frame.lookup(Ns::Macro, &name))
.is_none_or(|binding| binding.origin != Origin::MacroRule)
{
self.bind(Ns::Macro, &name, Origin::MacroRule);
}
}
syn::visit::visit_item_macro(self, item);
}
fn visit_foreign_item_fn(&mut self, item: &syn::ForeignItemFn) {
self.push_item_path(&item.sig.ident);
self.begin_generics(&item.sig.generics);
syn::visit::visit_foreign_item_fn(self, item);
self.frames.pop();
self.pop_item_path();
}
fn visit_impl_item(&mut self, item: &syn::ImplItem) {
let attrs: &[syn::Attribute] = match item {
syn::ImplItem::Fn(item) => &item.attrs,
syn::ImplItem::Const(item) => &item.attrs,
syn::ImplItem::Type(item) => &item.attrs,
syn::ImplItem::Macro(item) => &item.attrs,
_ => &[],
};
let observed = self.observe(attrs);
match item {
syn::ImplItem::Fn(f) => {
self.push_item_path(&f.sig.ident);
self.visit_fn(&f.sig, &f.block);
self.pop_item_path();
}
syn::ImplItem::Const(c) => {
self.push_item_path(&c.ident);
syn::visit::visit_impl_item_const(self, c);
self.pop_item_path();
}
syn::ImplItem::Type(t) => {
self.push_item_path(&t.ident);
syn::visit::visit_impl_item_type(self, t);
self.pop_item_path();
}
syn::ImplItem::Macro(m) => {
syn::visit::visit_impl_item_macro(self, m);
}
syn::ImplItem::Verbatim(_) | _ => {}
}
self.restore_observation(observed);
}
fn visit_trait_item(&mut self, item: &syn::TraitItem) {
let attrs: &[syn::Attribute] = match item {
syn::TraitItem::Fn(item) => &item.attrs,
syn::TraitItem::Const(item) => &item.attrs,
syn::TraitItem::Type(item) => &item.attrs,
syn::TraitItem::Macro(item) => &item.attrs,
_ => &[],
};
let observed = self.observe(attrs);
match item {
syn::TraitItem::Fn(f) => {
self.push_item_path(&f.sig.ident);
if let Some(body) = &f.default {
self.visit_fn(&f.sig, body);
} else {
self.begin_generics(&f.sig.generics);
syn::visit::visit_signature(self, &f.sig);
self.frames.pop();
}
self.pop_item_path();
}
syn::TraitItem::Const(c) => {
self.push_item_path(&c.ident);
syn::visit::visit_trait_item_const(self, c);
self.pop_item_path();
}
syn::TraitItem::Type(t) => {
self.push_item_path(&t.ident);
syn::visit::visit_trait_item_type(self, t);
self.pop_item_path();
}
syn::TraitItem::Macro(m) => {
syn::visit::visit_trait_item_macro(self, m);
}
syn::TraitItem::Verbatim(_) | _ => {}
}
self.restore_observation(observed);
}
fn visit_expr_closure(&mut self, closure: &syn::ExprClosure) {
let (span, input_tokens) = source_span_and_leaf_tokens(closure);
let inherited = Inherited {
frames: &self.frames,
mod_frames: &self.mod_frames,
targets: &self.targets,
};
let form = build_closure_form(inherited, closure);
self.emit_candidate(ContextKind::Closure, span, input_tokens, form);
self.frames.push(Frame::default());
if let Some(bound) = &closure.lifetimes {
syn::visit::visit_bound_lifetimes(self, bound);
}
let mut names = Vec::new();
for input in &closure.inputs {
pat_names(input, &mut names);
}
for name in &names {
self.bind(Ns::Value, name, Origin::Closure);
}
for input in &closure.inputs {
syn::visit::visit_pat(self, input);
}
syn::visit::visit_return_type(self, &closure.output);
syn::visit::visit_expr(self, &closure.body);
self.frames.pop();
}
fn visit_expr_block(&mut self, expr: &syn::ExprBlock) {
self.emit_block_expr(syn::Expr::Block(expr.clone()));
if let Some(label) = &expr.label {
self.bind(Ns::Label, &label.name.ident.to_string(), Origin::Label);
}
self.visit_block(&expr.block);
}
fn visit_expr_unsafe(&mut self, expr: &syn::ExprUnsafe) {
self.emit_block_expr(syn::Expr::Unsafe(expr.clone()));
syn::visit::visit_expr_unsafe(self, expr);
}
fn visit_expr_async(&mut self, expr: &syn::ExprAsync) {
self.emit_block_expr(syn::Expr::Async(expr.clone()));
syn::visit::visit_expr_async(self, expr);
}
fn visit_expr_try_block(&mut self, expr: &syn::ExprTryBlock) {
self.emit_block_expr(syn::Expr::TryBlock(expr.clone()));
syn::visit::visit_expr_try_block(self, expr);
}
fn visit_expr_const(&mut self, expr: &syn::ExprConst) {
self.emit_block_expr(syn::Expr::Const(expr.clone()));
syn::visit::visit_expr_const(self, expr);
}
fn visit_expr_if(&mut self, expr: &syn::ExprIf) {
self.frames.push(Frame::default());
self.walk_let_cond(&expr.cond);
self.emit_block_slot(&expr.then_branch);
self.visit_block(&expr.then_branch);
self.frames.pop();
if let Some((_, otherwise)) = &expr.else_branch {
syn::visit::visit_expr(self, otherwise);
}
}
fn visit_expr_for_loop(&mut self, for_loop: &syn::ExprForLoop) {
syn::visit::visit_expr(self, &for_loop.expr);
self.frames.push(Frame::default());
if let Some(label) = &for_loop.label {
self.bind(Ns::Label, &label.name.ident.to_string(), Origin::Label);
}
let mut names = Vec::new();
pat_names(&for_loop.pat, &mut names);
for name in &names {
self.bind(Ns::Value, name, Origin::Let);
}
self.emit_block_slot(&for_loop.body);
self.visit_block(&for_loop.body);
self.frames.pop();
}
fn visit_expr_while(&mut self, expr: &syn::ExprWhile) {
self.frames.push(Frame::default());
if let Some(label) = &expr.label {
self.bind(Ns::Label, &label.name.ident.to_string(), Origin::Label);
}
self.walk_let_cond(&expr.cond);
self.emit_block_slot(&expr.body);
self.visit_block(&expr.body);
self.frames.pop();
}
fn visit_expr_loop(&mut self, expr: &syn::ExprLoop) {
self.frames.push(Frame::default());
if let Some(label) = &expr.label {
self.bind(Ns::Label, &label.name.ident.to_string(), Origin::Label);
}
self.emit_block_slot(&expr.body);
self.visit_block(&expr.body);
self.frames.pop();
}
fn visit_arm(&mut self, arm: &syn::Arm) {
let (span, input_tokens) = source_span_and_leaf_tokens(arm);
let inherited = Inherited {
frames: &self.frames,
mod_frames: &self.mod_frames,
targets: &self.targets,
};
let form = build_arm_form(inherited, arm);
self.emit_candidate(ContextKind::Arm, span, input_tokens, form);
self.frames.push(Frame::default());
let mut names = Vec::new();
pat_names(&arm.pat, &mut names);
for name in &names {
self.bind(Ns::Value, name, Origin::Let);
}
syn::visit::visit_arm(self, arm);
self.frames.pop();
}
fn visit_pat_guard(&mut self, node: &syn::PatGuard) {
syn::visit::visit_pat(self, &node.pat);
self.walk_let_cond(&node.guard);
}
fn visit_bound_lifetimes(&mut self, node: &syn::BoundLifetimes) {
for param in &node.lifetimes {
if let syn::GenericParam::Lifetime(lifetime) = param {
self.bind(
Ns::Lifetime,
&lifetime.lifetime.ident.to_string(),
Origin::Lifetime,
);
}
}
syn::visit::visit_bound_lifetimes(self, node);
}
fn visit_trait_bound(&mut self, node: &syn::TraitBound) {
self.frames.push(Frame::default());
syn::visit::visit_trait_bound(self, node);
self.frames.pop();
}
fn visit_type_fn_ptr(&mut self, node: &syn::TypeFnPtr) {
self.frames.push(Frame::default());
syn::visit::visit_type_fn_ptr(self, node);
self.frames.pop();
}
}
fn build_function_body_form(
inherited: Inherited<'_>,
body: &syn::Block,
early: bool,
) -> CanonicalForm {
let mut block = body.clone();
let frames = inherited.frames;
let mut renamer = Renamer::contextual(inherited);
if !renamer.observes_tokens() {
crate::drift::normalize_block(&mut block, frames);
}
renamer.normalize_return_body(&mut block, early);
renamer.visit_block_mut(&mut block);
let facts = renamer.into_reference_facts();
crate::form::from_node(&block, &facts)
}
fn build_block_slot_form(inherited: Inherited<'_>, block: &syn::Block) -> CanonicalForm {
let mut block = block.clone();
let frames = inherited.frames;
let mut renamer = Renamer::contextual(inherited);
if !renamer.observes_tokens() {
crate::drift::normalize_block(&mut block, frames);
}
renamer.visit_block_mut(&mut block);
let facts = renamer.into_reference_facts();
crate::form::from_node(&block, &facts)
}
fn build_block_expr_form(inherited: Inherited<'_>, expr: syn::Expr) -> CanonicalForm {
let mut expr = expr;
let frames = inherited.frames;
let mut renamer = Renamer::contextual(inherited);
if !renamer.observes_tokens() {
crate::drift::normalize_expr(&mut expr, frames);
}
renamer.visit_expr_mut(&mut expr);
let facts = renamer.into_reference_facts();
crate::form::from_node(&expr, &facts)
}
fn build_arm_form(inherited: Inherited<'_>, arm: &syn::Arm) -> CanonicalForm {
let mut arm = arm.clone();
let frames = inherited.frames;
let mut renamer = Renamer::contextual(inherited);
if !renamer.observes_tokens() {
crate::drift::normalize_arm(&mut arm, frames);
}
renamer.visit_arm_mut(&mut arm);
let facts = renamer.into_reference_facts();
crate::form::from_node(&arm, &facts)
}
fn build_closure_form(inherited: Inherited<'_>, closure: &syn::ExprClosure) -> CanonicalForm {
let mut expr = syn::Expr::Closure(closure.clone());
let frames = inherited.frames;
let mut renamer = Renamer::contextual(inherited);
if !renamer.observes_tokens() {
crate::drift::normalize_expr(&mut expr, frames);
}
renamer.visit_expr_mut(&mut expr);
let facts = renamer.into_reference_facts();
crate::form::from_node(&expr, &facts)
}