use foldhash::HashMap;
use foldhash::fast::RandomState;
use indexmap::IndexMap;
use serde::Deserialize;
use serde::Serialize;
use mago_atom::Atom;
use mago_atom::AtomMap;
use mago_atom::AtomSet;
use mago_reporting::Issue;
use mago_span::Span;
use crate::flags::attribute::AttributeFlags;
use crate::identifier::method::MethodIdentifier;
use crate::metadata::attribute::AttributeMetadata;
use crate::metadata::class_like_constant::ClassLikeConstantMetadata;
use crate::metadata::enum_case::EnumCaseMetadata;
use crate::metadata::flags::MetadataFlags;
use crate::metadata::property::PropertyMetadata;
use crate::metadata::ttype::TypeMetadata;
use crate::symbol::SymbolKind;
use crate::ttype::atomic::TAtomic;
use crate::ttype::template::GenericTemplate;
use crate::ttype::template::variance::Variance;
use crate::ttype::union::TUnion;
use crate::visibility::Visibility;
pub type TemplateTypes = IndexMap<Atom, GenericTemplate, RandomState>;
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct ClassLikeMetadata {
pub name: Atom,
pub original_name: Atom,
pub span: Span,
pub direct_parent_interfaces: AtomSet,
pub all_parent_interfaces: AtomSet,
pub direct_parent_class: Option<Atom>,
pub require_extends: AtomSet,
pub require_implements: AtomSet,
pub all_parent_classes: AtomSet,
pub used_traits: AtomSet,
pub trait_alias_map: AtomMap<Atom>,
pub trait_visibility_map: AtomMap<Visibility>,
pub trait_final_map: AtomSet,
pub child_class_likes: Option<AtomSet>,
pub name_span: Option<Span>,
pub kind: SymbolKind,
pub template_types: TemplateTypes,
pub template_readonly: AtomSet,
pub template_variance: HashMap<usize, Variance>,
pub template_extended_offsets: AtomMap<Vec<TUnion>>,
pub template_extended_parameters: AtomMap<IndexMap<Atom, TUnion, RandomState>>,
pub template_type_extends_count: AtomMap<usize>,
pub template_type_implements_count: AtomMap<usize>,
pub template_type_uses_count: AtomMap<usize>,
pub methods: AtomSet,
pub pseudo_methods: AtomSet,
pub static_pseudo_methods: AtomSet,
pub declaring_method_ids: AtomMap<MethodIdentifier>,
pub appearing_method_ids: AtomMap<MethodIdentifier>,
pub inheritable_method_ids: AtomMap<MethodIdentifier>,
pub overridden_method_ids: AtomMap<IndexMap<Atom, MethodIdentifier, RandomState>>,
pub properties: AtomMap<PropertyMetadata>,
pub appearing_property_ids: AtomMap<Atom>,
pub declaring_property_ids: AtomMap<Atom>,
pub inheritable_property_ids: AtomMap<Atom>,
pub overridden_property_ids: AtomMap<AtomSet>,
pub initialized_properties: AtomSet,
pub constants: AtomMap<ClassLikeConstantMetadata>,
pub trait_constant_ids: AtomMap<Atom>,
pub enum_cases: AtomMap<EnumCaseMetadata>,
pub invalid_dependencies: AtomSet,
pub attributes: Vec<AttributeMetadata>,
pub enum_type: Option<TAtomic>,
pub has_sealed_methods: Option<bool>,
pub has_sealed_properties: Option<bool>,
pub permitted_inheritors: Option<AtomSet>,
pub issues: Vec<Issue>,
pub attribute_flags: Option<AttributeFlags>,
pub flags: MetadataFlags,
pub type_aliases: AtomMap<TypeMetadata>,
pub imported_type_aliases: AtomMap<(Atom, Atom, Span)>,
pub mixins: Vec<TUnion>,
}
impl ClassLikeMetadata {
#[must_use]
pub fn new(
name: Atom,
original_name: Atom,
span: Span,
name_span: Option<Span>,
flags: MetadataFlags,
) -> ClassLikeMetadata {
ClassLikeMetadata {
constants: AtomMap::default(),
trait_constant_ids: AtomMap::default(),
enum_cases: AtomMap::default(),
flags,
kind: SymbolKind::Class,
direct_parent_interfaces: AtomSet::default(),
all_parent_classes: AtomSet::default(),
appearing_method_ids: AtomMap::default(),
attributes: Vec::new(),
all_parent_interfaces: AtomSet::default(),
declaring_method_ids: AtomMap::default(),
appearing_property_ids: AtomMap::default(),
declaring_property_ids: AtomMap::default(),
direct_parent_class: None,
require_extends: AtomSet::default(),
require_implements: AtomSet::default(),
inheritable_method_ids: AtomMap::default(),
enum_type: None,
inheritable_property_ids: AtomMap::default(),
initialized_properties: AtomSet::default(),
invalid_dependencies: AtomSet::default(),
span,
name_span,
methods: AtomSet::default(),
pseudo_methods: AtomSet::default(),
static_pseudo_methods: AtomSet::default(),
overridden_method_ids: AtomMap::default(),
overridden_property_ids: AtomMap::default(),
properties: AtomMap::default(),
template_variance: HashMap::default(),
template_type_extends_count: AtomMap::default(),
template_extended_parameters: AtomMap::default(),
template_extended_offsets: AtomMap::default(),
template_type_implements_count: AtomMap::default(),
template_type_uses_count: AtomMap::default(),
template_types: TemplateTypes::default(),
used_traits: AtomSet::default(),
trait_alias_map: AtomMap::default(),
trait_visibility_map: AtomMap::default(),
trait_final_map: AtomSet::default(),
name,
original_name,
child_class_likes: None,
template_readonly: AtomSet::default(),
has_sealed_methods: None,
has_sealed_properties: None,
permitted_inheritors: None,
issues: vec![],
attribute_flags: None,
type_aliases: AtomMap::default(),
imported_type_aliases: AtomMap::default(),
mixins: Vec::default(),
}
}
#[inline]
#[must_use]
pub fn get_trait_alias_map(&self) -> &AtomMap<Atom> {
&self.trait_alias_map
}
#[inline]
#[must_use]
pub fn get_template_type_names(&self) -> Vec<Atom> {
self.template_types.keys().copied().collect()
}
#[inline]
#[must_use]
pub fn get_template_type(&self, name: Atom) -> Option<&GenericTemplate> {
self.template_types.get(&name)
}
#[inline]
#[must_use]
pub fn get_template_type_with_index(&self, name: Atom) -> Option<(usize, &GenericTemplate)> {
self.template_types.get_full(&name).map(|(index, _, types)| (index, types))
}
#[must_use]
pub fn get_template_for_index(&self, index: usize) -> Option<(Atom, &GenericTemplate)> {
self.template_types.get_index(index).map(|(name, types)| (*name, types))
}
#[must_use]
pub fn get_template_name_for_index(&self, index: usize) -> Option<Atom> {
self.template_types.get_index(index).map(|(name, _)| *name)
}
#[must_use]
pub fn get_template_index_for_name(&self, name: Atom) -> Option<usize> {
self.template_types.get_index_of(&name)
}
#[inline]
#[must_use]
pub fn has_parent(&self, parent: Atom) -> bool {
self.all_parent_classes.contains(&parent) || self.all_parent_interfaces.contains(&parent)
}
#[inline]
#[must_use]
pub fn has_template_extended_parameter(&self, parent: Atom) -> bool {
self.template_extended_parameters.contains_key(&parent)
}
#[inline]
#[must_use]
pub fn has_appearing_method(&self, method: Atom) -> bool {
self.appearing_method_ids.contains_key(&method)
}
#[inline]
#[must_use]
pub fn get_property_names(&self) -> AtomSet {
self.properties.keys().copied().collect()
}
#[inline]
#[must_use]
pub fn has_appearing_property(&self, name: Atom) -> bool {
self.appearing_property_ids.contains_key(&name)
}
#[inline]
#[must_use]
pub fn has_declaring_property(&self, name: Atom) -> bool {
self.declaring_property_ids.contains_key(&name)
}
#[inline]
pub fn take_issues(&mut self) -> Vec<Issue> {
std::mem::take(&mut self.issues)
}
#[inline]
pub fn add_direct_parent_interface(&mut self, interface: Atom) {
self.direct_parent_interfaces.insert(interface);
self.all_parent_interfaces.insert(interface);
}
#[inline]
pub fn add_all_parent_interface(&mut self, interface: Atom) {
self.all_parent_interfaces.insert(interface);
}
#[inline]
pub fn add_all_parent_interfaces(&mut self, interfaces: impl IntoIterator<Item = Atom>) {
self.all_parent_interfaces.extend(interfaces);
}
#[inline]
pub fn add_all_parent_classes(&mut self, classes: impl IntoIterator<Item = Atom>) {
self.all_parent_classes.extend(classes);
}
#[inline]
pub fn add_used_trait(&mut self, trait_name: Atom) -> bool {
self.used_traits.insert(trait_name)
}
#[inline]
pub fn add_used_traits(&mut self, traits: impl IntoIterator<Item = Atom>) {
self.used_traits.extend(traits);
}
#[inline]
pub fn add_trait_alias(&mut self, method: Atom, alias: Atom) -> Option<Atom> {
self.trait_alias_map.insert(method, alias)
}
#[inline]
pub fn add_trait_visibility(&mut self, method: Atom, visibility: Visibility) -> Option<Visibility> {
self.trait_visibility_map.insert(method, visibility)
}
#[inline]
pub fn add_template_type(&mut self, name: Atom, constraint: GenericTemplate) {
self.template_types.insert(name, constraint);
}
#[inline]
pub fn add_template_variance_parameter(&mut self, index: usize, variance: Variance) -> Option<Variance> {
self.template_variance.insert(index, variance)
}
#[inline]
pub fn add_template_extended_offset(&mut self, name: Atom, types: Vec<TUnion>) -> Option<Vec<TUnion>> {
self.template_extended_offsets.insert(name, types)
}
#[inline]
pub fn extend_template_extended_parameters(
&mut self,
template_extended_parameters: AtomMap<IndexMap<Atom, TUnion, RandomState>>,
) {
self.template_extended_parameters.extend(template_extended_parameters);
}
#[inline]
pub fn add_template_extended_parameter(
&mut self,
parent_fqcn: Atom,
parameter_name: Atom,
parameter_type: TUnion,
) -> Option<TUnion> {
self.template_extended_parameters.entry(parent_fqcn).or_default().insert(parameter_name, parameter_type)
}
#[inline]
pub fn add_declaring_method_id(
&mut self,
method: Atom,
declaring_method_id: MethodIdentifier,
) -> Option<MethodIdentifier> {
self.add_appearing_method_id(method, declaring_method_id);
self.declaring_method_ids.insert(method, declaring_method_id)
}
#[inline]
pub fn add_appearing_method_id(
&mut self,
method: Atom,
appearing_method_id: MethodIdentifier,
) -> Option<MethodIdentifier> {
self.appearing_method_ids.insert(method, appearing_method_id)
}
#[inline]
pub fn add_overridden_method_parent(
&mut self,
method: Atom,
parent_method_id: MethodIdentifier,
) -> Option<MethodIdentifier> {
self.overridden_method_ids
.entry(method)
.or_default()
.insert(parent_method_id.get_class_name(), parent_method_id)
}
#[inline]
pub fn add_property(&mut self, name: Atom, property_metadata: PropertyMetadata) -> Option<PropertyMetadata> {
let class_name = self.name;
self.add_declaring_property_id(name, class_name);
if property_metadata.flags.has_default() {
self.initialized_properties.insert(name);
}
if !property_metadata.is_final() {
self.inheritable_property_ids.insert(name, class_name);
}
self.properties.insert(name, property_metadata)
}
#[inline]
pub fn add_property_metadata(&mut self, property_metadata: PropertyMetadata) -> Option<PropertyMetadata> {
let name = property_metadata.get_name().0;
self.add_property(name, property_metadata)
}
#[inline]
pub fn add_declaring_property_id(&mut self, prop: Atom, declaring_fqcn: Atom) -> Option<Atom> {
self.appearing_property_ids.insert(prop, declaring_fqcn);
self.declaring_property_ids.insert(prop, declaring_fqcn)
}
#[must_use]
pub fn get_missing_required_interface<'a>(&self, other: &'a ClassLikeMetadata) -> Option<&'a Atom> {
for required_interface in &other.require_implements {
if self.all_parent_interfaces.contains(required_interface) {
continue;
}
if (self.flags.is_abstract() || self.kind.is_trait())
&& self.require_implements.contains(required_interface)
{
continue; }
return Some(required_interface);
}
None
}
#[must_use]
pub fn get_missing_required_extends<'a>(&self, other: &'a ClassLikeMetadata) -> Option<&'a Atom> {
for required_extend in &other.require_extends {
if self.all_parent_classes.contains(required_extend) {
continue;
}
if self.kind.is_interface() && self.all_parent_interfaces.contains(required_extend) {
continue;
}
if (self.flags.is_abstract() || self.kind.is_trait()) && self.require_extends.contains(required_extend) {
continue; }
return Some(required_extend);
}
None
}
#[must_use]
pub fn is_permitted_to_inherit(&self, other: &ClassLikeMetadata) -> bool {
if self.kind.is_trait() || self.flags.is_abstract() {
return true; }
let Some(permitted_inheritors) = &other.permitted_inheritors else {
return true; };
if permitted_inheritors.contains(&self.name) {
return true; }
self.all_parent_interfaces.iter().any(|parent_interface| permitted_inheritors.contains(parent_interface))
|| self.all_parent_classes.iter().any(|parent_class| permitted_inheritors.contains(parent_class))
|| self.used_traits.iter().any(|used_trait| permitted_inheritors.contains(used_trait))
}
#[inline]
pub fn mark_as_populated(&mut self) {
self.flags |= MetadataFlags::POPULATED;
self.shrink_to_fit();
}
#[inline]
pub fn shrink_to_fit(&mut self) {
self.properties.shrink_to_fit();
self.initialized_properties.shrink_to_fit();
self.appearing_property_ids.shrink_to_fit();
self.declaring_property_ids.shrink_to_fit();
self.inheritable_property_ids.shrink_to_fit();
self.overridden_property_ids.shrink_to_fit();
self.appearing_method_ids.shrink_to_fit();
self.declaring_method_ids.shrink_to_fit();
self.inheritable_method_ids.shrink_to_fit();
self.overridden_method_ids.shrink_to_fit();
self.attributes.shrink_to_fit();
self.constants.shrink_to_fit();
self.enum_cases.shrink_to_fit();
self.type_aliases.shrink_to_fit();
}
}