use std::collections::BTreeSet;
use crate::compat::added_lines::{AddedLinesSubject, file_line};
use crate::compat::qualified_call_resolve::{ModuleProvenance, TreeReader, classify_module};
use crate::compat::source_attributes::{
extract_behaviours, extract_callbacks, extract_optional_callbacks,
};
use crate::model::names::{ModuleName, RelativePath};
use crate::model::verdict::Reason;
pub fn analyse_behaviour_callbacks(
subjects: &[AddedLinesSubject<'_>],
covered_modules: &BTreeSet<ModuleName>,
resolve_module_path: &dyn Fn(&ModuleName) -> Option<RelativePath>,
read_target: TreeReader<'_>,
read_source: Option<TreeReader<'_>>,
) -> Vec<Reason> {
let Some(read_source) = read_source else {
return Vec::new();
};
let mut reasons = Vec::new();
for subject in subjects {
let mut seen = BTreeSet::new();
for declared in extract_behaviours(subject.added_text) {
if !seen.insert(declared.behaviour.clone()) {
continue;
}
let ModuleProvenance::FirstParty { path } =
classify_module(&declared.behaviour, covered_modules, resolve_module_path)
else {
continue;
};
let (Some(target_text), Some(source_text)) = (read_target(&path), read_source(&path))
else {
continue;
};
let target_callbacks = extract_callbacks(&target_text);
if target_callbacks.is_empty() {
continue;
}
let source_callbacks = extract_callbacks(&source_text);
let optional = extract_optional_callbacks(&target_text);
let line = file_line(subject.line_map, declared.line);
for (key, target_signature) in &target_callbacks {
match source_callbacks.get(key) {
None if !optional.contains(key) => {
reasons.push(Reason::BehaviourCallbackAddedOnTarget {
source_path: subject.source_path.clone(),
behaviour: declared.behaviour.clone(),
callback: key.0.clone(),
arity: key.1,
line,
});
}
Some(source_signature) if source_signature != target_signature => {
reasons.push(Reason::BehaviourCallbackDriftOnTarget {
source_path: subject.source_path.clone(),
behaviour: declared.behaviour.clone(),
callback: key.0.clone(),
arity: key.1,
source_signature: source_signature.clone(),
target_signature: target_signature.clone(),
line,
});
}
_ => {}
}
}
}
}
reasons
}