1use alloc::boxed::Box;
4use alloc::collections::btree_map::Entry;
5use alloc::collections::{BTreeMap, BTreeSet};
6use alloc::string::String;
7use alloc::string::ToString;
8use alloc::vec;
9use alloc::vec::Vec;
10
11use core::cell::Cell;
12
13use quote::ToTokens;
14use syn::visit::Visit;
15
16use crate::alpha;
17use crate::form::{self, OWNER_PARAMETER};
18use crate::scope::{Binding, BindingId, Frame, Ns, Origin, PrimTy, ident_name, unraw};
19
20#[derive(Clone, PartialEq, Eq, Debug)]
22pub enum Owner {
23 Free,
25 Inherent(String),
27 Trait(String),
29}
30
31impl Owner {
32 fn self_name(&self, seed: &Frame) -> Option<String> {
34 let text = match self {
35 Self::Free => return None,
36 Self::Inherent(text) | Self::Trait(text) => text,
37 };
38 if let Some(binding) = seed.lookup(Ns::Type, unraw(text)) {
39 return Some(binding.canon.clone());
40 }
41 Some(mangle_owner(text))
42 }
43}
44
45fn mangle_owner(text: &str) -> String {
47 const HEX: [char; 16] = [
48 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f',
49 ];
50 let mut out = String::from("__dejadoc_self");
51 for byte in text.bytes() {
52 out.push(HEX[usize::from(byte >> 4)]);
53 out.push(HEX[usize::from(byte & 0x0f)]);
54 }
55 out
56}
57
58#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
60struct ModuleId(usize);
61
62#[expect(
64 clippy::struct_excessive_bools,
65 reason = "primitive, derive, observer, helper and prelude facts of a module are independent"
66)]
67struct Module {
68 seed: Frame,
69 prim_fallback: bool,
70 std_names: bool,
72 observed: bool,
73 helper_blocked: bool,
74 prelude: bool,
76 standard_imports: bool,
78}
79
80#[expect(
82 clippy::struct_excessive_bools,
83 reason = "primitive, observer and helper facts of a declaration are independent"
84)]
85struct Decl<'a> {
86 module: ModuleId,
87 attrs: &'a [syn::Attribute],
89 owner: Owner,
90 prim_fallback: bool,
91 observed: bool,
92 enclosing_observed: bool,
94 helper_module: Vec<String>,
95 helper_blocked: bool,
96 generics: Option<*const syn::Generics>,
98}
99
100pub struct SourceContext<'a> {
102 modules: Vec<Module>,
103 decls: BTreeMap<(*const syn::Signature, *const syn::Block), Decl<'a>>,
104 gen_frames: BTreeMap<*const syn::Generics, Frame>,
106 helpers: crate::helpers::HelperTable,
108 owned_methods: bool,
110 clone_method: bool,
112 collection_method: bool,
114}
115
116pub struct FunctionView<'a> {
118 ctx: &'a SourceContext<'a>,
119 entry: &'a Decl<'a>,
120 sig: &'a syn::Signature,
121 block: &'a syn::Block,
122}
123
124impl FunctionView<'_> {
125 #[must_use]
127 pub fn canonicalize(&self) -> crate::CanonicalForm {
128 let seed = self.ctx.seed(self.entry.module);
129 let generics = self.entry.generics.map(|key| self.ctx.gen_frame(key));
130 let self_owner = self.entry.owner.self_name(seed);
131 let mut sig = self.sig.clone();
132 if !matches!(self.entry.owner, Owner::Free) {
133 sig.ident = syn::Ident::new("__dejadoc_method", sig.ident.span());
134 }
135 let mut item = syn::ItemFn {
136 attrs: self
137 .entry
138 .attrs
139 .iter()
140 .filter(|attribute| live(&attribute.meta))
141 .cloned()
142 .collect(),
143 vis: syn::Visibility::Inherited,
144 modifiers: syn::FnModifiers::default(),
145 sig,
146 block: Box::new(self.block.clone()),
147 };
148 let helpers = transfer_helpers(&self.ctx.helpers, self.sig, self.block, &mut item);
149 let file = syn::File {
150 shebang: None,
151 frontmatter: None,
152 attrs: Vec::new(),
153 items: vec![syn::Item::Fn(item)],
154 };
155 form::canonical_file_with_options(
156 file,
157 true,
158 alpha::Options {
159 seed: Some(seed),
160 generics,
161 prim_fallback: self.entry.prim_fallback,
162 observed: self.entry.observed,
163 enclosing_observed: self.entry.enclosing_observed,
164 std_names: self.ctx.modules[self.entry.module.0].std_names,
165 prelude: self.ctx.modules[self.entry.module.0].prelude,
166 owned_methods: self.ctx.owned_methods,
167 clone_free: self.ctx.modules[self.entry.module.0].prelude
168 && self.ctx.modules[self.entry.module.0].standard_imports
169 && self.ctx.clone_method,
170 collection_free: self.ctx.modules[self.entry.module.0].prelude
171 && self.ctx.modules[self.entry.module.0].standard_imports
172 && self.ctx.collection_method,
173 self_owner,
174 helper_table: Some(&self.ctx.helpers),
175 helper_module: &self.entry.helper_module,
176 helper_blocked: self.entry.helper_blocked,
177 helper_syntax: Some(helpers),
178 },
179 )
180 }
181}
182
183fn transfer_helpers(
184 helpers: &crate::helpers::HelperTable,
185 signature: &syn::Signature,
186 body: &syn::Block,
187 cloned: &mut syn::ItemFn,
188) -> crate::helpers::HelperSyntax {
189 crate::helpers::HelperSyntax::cloned_function(helpers, signature, body, cloned)
190}
191
192impl<'a> SourceContext<'a> {
193 fn seed(&self, module: ModuleId) -> &Frame {
195 &self.modules[module.0].seed
196 }
197
198 fn gen_frame(&self, key: *const syn::Generics) -> &Frame {
200 &self.gen_frames[&key]
201 }
202
203 pub fn new<I>(files: I) -> Self
206 where
207 I: IntoIterator<Item = (&'a [String], &'a syn::File)>,
208 {
209 let files: Vec<(&'a [String], &'a syn::File)> = files.into_iter().collect();
210 let mut scopes: ModuleMap<'a> = BTreeMap::new();
211 for (path, file) in &files {
212 declare_module(&mut scopes, (*path).to_vec(), ModuleScope::File(file));
213 }
214 for (path, file) in &files {
215 register_inline(&mut scopes, path, &file.items);
216 }
217 let ancestry = module_ancestry(&scopes);
218 let crate_rebound = files
221 .iter()
222 .any(|(_, file)| crate::drift::file_rebound(file) != 0);
223 let blocked: BTreeSet<Vec<String>> = ancestry
224 .iter()
225 .filter(|(_, flags)| flags.blocked)
226 .map(|(path, _)| path.clone())
227 .collect();
228 let mut module_ids: ModuleIds = BTreeMap::new();
229 let mut modules: Vec<Module> = Vec::new();
230 for ((path, declarations), ancestry) in scopes.iter().zip(ancestry.values()) {
231 let ids = declarations
232 .iter()
233 .map(|scope| {
234 let prim_fallback = scope_primitives_known(scope.items());
235 let id = ModuleId(modules.len());
236 modules.push(Module {
237 seed: seed_frame(&scopes, path, &scope, prim_fallback),
238 prim_fallback,
239 std_names: !crate_rebound && std_names_kept(&scopes, path, &scope),
240 observed: ancestry.observed,
241 helper_blocked: ancestry.blocked,
242 prelude: !ancestry.bare,
243 standard_imports: standard_imports(scope.items()),
244 });
245 (scope.key(), id)
246 })
247 .collect();
248 module_ids.insert(path.clone(), ids);
249 }
250 let helpers = crate::helpers::module_table(&crate::helpers::Scopes::new(scopes, blocked));
251 for module in &mut modules {
252 for binding in module.seed.bindings_mut(Ns::Value).values_mut() {
253 binding.helper = helpers.by_canon(&binding.canon);
254 }
255 }
256 let mut decls: BTreeMap<(*const syn::Signature, *const syn::Block), Decl<'a>> =
257 BTreeMap::new();
258 let mut gen_frames: BTreeMap<*const syn::Generics, Frame> = BTreeMap::new();
259 for (path, file) in &files {
260 index_scope(
261 &mut decls,
262 &module_ids,
263 &modules,
264 path,
265 ModuleScope::File(file),
266 false,
267 false,
268 false,
269 &mut gen_frames,
270 );
271 }
272 let owned_methods = !files
273 .iter()
274 .any(|(_, file)| declares_trait_method(&file.items, &["to_string", "to_owned"]));
275 let clone_method = !files
276 .iter()
277 .any(|(_, file)| declares_trait_method(&file.items, &["clone"]));
278 let collection_method = !files
279 .iter()
280 .any(|(_, file)| declares_trait_method(&file.items, &alpha::COLLECTION_METHODS));
281 Self {
282 modules,
283 decls,
284 gen_frames,
285 helpers,
286 owned_methods,
287 clone_method,
288 collection_method,
289 }
290 }
291
292 #[must_use]
295 pub fn function<'s>(
296 &'s self,
297 sig: &'s syn::Signature,
298 block: &'s syn::Block,
299 ) -> Option<FunctionView<'s>> {
300 let key = (
301 core::ptr::from_ref::<syn::Signature>(sig),
302 core::ptr::from_ref::<syn::Block>(block),
303 );
304 let entry = self.decls.get(&key)?;
305 Some(FunctionView {
306 ctx: self,
307 entry,
308 sig,
309 block,
310 })
311 }
312}
313
314fn glob_free(items: &[syn::Item]) -> bool {
316 !items
317 .iter()
318 .any(|item| matches!(item, syn::Item::Use(use_item) if has_glob(&use_item.tree)))
319}
320
321pub(crate) fn scope_primitives_known(items: &[syn::Item]) -> bool {
322 let mut uncertainty = ScopeUncertainty(false);
323 for item in items {
324 uncertainty.visit_item(item);
325 if uncertainty.0 {
326 return false;
327 }
328 }
329 glob_free(items)
330}
331
332struct ScopeUncertainty(bool);
333
334impl<'ast> Visit<'ast> for ScopeUncertainty {
335 fn visit_attribute(&mut self, attr: &'ast syn::Attribute) {
336 self.0 |=
337 live(&attr.meta) || attr.path().is_ident("cfg") || attr.path().is_ident("cfg_attr");
338 }
339
340 fn visit_item_mod(&mut self, item: &'ast syn::ItemMod) {
341 for attr in &item.attrs {
342 self.visit_attribute(attr);
343 }
344 }
345
346 fn visit_item_macro(&mut self, item: &'ast syn::ItemMacro) {
347 self.0 |= item.ident.is_none();
348 syn::visit::visit_item_macro(self, item);
349 }
350
351 fn visit_item_extern_crate(&mut self, _: &'ast syn::ItemExternCrate) {
352 self.0 = true;
353 }
354
355 fn visit_block(&mut self, _: &'ast syn::Block) {}
356}
357
358pub(crate) fn has_glob(tree: &syn::UseTree) -> bool {
359 match tree {
360 syn::UseTree::Glob(_) => true,
361 syn::UseTree::Path(path) => has_glob(&path.tree),
362 syn::UseTree::Group(group) => group.items.iter().any(has_glob),
363 _ => false,
364 }
365}
366
367#[derive(Clone, Copy)]
369pub(crate) enum ModuleScope<'a> {
370 File(&'a syn::File),
371 Inline(&'a syn::ItemMod),
372}
373
374impl<'a> ModuleScope<'a> {
375 pub(crate) fn items(&self) -> &'a [syn::Item] {
376 match self {
377 Self::File(file) => &file.items,
378 Self::Inline(module) => module.content.as_ref().map_or(&[], |(_, items)| items),
379 }
380 }
381
382 fn key(&self) -> *const () {
384 match self {
385 Self::File(file) => core::ptr::from_ref(*file).cast(),
386 Self::Inline(module) => core::ptr::from_ref(*module).cast(),
387 }
388 }
389
390 fn shadows_primitive_crate(&self, name: &str) -> bool {
391 let raw = if name == "core" { "r#core" } else { "r#std" };
392 let matches = |ident: &syn::Ident| ident == name || ident == raw;
393 self.items().iter().any(|item| {
394 alpha::type_item_ident(item).is_some_and(matches)
395 || matches!(item, syn::Item::Use(import)
396 if alpha::imported_name_matches(&import.tree, matches))
397 })
398 }
399}
400
401#[derive(Clone)]
403pub(crate) struct Declarations<'a> {
404 first: ModuleScope<'a>,
405 rest: Vec<ModuleScope<'a>>,
406}
407
408impl<'a> Declarations<'a> {
409 pub(crate) fn iter(&self) -> impl Iterator<Item = ModuleScope<'a>> + '_ {
410 core::iter::once(self.first).chain(self.rest.iter().copied())
411 }
412
413 pub(crate) fn repeated(&self) -> bool {
414 !self.rest.is_empty()
415 }
416
417 fn primitives_known(&self) -> bool {
418 self.iter()
419 .all(|scope| scope_primitives_known(scope.items()))
420 }
421
422 fn shadows_primitive_crate(&self, name: &str) -> bool {
423 self.iter().any(|scope| scope.shadows_primitive_crate(name))
424 }
425}
426
427pub(crate) type ModuleMap<'a> = BTreeMap<Vec<String>, Declarations<'a>>;
429
430fn std_names_kept(scopes: &ModuleMap<'_>, path: &[String], scope: &ModuleScope<'_>) -> bool {
433 let (kept, supers) = crate::drift::module_std_names(scope.items());
434 kept && (1..=supers).all(|levels| {
435 let Some(end) = path.len().checked_sub(levels) else {
436 return false;
437 };
438 scopes.get(&path[..end]).is_some_and(|declarations| {
439 declarations
440 .iter()
441 .all(|ancestor| std_names_kept(scopes, &path[..end], &ancestor))
442 })
443 })
444}
445
446type ModuleIds = BTreeMap<Vec<String>, Vec<(*const (), ModuleId)>>;
448
449pub(crate) fn declare_module<'a>(
451 scopes: &mut ModuleMap<'a>,
452 path: Vec<String>,
453 scope: ModuleScope<'a>,
454) {
455 match scopes.entry(path) {
456 Entry::Vacant(entry) => {
457 entry.insert(Declarations {
458 first: scope,
459 rest: Vec::new(),
460 });
461 }
462 Entry::Occupied(mut entry) => {
463 let declarations = entry.get_mut();
464 if declarations.iter().all(|known| known.key() != scope.key()) {
465 declarations.rest.push(scope);
466 }
467 }
468 }
469}
470
471pub(crate) fn register_inline<'a>(
473 scopes: &mut ModuleMap<'a>,
474 path: &[String],
475 items: &'a [syn::Item],
476) {
477 for item in items {
478 if let syn::Item::Mod(mod_item) = item
479 && let Some((_, inner)) = &mod_item.content
480 {
481 let mut child = path.to_vec();
482 child.push(unraw(&mod_item.ident.to_string()).to_string());
483 declare_module(scopes, child.clone(), ModuleScope::Inline(mod_item));
484 register_inline(scopes, &child, inner);
485 }
486 }
487}
488
489#[derive(Clone, Copy, Default)]
491struct Ancestry {
492 observed: bool,
493 blocked: bool,
494 bare: bool,
496}
497
498impl Ancestry {
499 fn absorb(&mut self, attrs: &[syn::Attribute]) {
500 self.observed |= live_attr(attrs);
501 self.blocked |= blocking_attr(attrs);
502 self.bare |= removes_prelude(attrs);
503 }
504}
505
506fn module_ancestry(scopes: &ModuleMap<'_>) -> BTreeMap<Vec<String>, Ancestry> {
507 let mut direct: BTreeMap<Vec<String>, Ancestry> = BTreeMap::new();
508 for (path, declarations) in scopes {
509 if declarations.repeated() {
510 direct.entry(path.clone()).or_default().blocked = true;
511 }
512 for scope in declarations.iter() {
513 if let ModuleScope::File(file) = scope {
514 direct.entry(path.clone()).or_default().absorb(&file.attrs);
515 }
516 for item in scope.items() {
517 if let syn::Item::Mod(module) = item {
518 let mut child = path.clone();
519 child.push(ident_name(&module.ident));
520 direct.entry(child).or_default().absorb(&module.attrs);
521 }
522 }
523 }
524 }
525 scopes
526 .keys()
527 .map(|path| {
528 let mut flags = Ancestry::default();
529 for end in 0..=path.len() {
530 if let Some(direct) = direct.get(&path[..end]) {
531 flags.observed |= direct.observed;
532 flags.blocked |= direct.blocked;
533 flags.bare |= direct.bare;
534 }
535 }
536 (path.clone(), flags)
537 })
538 .collect()
539}
540
541pub(crate) fn blocked_modules(scopes: &ModuleMap<'_>) -> BTreeSet<Vec<String>> {
542 module_ancestry(scopes)
543 .into_iter()
544 .filter_map(|(path, flags)| flags.blocked.then_some(path))
545 .collect()
546}
547
548pub(crate) fn removes_prelude(attrs: &[syn::Attribute]) -> bool {
551 attrs.iter().any(|attr| {
552 let path = attr.path();
553 path.is_ident("no_std")
554 || path.is_ident("no_implicit_prelude")
555 || (path.is_ident("cfg_attr")
556 && attr
557 .meta
558 .to_token_stream()
559 .into_iter()
560 .any(|token| prelude_attribute_token(&token)))
561 })
562}
563
564fn prelude_attribute_token(token: &proc_macro2::TokenTree) -> bool {
565 match token {
566 proc_macro2::TokenTree::Ident(ident) => ident == "no_std" || ident == "no_implicit_prelude",
567 proc_macro2::TokenTree::Group(group) => group
568 .stream()
569 .into_iter()
570 .any(|token| prelude_attribute_token(&token)),
571 _ => false,
572 }
573}
574
575pub(crate) fn declares_trait_method(items: &[syn::Item], names: &[&str]) -> bool {
578 struct Search<'a>(&'a [&'a str], bool);
579 impl syn::visit::Visit<'_> for Search<'_> {
580 fn visit_trait_item_fn(&mut self, method: &syn::TraitItemFn) {
581 let name = ident_name(&method.sig.ident);
582 self.1 |= self.0.contains(&name.as_str());
583 }
584 }
585 let mut search = Search(names, false);
586 for item in items {
587 syn::visit::Visit::visit_item(&mut search, item);
588 }
589 search.1
590}
591
592pub(crate) fn standard_imports(items: &[syn::Item]) -> bool {
595 struct Search(bool);
596 impl syn::visit::Visit<'_> for Search {
597 fn visit_item_use(&mut self, item: &syn::ItemUse) {
598 self.0 &= standard_tree(&item.tree);
599 }
600 }
601 fn standard_tree(tree: &syn::UseTree) -> bool {
602 match tree {
603 syn::UseTree::Path(path) => {
604 matches!(ident_name(&path.ident).as_str(), "std" | "core" | "alloc")
605 }
606 syn::UseTree::Group(group) => group.items.iter().all(standard_tree),
607 _ => false,
608 }
609 }
610 let mut search = Search(true);
611 for item in items {
612 syn::visit::Visit::visit_item(&mut search, item);
613 }
614 search.0
615}
616
617pub(crate) fn qualified_name(module: &[String], name: &str) -> String {
618 let mut out = String::from("__dejadoc_inherited");
619 for part in module
620 .iter()
621 .map(|part| unraw(part))
622 .chain(core::iter::once(unraw(name)))
623 {
624 qualified_part(&mut out, part);
625 }
626 out
627}
628
629pub(crate) fn qualified_part(out: &mut String, part: &str) {
630 const HEX: &[u8; 16] = b"0123456789abcdef";
631 out.push('_');
632 for byte in part.bytes() {
633 out.push(char::from(HEX[usize::from(byte >> 4)]));
634 out.push(char::from(HEX[usize::from(byte & 15)]));
635 }
636}
637
638#[derive(Clone)]
639pub(crate) struct ImportedTarget {
640 pub(crate) canon: String,
641 pub(crate) prim: Option<PrimTy>,
642 module: Option<Vec<String>>,
643 macro_resolution: Option<crate::macros::MacroResolution>,
644}
645
646impl ImportedTarget {
647 fn agree(self, other: &Self) -> Option<Self> {
649 (self.canon == other.canon).then(|| Self {
650 prim: self.prim.filter(|prim| other.prim == Some(*prim)),
651 macro_resolution: self
652 .macro_resolution
653 .filter(|standard| other.macro_resolution == Some(*standard)),
654 module: self
655 .module
656 .filter(|module| other.module.as_ref() == Some(module)),
657 canon: self.canon,
658 })
659 }
660}
661
662pub(crate) enum UseTarget {
664 External,
666 Item(ImportedTarget, [bool; 5]),
668 Unresolved,
670}
671
672const IMPORT_NAMESPACES: [Ns; 3] = [Ns::Value, Ns::Type, Ns::Macro];
673
674#[derive(Default)]
675enum UnknownImport {
676 #[default]
677 None,
678 Unique(ImportedTarget),
679 Ambiguous,
680}
681
682#[derive(Default)]
683struct ImportedName {
684 resolved: [Option<ImportedTarget>; 5],
685 unknown: UnknownImport,
686 gated: bool,
688}
689
690impl ImportedName {
691 fn settle(&mut self, from: &[String], alias: &str) {
694 if self.gated {
695 for target in self.resolved.iter_mut().flatten() {
696 target.macro_resolution = Some(crate::macros::MacroResolution::Opaque);
697 }
698 if let UnknownImport::Unique(target) = &mut self.unknown {
699 target.macro_resolution = Some(crate::macros::MacroResolution::Opaque);
700 }
701 }
702 if !self.gated
703 || matches!(self.unknown, UnknownImport::None)
704 || self.resolved.iter().all(Option::is_none)
705 {
706 return;
707 }
708 for slot in self.resolved.iter_mut().filter(|slot| slot.is_some()) {
709 *slot = Some(ImportedTarget {
710 canon: qualified_name(from, alias),
711 prim: None,
712 module: None,
713 macro_resolution: None,
714 });
715 }
716 self.unknown = UnknownImport::Ambiguous;
717 }
718}
719
720fn cfg_gated(attrs: &[syn::Attribute]) -> bool {
722 attrs
723 .iter()
724 .any(|attr| attr.path().is_ident("cfg") || attr.path().is_ident("cfg_attr"))
725}
726
727struct ImportPath<'a> {
728 module: &'a [String],
729 name: &'a str,
730 parent: Option<&'a Self>,
731}
732
733impl ImportPath<'_> {
734 fn contains(&self, module: &[String], name: &str) -> bool {
735 let mut active = Some(self);
736 while let Some(query) = active {
737 if query.module == module && query.name == name {
738 return true;
739 }
740 active = query.parent;
741 }
742 false
743 }
744}
745
746struct UseBinder<'a> {
747 scopes: &'a ModuleMap<'a>,
748 from: &'a [String],
749 import: &'a syn::ItemUse,
750 imports: &'a mut BTreeMap<String, ImportedName>,
751 only_name: Option<&'a str>,
752 depth: usize,
753 active: Option<&'a ImportPath<'a>>,
754 steps: &'a Cell<usize>,
755}
756
757fn import_slots(
758 slots: &mut [Option<ImportedTarget>; 5],
759 namespaces: [bool; 5],
760 target: ImportedTarget,
761) {
762 let mut active = namespaces
763 .into_iter()
764 .enumerate()
765 .filter_map(|(index, present)| present.then_some(index))
766 .peekable();
767 while let Some(index) = active.next() {
768 if active.peek().is_some() {
769 slots[index] = Some(target.clone());
770 } else {
771 slots[index] = Some(target);
772 break;
773 }
774 }
775}
776
777fn merge_import_slots(
780 slots: &mut [Option<ImportedTarget>; 5],
781 namespaces: [bool; 5],
782 target: &ImportedTarget,
783 from: &[String],
784 alias: &str,
785) {
786 for (slot, present) in slots.iter_mut().zip(namespaces) {
787 if !present {
788 continue;
789 }
790 *slot = Some(match slot.take() {
791 None => target.clone(),
792 Some(current) => current.agree(target).unwrap_or_else(|| ImportedTarget {
793 canon: qualified_name(from, alias),
794 prim: None,
795 module: None,
796 macro_resolution: None,
797 }),
798 });
799 }
800}
801
802fn bind_imports(
803 frame: &mut Frame,
804 from: &[String],
805 imports: BTreeMap<String, ImportedName>,
806 next_id: &mut usize,
807) {
808 for (alias, mut imported) in imports {
809 imported.settle(from, &alias);
810 let unknown = match imported.unknown {
811 UnknownImport::None => None,
812 UnknownImport::Unique(target) => Some(target),
813 UnknownImport::Ambiguous => Some(ImportedTarget {
814 canon: qualified_name(from, &alias),
815 prim: None,
816 module: None,
817 macro_resolution: None,
818 }),
819 };
820 if let Some(target) = unknown {
821 let mut unclaimed = [false; 5];
822 for ns in IMPORT_NAMESPACES {
823 unclaimed[ns as usize] =
824 imported.resolved[ns as usize].is_none() && frame.lookup(ns, &alias).is_none();
825 }
826 import_slots(&mut imported.resolved, unclaimed, target);
827 }
828 for ns in IMPORT_NAMESPACES {
829 if let Some(target) = imported.resolved[ns as usize].take() {
830 frame.bind(
831 ns,
832 &alias,
833 Binding {
834 id: BindingId(*next_id),
835 canon: target.canon,
836 origin: Origin::Use,
837 prim: if ns == Ns::Type { target.prim } else { None },
838 target: None,
839 raw: false,
840 port: 0,
841 value_port: None,
842 helper: None,
843 macro_resolution: target.macro_resolution,
844 },
845 );
846 *next_id += 1;
847 }
848 }
849 }
850}
851
852fn use_tree_bindings(binder: &mut UseBinder<'_>, prefix: &[String], tree: &syn::UseTree) {
853 if binder.depth >= 64 {
854 return;
855 }
856 binder.depth += 1;
857 match tree {
858 syn::UseTree::Path(path) => {
859 let mut next = prefix.to_vec();
860 next.push(ident_name(&path.ident));
861 use_tree_bindings(binder, &next, &path.tree);
862 }
863 syn::UseTree::Name(name) if name.ident == "self" && !prefix.is_empty() => {
864 bind_use_name(
865 binder,
866 prefix,
867 prefix.last().expect("nonempty import prefix"),
868 );
869 }
870 syn::UseTree::Name(name) => {
871 let mut path = prefix.to_vec();
872 path.push(ident_name(&name.ident));
873 bind_use_name(binder, &path, &name.ident.to_string());
874 }
875 syn::UseTree::Rename(rename) => {
876 let mut path = prefix.to_vec();
877 if rename.ident != "self" || path.is_empty() {
878 path.push(ident_name(&rename.ident));
879 }
880 bind_use_name(binder, &path, &rename.rename.to_string());
881 }
882 syn::UseTree::Group(group) => {
883 for inner in &group.items {
884 use_tree_bindings(binder, prefix, inner);
885 }
886 }
887 syn::UseTree::Glob(_) => {}
888 }
889 binder.depth -= 1;
890}
891
892fn bind_use_name(binder: &mut UseBinder<'_>, path: &[String], alias: &str) {
893 if binder.only_name.is_some_and(|name| unraw(alias) != name) || path.is_empty() {
894 return;
895 }
896 let target = resolve_use_at(
897 binder.scopes,
898 binder.from,
899 path,
900 binder.import.leading_colon.is_some(),
901 binder.depth,
902 binder.active,
903 binder.steps,
904 );
905 let imported = if let Some(imported) = binder.imports.get_mut(alias) {
906 imported
907 } else {
908 binder.imports.entry(alias.to_string()).or_default()
909 };
910 imported.gated |= cfg_gated(&binder.import.attrs);
911 let target = match target {
912 UseTarget::Item(target, namespaces) => {
913 if imported.resolved.iter().all(Option::is_none) {
914 import_slots(&mut imported.resolved, namespaces, target);
915 } else {
916 merge_import_slots(
917 &mut imported.resolved,
918 namespaces,
919 &target,
920 binder.from,
921 alias,
922 );
923 }
924 return;
925 }
926 UseTarget::External => ImportedTarget {
927 canon: qualified_name(&path[..path.len() - 1], &path[path.len() - 1]),
928 prim: external_prim(path).filter(|_| {
929 binder
930 .scopes
931 .get(binder.from)
932 .expect("indexed importing module")
933 .primitives_known()
934 }),
935 module: None,
936 macro_resolution: crate::macros::MacroResolution::external_path(path),
937 },
938 UseTarget::Unresolved => ImportedTarget {
939 canon: qualified_name(binder.from, alias),
940 prim: None,
941 module: None,
942 macro_resolution: None,
943 },
944 };
945 imported.unknown = match imported.unknown {
946 UnknownImport::None => UnknownImport::Unique(target),
947 UnknownImport::Unique(_) | UnknownImport::Ambiguous => UnknownImport::Ambiguous,
948 };
949}
950
951pub(crate) fn resolve_use(
954 scopes: &ModuleMap<'_>,
955 from: &[String],
956 path: &[String],
957 leading_colon: bool,
958 steps: &Cell<usize>,
959) -> UseTarget {
960 resolve_use_at(scopes, from, path, leading_colon, 0, None, steps)
961}
962
963const UNION_STEPS: usize = 1024;
965
966fn resolve_use_at(
967 scopes: &ModuleMap<'_>,
968 from: &[String],
969 path: &[String],
970 leading_colon: bool,
971 depth: usize,
972 active: Option<&ImportPath<'_>>,
973 steps: &Cell<usize>,
974) -> UseTarget {
975 if depth >= 64 || path.len() > 64 {
976 return UseTarget::Unresolved;
977 }
978 let Some(first) = path.first().map(|name| unraw(name)) else {
979 return UseTarget::Unresolved;
980 };
981 if leading_colon {
982 return UseTarget::External;
983 }
984 let local = scopes.get(from).expect("indexed importing module");
985 if matches!(first, "std" | "core") && !local.shadows_primitive_crate(first) {
986 return UseTarget::External;
987 }
988 let (mut base, mut rest) = match first {
989 "crate" => (Vec::new(), &path[1..]),
990 "self" => (from.to_vec(), &path[1..]),
991 _ => (from.to_vec(), path),
992 };
993 while rest.first().is_some_and(|name| unraw(name) == "super") {
994 if base.pop().is_none() {
995 return UseTarget::Unresolved;
996 }
997 rest = &rest[1..];
998 }
999 let Some(mut scope) = scopes.get(&base) else {
1000 return UseTarget::Unresolved;
1001 };
1002 if rest.is_empty() {
1003 let Some(name) = base.last() else {
1004 return UseTarget::Unresolved;
1005 };
1006 return UseTarget::Item(
1007 ImportedTarget {
1008 canon: qualified_name(&base[..base.len() - 1], name),
1009 prim: None,
1010 macro_resolution: None,
1011 module: Some(base.clone()),
1012 },
1013 [false, true, false, false, false],
1014 );
1015 }
1016 for (index, name) in rest.iter().enumerate() {
1017 let name = unraw(name);
1018 if index + 1 == rest.len() {
1019 return resolve_item(scopes, scope, &base, name, depth + 1, active, steps);
1020 }
1021 let mut next_path = base.clone();
1022 next_path.push(name.to_string());
1023 if !scopes.contains_key(&next_path) {
1024 let UseTarget::Item(target, namespaces) =
1025 resolve_item(scopes, scope, &base, name, depth + 1, active, steps)
1026 else {
1027 return UseTarget::Unresolved;
1028 };
1029 if !namespaces[1] {
1030 return UseTarget::Unresolved;
1031 }
1032 let Some(module) = target.module else {
1033 return UseTarget::Unresolved;
1034 };
1035 next_path = module;
1036 }
1037 scope = scopes
1038 .get(&next_path)
1039 .expect("indexed import target module");
1040 base = next_path;
1041 }
1042 UseTarget::Unresolved
1043}
1044
1045fn resolve_item(
1048 scopes: &ModuleMap<'_>,
1049 declarations: &Declarations<'_>,
1050 module: &[String],
1051 name: &str,
1052 depth: usize,
1053 active: Option<&ImportPath<'_>>,
1054 steps: &Cell<usize>,
1055) -> UseTarget {
1056 if !declarations.repeated() {
1057 let items = declarations.first.items();
1058 return resolve_declared(scopes, items, module, name, depth, active, steps);
1059 }
1060 let mut merged: Option<(ImportedTarget, [bool; 5])> = None;
1061 for scope in declarations.iter() {
1062 steps.set(steps.get() + 1);
1063 if steps.get() > UNION_STEPS {
1064 return UseTarget::Unresolved;
1065 }
1066 let items = scope.items();
1067 match resolve_declared(scopes, items, module, name, depth, active, steps) {
1068 UseTarget::Item(target, namespaces) => {
1069 merged = Some(match merged {
1070 None => (target, namespaces),
1071 Some((current, seen)) => {
1072 let Some(target) = current.agree(&target) else {
1073 return UseTarget::Unresolved;
1074 };
1075 (
1076 target,
1077 core::array::from_fn(|ns| seen[ns] || namespaces[ns]),
1078 )
1079 }
1080 });
1081 }
1082 UseTarget::Unresolved if !may_name(items, name) => {}
1083 UseTarget::External | UseTarget::Unresolved => return UseTarget::Unresolved,
1084 }
1085 }
1086 merged.map_or(UseTarget::Unresolved, |(target, namespaces)| {
1087 UseTarget::Item(target, namespaces)
1088 })
1089}
1090
1091fn may_name(items: &[syn::Item], name: &str) -> bool {
1093 items.iter().any(|item| match item {
1094 syn::Item::Use(import) => {
1095 has_glob(&import.tree)
1096 || alpha::imported_name_matches(&import.tree, |ident| ident_name(ident) == name)
1097 }
1098 syn::Item::ExternCrate(krate) => {
1099 let bound = krate
1100 .rename
1101 .as_ref()
1102 .map_or(&krate.ident, |(_, rename)| rename);
1103 ident_name(bound) == name
1104 }
1105 syn::Item::Macro(item) => item.ident.is_none(),
1106 _ => false,
1107 })
1108}
1109
1110fn resolve_declared(
1111 scopes: &ModuleMap<'_>,
1112 items: &[syn::Item],
1113 module: &[String],
1114 name: &str,
1115 depth: usize,
1116 active: Option<&ImportPath<'_>>,
1117 steps: &Cell<usize>,
1118) -> UseTarget {
1119 if depth >= 64 {
1120 return UseTarget::Unresolved;
1121 }
1122 let mut namespaces = [false; 5];
1123 for item in items {
1124 let value = match item {
1125 syn::Item::Fn(item) => Some(&item.sig.ident),
1126 syn::Item::Const(item) => Some(&item.ident),
1127 syn::Item::Static(item) => Some(&item.ident),
1128 _ => None,
1129 };
1130 if value.is_some_and(|ident| ident_name(ident) == name) {
1131 namespaces[Ns::Value as usize] = true;
1132 }
1133 if alpha::type_item_ident(item).is_some_and(|ident| ident_name(ident) == name) {
1134 namespaces[Ns::Type as usize] = true;
1135 if let syn::Item::Struct(item) = item
1136 && !matches!(item.fields, syn::Fields::Named(_))
1137 {
1138 namespaces[Ns::Value as usize] = true;
1139 }
1140 }
1141 if let syn::Item::Macro(item) = item
1142 && item
1143 .ident
1144 .as_ref()
1145 .is_some_and(|ident| ident_name(ident) == name)
1146 {
1147 namespaces[Ns::Macro as usize] = true;
1148 }
1149 }
1150 if namespaces.iter().any(|present| *present) {
1151 let prim = items.iter().find_map(|item| match item {
1152 syn::Item::Type(alias) if ident_name(&alias.ident) == name => {
1153 alias_chain_prim(alias, &items.iter(), scope_primitives_known(items), 0).or_else(
1154 || resolved_alias_prim(scopes, module, alias, depth + 1, active, steps),
1155 )
1156 }
1157 _ => None,
1158 });
1159 UseTarget::Item(
1160 ImportedTarget {
1161 canon: qualified_name(module, name),
1162 prim,
1163 macro_resolution: None,
1164 module: items.iter().find_map(|item| {
1165 if let syn::Item::Mod(item) = item
1166 && ident_name(&item.ident) == name
1167 {
1168 let mut path = module.to_vec();
1169 path.push(name.to_string());
1170 scopes.contains_key(&path).then_some(path)
1171 } else {
1172 None
1173 }
1174 }),
1175 },
1176 namespaces,
1177 )
1178 } else {
1179 resolve_imported_item(scopes, items, module, name, depth, active, steps)
1180 }
1181}
1182
1183fn resolve_imported_item(
1184 scopes: &ModuleMap<'_>,
1185 items: &[syn::Item],
1186 module: &[String],
1187 name: &str,
1188 depth: usize,
1189 active: Option<&ImportPath<'_>>,
1190 steps: &Cell<usize>,
1191) -> UseTarget {
1192 if active.is_some_and(|query| query.contains(module, name)) {
1193 return UseTarget::Unresolved;
1194 }
1195 let query = ImportPath {
1196 module,
1197 name,
1198 parent: active,
1199 };
1200 let mut namespaces = [false; 5];
1201 let mut imports = BTreeMap::new();
1202 for item in items {
1203 if let syn::Item::Use(import) = item {
1204 use_tree_bindings(
1205 &mut UseBinder {
1206 scopes,
1207 from: module,
1208 import,
1209 imports: &mut imports,
1210 only_name: Some(name),
1211 depth: depth + 1,
1212 active: Some(&query),
1213 steps,
1214 },
1215 &[],
1216 &import.tree,
1217 );
1218 }
1219 }
1220 let Some((alias, mut imported)) = imports.into_iter().find(|(alias, _)| unraw(alias) == name)
1221 else {
1222 return UseTarget::Unresolved;
1223 };
1224 imported.settle(module, &alias);
1225 let mut target: Option<ImportedTarget> = None;
1226 for (index, slot) in imported.resolved.into_iter().enumerate() {
1227 if let Some(resolved) = slot {
1228 if target
1229 .as_ref()
1230 .is_some_and(|current| current.canon != resolved.canon)
1231 {
1232 return UseTarget::Unresolved;
1233 }
1234 namespaces[index] = true;
1235 target = Some(resolved);
1236 }
1237 }
1238 if target.is_none()
1239 && let UnknownImport::Unique(external) = imported.unknown
1240 && external.prim.is_some()
1241 {
1242 namespaces[1] = true;
1243 target = Some(external);
1244 }
1245 target.map_or(UseTarget::Unresolved, |target| {
1246 UseTarget::Item(target, namespaces)
1247 })
1248}
1249
1250pub(crate) fn external_prim(path: &[String]) -> Option<PrimTy> {
1253 if let [first, middle, third] = path
1254 && middle == "primitive"
1255 && (first == "std" || first == "core")
1256 {
1257 return crate::scope::primitive_suffix(third);
1258 }
1259 None
1260}
1261
1262fn alias_chain_prim<'a>(
1265 alias: &syn::ItemType,
1266 siblings: &(impl Iterator<Item = &'a syn::Item> + Clone),
1267 prim_fallback: bool,
1268 hops: usize,
1269) -> Option<PrimTy> {
1270 if hops > 16 || !prim_fallback || !alias.generics.params.is_empty() {
1271 return None;
1272 }
1273 let target = alpha::single_segment_type_name(&alias.ty)?;
1274 let target = unraw(&target);
1275 for item in siblings.clone() {
1276 match item {
1277 syn::Item::Type(other) if ident_name(&other.ident) == target => {
1278 return alias_chain_prim(other, siblings, prim_fallback, hops + 1);
1279 }
1280 syn::Item::Use(use_item) => {
1281 if alpha::imported_name_matches(&use_item.tree, |ident| ident_name(ident) == target)
1282 {
1283 return None;
1284 }
1285 }
1286 other => {
1287 if let Some(ident) = alpha::type_item_ident(other)
1288 && ident_name(ident) == target
1289 {
1290 return None;
1291 }
1292 }
1293 }
1294 }
1295 crate::scope::primitive_suffix(target)
1296}
1297
1298fn alias_type_path(mut ty: &syn::Type) -> Option<&syn::Path> {
1299 for _ in 0..64 {
1300 match ty {
1301 syn::Type::Paren(paren) => ty = &paren.elem,
1302 syn::Type::Group(group) => ty = &group.elem,
1303 syn::Type::Path(path)
1304 if path.qself.is_none()
1305 && path
1306 .path
1307 .segments
1308 .iter()
1309 .all(|segment| segment.arguments.is_empty()) =>
1310 {
1311 return Some(&path.path);
1312 }
1313 _ => return None,
1314 }
1315 }
1316 None
1317}
1318
1319fn resolved_alias_prim(
1320 scopes: &ModuleMap<'_>,
1321 module: &[String],
1322 alias: &syn::ItemType,
1323 depth: usize,
1324 active: Option<&ImportPath<'_>>,
1325 steps: &Cell<usize>,
1326) -> Option<PrimTy> {
1327 if depth >= 64 || !alias.generics.params.is_empty() || blocking_attr(&alias.attrs) {
1328 return None;
1329 }
1330 let scope = scopes.get(module)?;
1331 if !scope.primitives_known() {
1332 return None;
1333 }
1334 let path = alias_type_path(&alias.ty)?;
1335 let names: Vec<String> = path
1336 .segments
1337 .iter()
1338 .map(|segment| ident_name(&segment.ident))
1339 .collect();
1340 match resolve_use_at(
1341 scopes,
1342 module,
1343 &names,
1344 path.leading_colon.is_some(),
1345 depth + 1,
1346 active,
1347 steps,
1348 ) {
1349 UseTarget::Item(target, namespaces) if namespaces[1] => target.prim,
1350 UseTarget::External => external_prim(&names),
1351 UseTarget::Unresolved if names.len() == 1 && !scope.shadows_primitive_crate(&names[0]) => {
1352 crate::scope::primitive_suffix(&names[0])
1353 }
1354 _ => None,
1355 }
1356}
1357
1358fn seed_alias_prim(
1359 scopes: &ModuleMap<'_>,
1360 module: &[String],
1361 alias: &syn::ItemType,
1362 frame: &Frame,
1363 prim_fallback: bool,
1364 steps: &Cell<usize>,
1365) -> Option<PrimTy> {
1366 if !alias.generics.params.is_empty() || blocking_attr(&alias.attrs) {
1367 return None;
1368 }
1369 let path = alias_type_path(&alias.ty)?;
1370 if path.leading_colon.is_none() && path.segments.len() == 1 {
1371 let name = ident_name(&path.segments[0].ident);
1372 return match frame.lookup(Ns::Type, &name) {
1373 Some(binding) => binding.prim,
1374 None if prim_fallback => crate::scope::primitive_suffix(&name),
1375 None => None,
1376 };
1377 }
1378 resolved_alias_prim(scopes, module, alias, 0, None, steps)
1379}
1380
1381fn bind_item_ident(
1382 frame: &mut Frame,
1383 module: &[String],
1384 ns: Ns,
1385 ident: &syn::Ident,
1386 origin: Origin,
1387 prim: Option<PrimTy>,
1388 next_id: &mut usize,
1389) {
1390 let name = ident.to_string();
1391 let binding = Binding {
1392 id: BindingId(*next_id),
1393 canon: qualified_name(module, &name),
1394 origin,
1395 target: None,
1396 raw: false,
1397 prim,
1398 port: 0,
1399 value_port: None,
1400 helper: None,
1401 macro_resolution: None,
1402 };
1403 frame.bind(ns, &name, binding);
1404 *next_id += 1;
1405}
1406
1407fn bind_extern_crate(
1408 frame: &mut Frame,
1409 module: &[String],
1410 item: &syn::ItemExternCrate,
1411 next_id: &mut usize,
1412) {
1413 let ident = item.rename.as_ref().map_or(&item.ident, |(_, alias)| alias);
1414 let name = ident.to_string();
1415 frame.bind(
1416 Ns::Type,
1417 &name,
1418 Binding {
1419 id: BindingId(*next_id),
1420 canon: qualified_name(module, &name),
1421 origin: Origin::Use,
1422 target: None,
1423 raw: false,
1424 prim: None,
1425 port: 0,
1426 value_port: None,
1427 helper: None,
1428 macro_resolution: crate::macros::MacroResolution::external_crate(item),
1429 },
1430 );
1431 *next_id += 1;
1432}
1433
1434fn bind_macro_rule(
1435 frame: &mut Frame,
1436 module: &[String],
1437 item: &syn::ItemMacro,
1438 next_id: &mut usize,
1439) {
1440 if let Some(ident) = &item.ident {
1441 bind_item_ident(
1442 frame,
1443 module,
1444 Ns::Macro,
1445 ident,
1446 Origin::MacroRule,
1447 None,
1448 next_id,
1449 );
1450 }
1451}
1452
1453fn item_seed<'a>(
1456 item: &'a syn::Item,
1457 siblings: &(impl Iterator<Item = &'a syn::Item> + Clone),
1458 prim_fallback: bool,
1459) -> Option<(Ns, &'a syn::Ident, Origin, Option<PrimTy>)> {
1460 match item {
1461 syn::Item::Fn(i) => Some((Ns::Value, &i.sig.ident, Origin::Item, None)),
1462 syn::Item::Const(i) => Some((Ns::Value, &i.ident, Origin::Item, None)),
1463 syn::Item::Static(i) => Some((Ns::Value, &i.ident, Origin::Item, None)),
1464 syn::Item::Struct(i) => Some((Ns::Type, &i.ident, Origin::Item, None)),
1465 syn::Item::Enum(i) => Some((Ns::Type, &i.ident, Origin::Item, None)),
1466 syn::Item::Union(i) => Some((Ns::Type, &i.ident, Origin::Item, None)),
1467 syn::Item::Trait(i) => Some((Ns::Type, &i.ident, Origin::Item, None)),
1468 syn::Item::TraitAlias(i) => Some((Ns::Type, &i.ident, Origin::Item, None)),
1469 syn::Item::Mod(i) => Some((Ns::Type, &i.ident, Origin::Item, None)),
1470 syn::Item::Type(i) => Some((
1471 Ns::Type,
1472 &i.ident,
1473 Origin::TypeAlias,
1474 alias_chain_prim(i, siblings, prim_fallback, 0),
1475 )),
1476 _ => None,
1477 }
1478}
1479
1480pub(crate) fn seed_frame(
1483 scopes: &ModuleMap<'_>,
1484 path: &[String],
1485 scope: &ModuleScope<'_>,
1486 prim_fallback: bool,
1487) -> Frame {
1488 seed_scope(scopes, path, &scope.items().iter(), None, prim_fallback)
1489}
1490
1491pub(crate) fn seed_items(
1492 scopes: &ModuleMap<'_>,
1493 module: &[String],
1494 items: &[&syn::Item],
1495 outer: &Frame,
1496 prim_fallback: bool,
1497) -> Frame {
1498 seed_scope(
1499 scopes,
1500 module,
1501 &items.iter().copied(),
1502 Some(outer),
1503 prim_fallback,
1504 )
1505}
1506
1507fn seed_scope<'a>(
1508 scopes: &ModuleMap<'_>,
1509 path: &[String],
1510 items: &(impl Iterator<Item = &'a syn::Item> + Clone),
1511 outer: Option<&Frame>,
1512 prim_fallback: bool,
1513) -> Frame {
1514 let mut frame = outer.cloned().unwrap_or_default();
1515 let mut next_id = frame.next_id();
1516 let mut imports = BTreeMap::new();
1517 let steps = Cell::new(0);
1518 for item in items.clone() {
1519 if let Some((ns, ident, origin, prim)) =
1520 item_seed(item, items, prim_fallback && outer.is_none())
1521 {
1522 bind_item_ident(&mut frame, path, ns, ident, origin, prim, &mut next_id);
1523 if let syn::Item::Enum(item) = item {
1524 frame.set_variants(&ident_name(&item.ident), item);
1525 }
1526 if let syn::Item::Struct(item) = item
1527 && !matches!(item.fields, syn::Fields::Named(_))
1528 {
1529 bind_item_ident(
1530 &mut frame,
1531 path,
1532 Ns::Value,
1533 &item.ident,
1534 Origin::Item,
1535 None,
1536 &mut next_id,
1537 );
1538 }
1539 continue;
1540 }
1541 match item {
1542 syn::Item::ExternCrate(item) => {
1543 bind_extern_crate(&mut frame, path, item, &mut next_id);
1544 }
1545 syn::Item::Use(use_item) => {
1546 frame.glob |= has_glob(&use_item.tree);
1547 use_tree_bindings(
1548 &mut UseBinder {
1549 scopes,
1550 from: path,
1551 import: use_item,
1552 imports: &mut imports,
1553 only_name: None,
1554 depth: 0,
1555 active: None,
1556 steps: &steps,
1557 },
1558 &[],
1559 &use_item.tree,
1560 );
1561 }
1562 syn::Item::Macro(item) => bind_macro_rule(&mut frame, path, item, &mut next_id),
1563 _ => {}
1564 }
1565 }
1566 bind_imports(&mut frame, path, imports, &mut next_id);
1567 for _ in 0..16 {
1568 let mut changed = false;
1569 for item in items.clone() {
1570 if let syn::Item::Type(alias) = item {
1571 if outer.is_none()
1572 && alias_type_path(&alias.ty).is_some_and(|path| path.segments.len() == 1)
1573 {
1574 continue;
1575 }
1576 let name = ident_name(&alias.ident);
1577 let lexical_shadow = outer.is_some()
1578 && alias_type_path(&alias.ty).is_some_and(|target| {
1579 target.leading_colon.is_none()
1580 && target.segments.len() > 1
1581 && target.segments.first().is_some_and(|first| {
1582 let first_name = ident_name(&first.ident);
1583 !matches!(first_name.as_str(), "crate" | "self" | "super")
1584 && items.clone().any(|item| {
1585 alpha::type_item_ident(item)
1586 .is_some_and(|ident| ident_name(ident) == first_name)
1587 })
1588 })
1589 });
1590 let prim = if lexical_shadow {
1591 None
1592 } else {
1593 seed_alias_prim(scopes, path, alias, &frame, prim_fallback, &steps)
1594 };
1595 if let Some(binding) = frame.bindings_mut(Ns::Type).get_mut(&name)
1596 && binding.origin == Origin::TypeAlias
1597 && binding.prim != prim
1598 {
1599 binding.prim = prim;
1600 changed = true;
1601 }
1602 }
1603 }
1604 if !changed {
1605 break;
1606 }
1607 }
1608 frame
1609}
1610
1611fn bind_generic_param(
1613 frame: &mut Frame,
1614 next_id: &mut usize,
1615 namespaces: &[Ns],
1616 ident: &syn::Ident,
1617 origin: Origin,
1618 label: String,
1619) {
1620 let name = ident.to_string();
1621 let binding = Binding {
1622 id: BindingId(*next_id),
1623 canon: label,
1624 origin,
1625 target: None,
1626 raw: false,
1627 prim: None,
1628 port: 0,
1629 value_port: None,
1630 helper: None,
1631 macro_resolution: None,
1632 };
1633 for ns in namespaces {
1634 frame.bind(*ns, &name, binding.clone());
1635 }
1636 *next_id += 1;
1637}
1638
1639fn generics_frame(
1641 module: ModuleId,
1642 modules: &[Module],
1643 generics: &syn::Generics,
1644 labels: Vec<String>,
1645) -> Frame {
1646 let mut frame = Frame::default();
1647 let mut next_id = modules[module.0].seed.next_id();
1648 for (param, label) in generics.params.iter().zip(labels) {
1649 let (namespaces, ident, origin): (&[Ns], _, _) = match param {
1650 syn::GenericParam::Type(ty_param) => (&[Ns::Type], &ty_param.ident, Origin::Generic),
1651 syn::GenericParam::Lifetime(lifetime) => {
1652 (&[Ns::Lifetime], &lifetime.lifetime.ident, Origin::Lifetime)
1653 }
1654 syn::GenericParam::Const(r#const) => {
1655 (&[Ns::Value, Ns::Type], &r#const.ident, Origin::Generic)
1656 }
1657 };
1658 bind_generic_param(&mut frame, &mut next_id, namespaces, ident, origin, label);
1659 }
1660 frame
1661}
1662
1663fn trait_roles(generics: &syn::Generics) -> Vec<String> {
1665 (0..generics.params.len())
1666 .map(|position| alloc::format!("{OWNER_PARAMETER}t_{position}"))
1667 .collect()
1668}
1669
1670fn impl_roles(item: &syn::ItemImpl) -> Vec<String> {
1674 let mut roles = OwnerRoles {
1675 params: &item.generics,
1676 labels: BTreeMap::new(),
1677 };
1678 let mut path = String::from("s");
1679 roles.ty(&item.self_ty, &mut path);
1680 if let Some((trait_path, _)) = &item.trait_ {
1681 let mut path = String::from("t");
1682 roles.path_args(trait_path, &mut path, &mut 0);
1683 }
1684 let mut unplaced = 0;
1685 item.generics
1686 .params
1687 .iter()
1688 .map(|param| {
1689 let role = roles
1690 .labels
1691 .get(¶m_key(param))
1692 .cloned()
1693 .unwrap_or_else(|| {
1694 unplaced += 1;
1695 alloc::format!("b_{}", unplaced - 1)
1696 });
1697 alloc::format!("{OWNER_PARAMETER}{role}")
1698 })
1699 .collect()
1700}
1701
1702fn param_key(param: &syn::GenericParam) -> (bool, String) {
1704 match param {
1705 syn::GenericParam::Lifetime(lifetime) => (true, lifetime.lifetime.ident.to_string()),
1706 syn::GenericParam::Type(ty_param) => (false, ty_param.ident.to_string()),
1707 syn::GenericParam::Const(r#const) => (false, r#const.ident.to_string()),
1708 }
1709}
1710
1711struct OwnerRoles<'a> {
1713 params: &'a syn::Generics,
1714 labels: BTreeMap<(bool, String), String>,
1715}
1716
1717impl OwnerRoles<'_> {
1718 fn place(&mut self, lifetime: bool, ident: &syn::Ident, path: &str) {
1720 let key = (lifetime, ident.to_string());
1721 if !self.labels.contains_key(&key)
1722 && self
1723 .params
1724 .params
1725 .iter()
1726 .any(|param| param_key(param) == key)
1727 {
1728 self.labels.insert(key, path.to_string());
1729 }
1730 }
1731
1732 fn ty(&mut self, ty: &syn::Type, path: &mut String) {
1734 match ty {
1735 syn::Type::Path(type_path) if type_path.qself.is_none() => {
1736 if let Some(ident) = type_path.path.get_ident() {
1737 self.place(false, ident, path);
1738 } else {
1739 self.path_args(&type_path.path, path, &mut 0);
1740 }
1741 }
1742 syn::Type::Reference(reference) => {
1743 if let Some(lifetime) = &reference.lifetime {
1744 let len = enter(path, &mut 0);
1745 self.place(true, &lifetime.ident, path);
1746 path.truncate(len);
1747 }
1748 let len = enter(path, &mut 1);
1749 self.ty(&reference.elem, path);
1750 path.truncate(len);
1751 }
1752 syn::Type::Array(array) => {
1753 let len = enter(path, &mut 0);
1754 self.ty(&array.elem, path);
1755 path.truncate(len);
1756 let len = enter(path, &mut 1);
1757 self.expr(&array.len, path);
1758 path.truncate(len);
1759 }
1760 syn::Type::Tuple(tuple) => {
1761 let mut index = 0;
1762 for elem in &tuple.elems {
1763 let len = enter(path, &mut index);
1764 self.ty(elem, path);
1765 path.truncate(len);
1766 }
1767 }
1768 syn::Type::Slice(syn::TypeSlice { elem, .. })
1769 | syn::Type::Ptr(syn::TypePtr { elem, .. })
1770 | syn::Type::Paren(syn::TypeParen { elem, .. })
1771 | syn::Type::Group(syn::TypeGroup { elem, .. }) => {
1772 let len = enter(path, &mut 0);
1773 self.ty(elem, path);
1774 path.truncate(len);
1775 }
1776 other => self.occurrences(|visitor| visitor.visit_type(other), path),
1777 }
1778 }
1779
1780 fn path_args(&mut self, path_syntax: &syn::Path, path: &mut String, index: &mut usize) {
1783 for segment in &path_syntax.segments {
1784 if let syn::PathArguments::AngleBracketed(arguments) = &segment.arguments {
1785 for argument in &arguments.args {
1786 let len = enter(path, index);
1787 self.argument(argument, path);
1788 path.truncate(len);
1789 }
1790 }
1791 }
1792 }
1793
1794 fn argument(&mut self, argument: &syn::GenericArgument, path: &mut String) {
1795 match argument {
1796 syn::GenericArgument::Type(ty) => self.ty(ty, path),
1797 syn::GenericArgument::Lifetime(lifetime) => self.place(true, &lifetime.ident, path),
1798 syn::GenericArgument::Const(expr) => self.expr(expr, path),
1799 other => self.occurrences(|visitor| visitor.visit_generic_argument(other), path),
1800 }
1801 }
1802
1803 fn expr(&mut self, expr: &syn::Expr, path: &str) {
1804 if let syn::Expr::Path(expr_path) = expr
1805 && expr_path.qself.is_none()
1806 && let Some(ident) = expr_path.path.get_ident()
1807 {
1808 self.place(false, ident, path);
1809 } else {
1810 self.occurrences(|visitor| visitor.visit_expr(expr), path);
1811 }
1812 }
1813
1814 fn occurrences(&mut self, visit: impl FnOnce(&mut Occurrences<'_, '_>), path: &str) {
1816 let mut visitor = Occurrences {
1817 roles: self,
1818 path,
1819 count: 0,
1820 };
1821 visit(&mut visitor);
1822 }
1823}
1824
1825fn enter(path: &mut String, index: &mut usize) -> usize {
1827 let len = path.len();
1828 path.push('_');
1829 path.push_str(&index.to_string());
1830 *index += 1;
1831 len
1832}
1833
1834struct Occurrences<'r, 'g> {
1836 roles: &'r mut OwnerRoles<'g>,
1837 path: &'r str,
1838 count: usize,
1839}
1840
1841impl Occurrences<'_, '_> {
1842 fn place(&mut self, lifetime: bool, ident: &syn::Ident) {
1843 let label = alloc::format!("{}_v{}", self.path, self.count);
1844 self.count += 1;
1845 self.roles.place(lifetime, ident, &label);
1846 }
1847}
1848
1849impl<'ast> Visit<'ast> for Occurrences<'_, '_> {
1850 fn visit_path(&mut self, path: &'ast syn::Path) {
1851 if let Some(ident) = path.get_ident() {
1852 self.place(false, ident);
1853 }
1854 syn::visit::visit_path(self, path);
1855 }
1856
1857 fn visit_lifetime(&mut self, lifetime: &'ast syn::Lifetime) {
1858 self.place(true, &lifetime.ident);
1859 }
1860}
1861
1862#[expect(
1864 clippy::too_many_arguments,
1865 reason = "the index carries its original module and observer evidence"
1866)]
1867#[expect(
1868 clippy::too_many_lines,
1869 reason = "each item kind carries its original owner and ancestry"
1870)]
1871fn index_scope<'a>(
1872 decls: &mut BTreeMap<(*const syn::Signature, *const syn::Block), Decl<'a>>,
1873 module_ids: &ModuleIds,
1874 modules: &[Module],
1875 path: &[String],
1876 scope: ModuleScope<'a>,
1877 observed: bool,
1878 macro_input: bool,
1879 helper_blocked: bool,
1880 gen_frames: &mut BTreeMap<*const syn::Generics, Frame>,
1881) {
1882 let module = module_ids
1883 .get(path)
1884 .and_then(|ids| ids.iter().find(|(key, _)| *key == scope.key()))
1885 .expect("a module id")
1886 .1;
1887 let prim_fallback = modules[module.0].prim_fallback;
1888 let observed = observed || modules[module.0].observed;
1889 let helper_blocked = helper_blocked || modules[module.0].helper_blocked;
1890 for item in scope.items() {
1891 match item {
1892 syn::Item::Fn(f) => {
1893 index_fn(
1894 decls,
1895 FnDecl {
1896 module,
1897 prim_fallback,
1898 owner: Owner::Free,
1899 observed,
1900 macro_input,
1901 helper_module: path,
1902 helper_blocked,
1903 attrs: &f.attrs,
1904 generics: None,
1905 },
1906 &f.sig,
1907 &f.block,
1908 );
1909 }
1910 syn::Item::Mod(m) => {
1911 if m.content.is_some() {
1912 let mut child = path.to_vec();
1913 child.push(unraw(&m.ident.to_string()).to_string());
1914 index_scope(
1915 decls,
1916 module_ids,
1917 modules,
1918 &child,
1919 ModuleScope::Inline(m),
1920 observed || live_attr(&m.attrs),
1921 macro_input || crate::drift::has_macro_attribute(&m.attrs),
1922 helper_blocked || blocking_attr(&m.attrs),
1923 gen_frames,
1924 );
1925 }
1926 }
1927 syn::Item::Impl(i) => {
1928 let text = i.self_ty.to_token_stream().to_string();
1929 let owner = if i.trait_.is_some() {
1930 Owner::Trait(text)
1931 } else {
1932 Owner::Inherent(text)
1933 };
1934 let observed = observed || live_attr(&i.attrs);
1935 let macro_input = macro_input || crate::drift::has_macro_attribute(&i.attrs);
1936 let key = &raw const i.generics;
1937 gen_frames
1938 .entry(key)
1939 .or_insert_with(|| generics_frame(module, modules, &i.generics, impl_roles(i)));
1940 for member in &i.items {
1941 if let syn::ImplItem::Fn(f) = member {
1942 index_fn(
1943 decls,
1944 FnDecl {
1945 module,
1946 prim_fallback,
1947 owner: owner.clone(),
1948 observed,
1949 macro_input,
1950 helper_module: path,
1951 helper_blocked: helper_blocked || blocking_attr(&i.attrs),
1952 attrs: &f.attrs,
1953 generics: Some(key),
1954 },
1955 &f.sig,
1956 &f.block,
1957 );
1958 }
1959 }
1960 }
1961 syn::Item::Trait(t) => {
1962 let text = t.ident.to_string();
1963 let observed = observed || live_attr(&t.attrs);
1964 let macro_input = macro_input || crate::drift::has_macro_attribute(&t.attrs);
1965 let key = &raw const t.generics;
1966 gen_frames.entry(key).or_insert_with(|| {
1967 generics_frame(module, modules, &t.generics, trait_roles(&t.generics))
1968 });
1969 for member in &t.items {
1970 if let syn::TraitItem::Fn(f) = member
1971 && let Some(block) = &f.default
1972 {
1973 index_fn(
1974 decls,
1975 FnDecl {
1976 module,
1977 prim_fallback,
1978 owner: Owner::Trait(text.clone()),
1979 observed,
1980 macro_input,
1981 attrs: &f.attrs,
1982 helper_module: path,
1983 helper_blocked: helper_blocked || blocking_attr(&t.attrs),
1984 generics: Some(key),
1985 },
1986 &f.sig,
1987 block,
1988 );
1989 }
1990 }
1991 }
1992 _ => {}
1993 }
1994 }
1995}
1996
1997#[expect(
1999 clippy::struct_excessive_bools,
2000 reason = "primitive, observer, macro input and helper facts of a function are independent"
2001)]
2002struct FnDecl<'a, 'path> {
2003 module: ModuleId,
2004 prim_fallback: bool,
2005 owner: Owner,
2006 observed: bool,
2007 macro_input: bool,
2009 helper_module: &'path [String],
2010 helper_blocked: bool,
2011 attrs: &'a [syn::Attribute],
2012 generics: Option<*const syn::Generics>,
2013}
2014
2015fn index_fn<'a>(
2016 decls: &mut BTreeMap<(*const syn::Signature, *const syn::Block), Decl<'a>>,
2017 facts: FnDecl<'a, '_>,
2018 sig: &syn::Signature,
2019 block: &syn::Block,
2020) {
2021 let key = (
2022 core::ptr::from_ref::<syn::Signature>(sig),
2023 core::ptr::from_ref::<syn::Block>(block),
2024 );
2025 decls.insert(
2026 key,
2027 Decl {
2028 module: facts.module,
2029 attrs: facts.attrs,
2030 owner: facts.owner,
2031 prim_fallback: facts.prim_fallback,
2032 observed: facts.observed || live_attr(facts.attrs),
2033 enclosing_observed: facts.macro_input,
2034 helper_module: facts.helper_module.to_vec(),
2035 helper_blocked: facts.helper_blocked || blocking_attr(facts.attrs),
2036 generics: facts.generics,
2037 },
2038 );
2039}
2040
2041pub(crate) fn live(meta: &syn::Meta) -> bool {
2045 let path = meta.path();
2046 if path.is_ident("cfg_attr") {
2047 return wrapped(meta, 1).is_none_or(|metas| metas.iter().any(live));
2048 }
2049 if path.is_ident("unsafe") {
2050 return wrapped(meta, 0).is_none_or(|metas| metas.iter().any(live));
2051 }
2052 if path.is_ident("doc")
2054 && matches!(meta, syn::Meta::NameValue(nv) if matches!(nv.value, syn::Expr::Macro(_)))
2055 {
2056 return true;
2057 }
2058 let inert = [
2059 "cfg",
2060 "inline",
2061 "cold",
2062 "must_use",
2063 "track_caller",
2064 "doc",
2065 "allow",
2066 "warn",
2067 "deny",
2068 "forbid",
2069 "expect",
2070 "no_mangle",
2071 "export_name",
2072 ];
2073 !inert.iter().any(|name| path.is_ident(name))
2074 && path.segments.first().is_none_or(|s| s.ident != "rustfmt")
2075}
2076
2077pub(crate) fn live_attr(attrs: &[syn::Attribute]) -> bool {
2079 attrs.iter().any(|attr| live(&attr.meta))
2080}
2081
2082pub(crate) fn blocking_meta(meta: &syn::Meta) -> bool {
2084 let path = meta.path();
2085 path.is_ident("cfg")
2086 || path.is_ident("cfg_attr")
2087 || path.is_ident("track_caller")
2088 || (path.is_ident("unsafe")
2089 && wrapped(meta, 0).is_none_or(|metas| metas.iter().any(blocking_meta)))
2090 || live(meta)
2091}
2092
2093pub(crate) fn blocking_attr(attrs: &[syn::Attribute]) -> bool {
2095 attrs.iter().any(|attr| blocking_meta(&attr.meta))
2096}
2097
2098#[must_use]
2107pub fn wrapped(meta: &syn::Meta, skip: usize) -> Option<Vec<syn::Meta>> {
2108 let syn::Meta::List(list) = meta else {
2109 return None;
2110 };
2111 let parser = syn::punctuated::Punctuated::<syn::Meta, syn::Token![,]>::parse_terminated;
2112 list.parse_args_with(parser)
2113 .ok()
2114 .map(|metas| metas.into_iter().skip(skip).collect())
2115}