use foldhash::HashMap;
use mago_word::Word;
use mago_word::word;
use crate::identifier::method::MethodIdentifier;
use crate::metadata::CodebaseMetadata;
use crate::metadata::class_like::ClassLikeMetadata;
pub fn inherit_methods_from_parent(
metadata: &mut ClassLikeMetadata,
parent_metadata: &ClassLikeMetadata,
codebase: &CodebaseMetadata,
) {
let class_like_name = metadata.name;
let parent_is_trait = parent_metadata.kind.is_trait();
let reverse_alias_map: Option<HashMap<Word, Vec<Word>>> = if parent_is_trait && !metadata.trait_alias_map.is_empty()
{
let mut map: HashMap<Word, Vec<Word>> = HashMap::default();
for (original, alias) in metadata.get_trait_alias_map() {
map.entry(*original).or_default().push(*alias);
}
Some(map)
} else {
None
};
for (method_name_lc, appearing_method_id) in &parent_metadata.appearing_method_ids {
let mut process_name = |aliased_method_name: Word| {
if metadata.has_appearing_method(aliased_method_name) {
return;
}
let implemented_method_id = MethodIdentifier::new(class_like_name, aliased_method_name);
let final_appearing_id = if parent_is_trait { implemented_method_id } else { *appearing_method_id };
metadata.appearing_method_ids.insert(aliased_method_name, final_appearing_id);
};
process_name(*method_name_lc);
if let Some(reverse_map) = reverse_alias_map.as_ref()
&& let Some(aliases) = reverse_map.get(method_name_lc)
{
for alias in aliases.iter().copied() {
process_name(alias);
}
}
}
for (method_name_lc, declaring_method_id) in &parent_metadata.inheritable_method_ids {
if !method_name_lc.eq(&word("__construct")) || parent_metadata.flags.has_consistent_constructor() {
if parent_is_trait {
let declaring_class = declaring_method_id.get_class_name();
if codebase
.function_likes
.get(&(declaring_class, *method_name_lc))
.and_then(|meta| meta.method_metadata.as_ref())
.is_some_and(|method| method.is_abstract)
{
metadata.add_overridden_method_parent(*method_name_lc, *declaring_method_id);
}
} else {
metadata.add_overridden_method_parent(*method_name_lc, *declaring_method_id);
}
if let Some(existing_overridden) = metadata.overridden_method_ids.get_mut(method_name_lc)
&& let Some(parent_overridden_map) = parent_metadata.overridden_method_ids.get(method_name_lc)
{
existing_overridden.extend(parent_overridden_map.iter().map(|(k, v)| (*k, *v)));
}
}
let process_name = |aliased_method_name: Word, metadata: &mut ClassLikeMetadata| {
if let Some(implementing_method_id) = metadata.declaring_method_ids.get(&aliased_method_name) {
let implementing_class = implementing_method_id.get_class_name();
let implementing_method_name = implementing_method_id.get_method_name();
let is_existing_pseudo_from_trait = !parent_is_trait
&& codebase.class_likes.get(&implementing_class).is_some_and(|c| c.kind.is_trait())
&& codebase
.function_likes
.get(&(implementing_class, implementing_method_name))
.is_some_and(|m| m.flags.is_magic_method());
if !is_existing_pseudo_from_trait
&& (!codebase
.method_is_abstract(implementing_class.as_bytes(), implementing_method_name.as_bytes())
|| implementing_class == class_like_name)
{
return;
}
}
metadata.declaring_method_ids.insert(aliased_method_name, *declaring_method_id);
metadata.inheritable_method_ids.insert(aliased_method_name, *declaring_method_id);
};
process_name(*method_name_lc, metadata);
if let Some(reverse_map) = reverse_alias_map.as_ref()
&& let Some(aliases) = reverse_map.get(method_name_lc)
{
for alias in aliases.iter().copied() {
process_name(alias, metadata);
}
}
}
}