use super::super::bindings::{
canonical_from_type, extract_let_binding, normalize_alias_expansion, CanonScope,
};
use super::super::local_symbols::{scope_for_local, FileScope};
use super::super::type_infer::resolve::{resolve_type, ResolveContext};
use super::super::type_infer::self_subst::substitute_bare_self;
use super::super::type_infer::{
extract_bindings, extract_for_bindings, infer_type, BindingLookup, CanonicalType, InferContext,
WorkspaceTypeIndex,
};
use super::{
bare, collect_pattern_idents, extract_pat_ident_name, method_unknown, parse_macro_tokens,
CanonicalCallCollector, CollectorBindings, FnContext,
};
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::spanned::Spanned;
impl<'a> CanonicalCallCollector<'a> {
pub(super) fn record_call(&mut self, target: String) {
self.calls.insert(target);
}
pub(super) fn resolve_method_targets(
&self,
receiver: &syn::Expr,
method_name: &str,
) -> Vec<String> {
if let Some(c) = self.try_fast_path_receiver(receiver, method_name) {
return vec![c];
}
self.try_inferred_targets(receiver, method_name)
}
pub(super) fn try_fast_path_receiver(
&self,
receiver: &syn::Expr,
method_name: &str,
) -> Option<String> {
let syn::Expr::Path(p) = receiver else {
return None;
};
if p.path.segments.len() != 1 {
return None;
}
let ident = p.path.segments[0].ident.to_string();
for (path_scope, non_path_scope) in self
.bindings
.iter()
.rev()
.zip(self.non_path_bindings.iter().rev())
{
if non_path_scope.contains_key(&ident) {
return None;
}
if let Some(binding) = path_scope.get(&ident) {
let mut full = binding.clone();
full.push(method_name.to_string());
return Some(full.join("::"));
}
}
None
}
pub(super) fn try_inferred_targets(
&self,
receiver: &syn::Expr,
method_name: &str,
) -> Vec<String> {
let Some(workspace) = self.workspace_index else {
return Vec::new();
};
let Some(inferred) = self.infer_receiver_type(receiver) else {
return Vec::new();
};
canonical_edges_for_method(&inferred, method_name, workspace)
}
pub(super) fn infer_receiver_type(&self, expr: &syn::Expr) -> Option<CanonicalType> {
let adapter = CollectorBindings {
scope: &self.bindings,
non_path_scope: &self.non_path_bindings,
};
let ctx = InferContext {
file: self.file,
mod_stack: self.mod_stack,
workspace: self.workspace_index?,
bindings: &adapter,
self_type: self.self_type_canonical.clone(),
workspace_files: self.workspace_files,
generic_params: Some(&self.generic_params),
reexports: self.reexports,
};
infer_type(expr, &ctx)
}
}
pub(super) fn canonical_edges_for_method(
ty: &CanonicalType,
method: &str,
workspace: &WorkspaceTypeIndex,
) -> Vec<String> {
if let Some(bounds) = ty.as_trait_bounds() {
return bounds
.iter()
.flat_map(|trait_segs| trait_dispatch_edges(trait_segs, method, workspace))
.collect();
}
match ty {
CanonicalType::Path(segs) => {
let mut full = segs.clone();
full.push(method.to_string());
vec![full.join("::")]
}
_ => Vec::new(),
}
}
pub(super) fn trait_dispatch_edges(
trait_segs: &[String],
method: &str,
workspace: &WorkspaceTypeIndex,
) -> Vec<String> {
let trait_canonical = trait_segs.join("::");
if !workspace.trait_has_method(&trait_canonical, method) {
return Vec::new();
}
vec![format!("{trait_canonical}::{method}")]
}