use super::bindings::{
canonical_from_type, extract_let_binding, normalize_alias_expansion, CanonScope,
};
use super::local_symbols::{scope_for_local, FileScope};
use super::type_infer::resolve::{resolve_type, ResolveContext};
use super::type_infer::self_subst::substitute_bare_self;
use super::type_infer::{
extract_bindings, extract_for_bindings, infer_type, BindingLookup, CanonicalType, InferContext,
WorkspaceTypeIndex,
};
use crate::adapters::analyzers::architecture::forbidden_rule::{
file_to_module_segments, resolve_to_crate_absolute_in,
};
use crate::adapters::shared::use_tree::AliasTarget;
use std::collections::{HashMap, HashSet};
use syn::visit::Visit;
const METHOD_UNKNOWN_PREFIX: &str = "<method>:";
const BARE_UNKNOWN_PREFIX: &str = "<bare>:";
pub struct FnContext<'a> {
pub file: &'a FileScope<'a>,
pub mod_stack: &'a [String],
pub body: &'a syn::Block,
pub signature_params: Vec<(String, &'a syn::Type)>,
pub generic_params: HashMap<String, super::signature_params::ParamInfo>,
pub self_type: Option<Vec<String>>,
pub workspace_index: Option<&'a WorkspaceTypeIndex>,
pub workspace_files: Option<&'a HashMap<String, FileScope<'a>>>,
pub reexports: Option<&'a super::reexports::ReexportMap>,
}
pub fn collect_canonical_calls(ctx: &FnContext<'_>) -> HashSet<String> {
let mut collector = CanonicalCallCollector::new(ctx);
collector.seed_signature_bindings();
collector.visit_block(ctx.body);
collector.calls
}
struct CanonicalCallCollector<'a> {
file: &'a FileScope<'a>,
mod_stack: &'a [String],
self_type_canonical: Option<Vec<String>>,
signature_params: Vec<(String, &'a syn::Type)>,
generic_params: HashMap<String, super::signature_params::ParamInfo>,
bindings: Vec<HashMap<String, Vec<String>>>,
non_path_bindings: Vec<HashMap<String, CanonicalType>>,
calls: HashSet<String>,
workspace_index: Option<&'a WorkspaceTypeIndex>,
workspace_files: Option<&'a HashMap<String, FileScope<'a>>>,
reexports: Option<&'a super::reexports::ReexportMap>,
}
mod canon;
mod install;
mod resolve;
mod scope;
mod visit;
pub(super) fn parse_macro_tokens(tokens: proc_macro2::TokenStream) -> Vec<syn::Expr> {
crate::adapters::shared::macro_tokens::recover_exprs(&tokens)
}
struct CollectorBindings<'a> {
scope: &'a [HashMap<String, Vec<String>>],
non_path_scope: &'a [HashMap<String, CanonicalType>],
}
impl BindingLookup for CollectorBindings<'_> {
fn lookup(&self, ident: &str) -> Option<CanonicalType> {
for (path_frame, non_path_frame) in self
.scope
.iter()
.rev()
.zip(self.non_path_scope.iter().rev())
{
if let Some(ty) = non_path_frame.get(ident) {
return Some(ty.clone());
}
if let Some(segs) = path_frame.get(ident) {
return Some(CanonicalType::Path(segs.clone()));
}
}
None
}
}
pub(super) fn extract_pat_ident_name(pat: &syn::Pat) -> Option<String> {
match pat {
syn::Pat::Ident(pi) => Some(pi.ident.to_string()),
syn::Pat::Type(pt) => extract_pat_ident_name(&pt.pat),
_ => None,
}
}
pub(super) fn collect_pattern_idents(pat: &syn::Pat, out: &mut Vec<String>) {
match pat {
syn::Pat::Ident(pi) => push_pat_ident(pi, out),
syn::Pat::Type(pt) => collect_pattern_idents(&pt.pat, out),
syn::Pat::Reference(r) => collect_pattern_idents(&r.pat, out),
syn::Pat::Paren(p) => collect_pattern_idents(&p.pat, out),
syn::Pat::Tuple(t) => walk_each(t.elems.iter(), out),
syn::Pat::TupleStruct(ts) => walk_each(ts.elems.iter(), out),
syn::Pat::Struct(s) => walk_each(s.fields.iter().map(|f| f.pat.as_ref()), out),
syn::Pat::Slice(s) => walk_each(s.elems.iter(), out),
syn::Pat::Or(o) => walk_each(o.cases.iter().take(1), out),
_ => {}
}
}
fn walk_each<'p, I: Iterator<Item = &'p syn::Pat>>(iter: I, out: &mut Vec<String>) {
for p in iter {
collect_pattern_idents(p, out);
}
}
fn push_pat_ident(pi: &syn::PatIdent, out: &mut Vec<String>) {
out.push(pi.ident.to_string());
if let Some((_, sub)) = &pi.subpat {
collect_pattern_idents(sub, out);
}
}
fn bare(path: &str) -> String {
format!("{BARE_UNKNOWN_PREFIX}{path}")
}
fn method_unknown(method: &str) -> String {
format!("{METHOD_UNKNOWN_PREFIX}{method}")
}