use super::edges::type_probe;
use super::index::DefIndex;
use super::ladder::{resolve_ref, Resolution};
use super::members::{declares_member, first_base_declaring};
use super::scope::FileContext;
use crate::extract::RegistrationRecord;
use crate::graph::{Edge, EdgesByKind, Fragment};
use std::collections::HashMap;
#[derive(Debug, Clone, Copy)]
pub(super) struct ResolvedRegistration {
pub implementation: usize,
pub service: usize,
}
fn resolve_registered_type(
raw: &str,
ns: &str,
file_ctx: &FileContext,
index: &DefIndex,
file_contexts: &HashMap<String, FileContext>,
) -> Option<usize> {
let probe = type_probe(raw, ns);
match resolve_ref(
&probe,
&file_ctx.usings,
ns,
index,
&file_ctx.aliases,
file_contexts,
) {
Resolution::Resolved(idx, _) => Some(idx),
Resolution::Ambiguous(..) | Resolution::External => None,
}
}
pub(super) fn resolve_registrations(
fragments_by_file: &[(String, Fragment)],
index: &DefIndex,
file_contexts: &HashMap<String, FileContext>,
) -> (Vec<Edge>, Vec<ResolvedRegistration>) {
let mut edges = Vec::new();
let mut resolved = Vec::new();
for (file, frag) in fragments_by_file {
if frag.registrations.is_empty() {
continue;
}
let Some(file_ctx) = file_contexts.get(file) else {
continue;
};
for reg in &frag.registrations {
let RegistrationRecord {
service,
implementation,
namespace,
line: _,
} = registration_as_record(reg);
let Some(service_idx) =
resolve_registered_type(&service, &namespace, file_ctx, index, file_contexts)
else {
continue;
};
let Some(impl_idx) = resolve_registered_type(
&implementation,
&namespace,
file_ctx,
index,
file_contexts,
) else {
continue;
};
let impl_def = &index.defs[impl_idx];
let service_def = &index.defs[service_idx];
edges.push(Edge::Implements {
from_file: impl_def.file.clone(),
from_line: impl_def.line,
to: service_def.id.clone(),
to_file: service_def.file.clone(),
member: None,
});
resolved.push(ResolvedRegistration {
implementation: impl_idx,
service: service_idx,
});
}
}
(edges, resolved)
}
fn registration_as_record(reg: &crate::graph::FragRegistration) -> RegistrationRecord {
RegistrationRecord {
service: reg.service.clone(),
implementation: reg.implementation.clone(),
namespace: reg.namespace.clone(),
line: reg.line,
}
}
fn matching_overload_count(index: &DefIndex, source: usize, target: usize, member: &str) -> usize {
let Some(source_ranges) = index.member_lists[source].method_arities.get(member) else {
return 0;
};
let Some(target_ranges) = index.member_lists[target].method_arities.get(member) else {
return 0;
};
source_ranges
.iter()
.map(|r| target_ranges.iter().filter(|t| *t == r).count())
.sum()
}
fn resolve_member_implements(index: &DefIndex, reg: ResolvedRegistration) -> (Vec<Edge>, usize) {
let mut edges = Vec::new();
let mut count = 0;
let service_def = &index.defs[reg.service];
for member in service_def.methods.clone() {
if matching_overload_count(index, reg.implementation, reg.service, &member) != 1 {
continue;
}
let impl_def = &index.defs[reg.implementation];
edges.push(Edge::Implements {
from_file: impl_def.file.clone(),
from_line: impl_def.line,
to: service_def.id.clone(),
to_file: service_def.file.clone(),
member: Some(member),
});
count += 1;
}
(edges, count)
}
fn resolve_member_overrides(
index: &DefIndex,
file_contexts: &HashMap<String, FileContext>,
impl_idx: usize,
) -> (Vec<Edge>, usize) {
let mut edges = Vec::new();
let mut count = 0;
for member in index.member_lists[impl_idx].override_methods.clone() {
let Some(base_idx) =
first_base_declaring(index, file_contexts, impl_idx, false, |idx, i| {
declares_member(idx, i, Some(&member), None)
})
else {
continue;
};
if matching_overload_count(index, impl_idx, base_idx, &member) != 1 {
continue;
}
let impl_def = &index.defs[impl_idx];
let base_def = &index.defs[base_idx];
edges.push(Edge::Overrides {
from_file: impl_def.file.clone(),
from_line: impl_def.line,
to: base_def.id.clone(),
to_file: base_def.file.clone(),
member: Some(member),
});
count += 1;
}
(edges, count)
}
pub(super) fn resolve_member_dispatch(
index: &DefIndex,
file_contexts: &HashMap<String, FileContext>,
registrations: &[ResolvedRegistration],
) -> (Vec<Edge>, usize, usize) {
let mut edges = Vec::new();
let mut implements_count = 0;
let mut overrides_count = 0;
let mut seen_impls: std::collections::HashSet<usize> = std::collections::HashSet::new();
for ® in registrations {
let (member_edges, member_count) = resolve_member_implements(index, reg);
edges.extend(member_edges);
implements_count += member_count;
if seen_impls.insert(reg.implementation) {
let (override_edges, override_count) =
resolve_member_overrides(index, file_contexts, reg.implementation);
edges.extend(override_edges);
overrides_count += override_count;
}
}
(edges, implements_count, overrides_count)
}
pub(super) fn append_dispatch_edges(
fragments_by_file: &[(String, Fragment)],
index: &DefIndex,
file_contexts: &HashMap<String, FileContext>,
edges: &mut Vec<Edge>,
edges_by_kind: &mut EdgesByKind,
) {
let (registration_edges, resolved) =
resolve_registrations(fragments_by_file, index, file_contexts);
edges_by_kind.implements += registration_edges.len();
edges.extend(registration_edges);
let (member_edges, implements, overrides) =
resolve_member_dispatch(index, file_contexts, &resolved);
edges_by_kind.implements += implements;
edges_by_kind.overrides += overrides;
edges.extend(member_edges);
}