mago_reflection/class_like/
inheritance.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
use ahash::HashMap;
use ahash::HashSet;

use mago_interner::StringIdentifier;
use mago_interner::ThreadedInterner;
use serde::Deserialize;
use serde::Serialize;

use crate::identifier::ClassLikeName;
use crate::identifier::Name;

use super::ClassLikeReflection;

/// Represents the inheritance details of a class-like entity, including implemented interfaces,
/// extended classes or interfaces, and any required inheritance constraints.
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize, Default)]
pub struct InheritanceReflection {
    /// Interfaces directly implemented by the current class or enum.
    pub direct_implemented_interfaces: HashSet<Name>,

    /// All interfaces implemented by the current class or any of its ancestors,
    /// including `direct_implemented_interfaces`.
    pub all_implemented_interfaces: HashSet<Name>,

    /// The class directly extended by the current class, if applicable.
    pub direct_extended_class: Option<Name>,

    /// All classes extended by the current class or any of its ancestors,
    /// including `direct_extended_class`.
    pub all_extended_classes: HashSet<Name>,

    /// Interfaces directly extended by the current interface, if applicable.
    pub direct_extended_interfaces: HashSet<Name>,

    /// All interfaces extended by the current interface or any of its ancestors,
    /// including `direct_extended_interfaces`.
    pub all_extended_interfaces: HashSet<Name>,

    /// Interfaces that the current class-like entity requires any inheriting entity to implement,
    /// as specified by the `@require-implements` tag.
    pub require_implementations: HashSet<StringIdentifier>,

    /// Classes or interfaces that the current class-like entity requires any inheriting entity to extend,
    /// as specified by the `@require-extends` tag.
    pub require_extensions: HashSet<StringIdentifier>,

    /// Identifiers of class-like entities that directly extend or implement the current class-like entity.
    pub children: HashSet<ClassLikeName>,

    /// A lookup map of string identifiers to class-like names.
    pub names: HashMap<StringIdentifier, Name>,
}

impl InheritanceReflection {
    #[inline]
    pub fn implements_interfaces(&self) -> bool {
        !self.all_implemented_interfaces.is_empty()
    }

    #[inline]
    pub fn extends_classes(&self) -> bool {
        !self.all_extended_classes.is_empty()
    }

    #[inline]
    pub fn has_children(&self) -> bool {
        !self.children.is_empty()
    }

    #[inline]
    pub fn is_instance_of(&self, interner: &ThreadedInterner, other: &ClassLikeReflection) -> bool {
        let Some(other_name) = other.name.inner() else {
            return false;
        };

        let other_identifier = interner.lowered(&other_name.value);
        if let Some(this_name) = self.direct_extended_class {
            let this_identifier = interner.lowered(&this_name.value);
            if this_identifier == other_identifier {
                return true;
            }
        }

        let Some(other_fqcn) = self.names.get(&other_identifier) else {
            return false;
        };

        self.all_extended_classes.contains(other_fqcn)
            || self.all_implemented_interfaces.contains(other_fqcn)
            || self.all_extended_interfaces.contains(other_fqcn)
    }

    #[inline]
    pub fn extends_class(&self, interner: &ThreadedInterner, other: &ClassLikeReflection) -> bool {
        let Some(name) = other.name.inner() else {
            return false; // we can't extend a class that does not have a name, i.e. anonymous class
        };

        self.extends_class_with_name(interner, &name.value)
    }

    #[inline]
    pub fn extends_class_with_name(&self, interner: &ThreadedInterner, other: &StringIdentifier) -> bool {
        let identifier = interner.lowered(other);
        let Some(other) = self.names.get(&identifier) else {
            return false;
        };

        self.all_extended_classes.contains(other)
    }

    #[inline]
    pub fn extends_interface(&self, interner: &ThreadedInterner, other: &ClassLikeReflection) -> bool {
        let Some(name) = other.name.inner() else {
            return false;
        };

        self.extends_interface_with_name(interner, &name.value)
    }

    #[inline]
    pub fn extends_interface_with_name(&self, interner: &ThreadedInterner, other: &StringIdentifier) -> bool {
        let identifier = interner.lowered(other);
        let Some(other) = self.names.get(&identifier) else {
            return false;
        };

        self.all_extended_interfaces.contains(other)
    }

    #[inline]
    pub fn implements_interface(&self, interner: &ThreadedInterner, other: &ClassLikeReflection) -> bool {
        let Some(name) = other.name.inner() else {
            return false;
        };

        self.implements_interface_with_name(interner, &name.value)
    }

    #[inline]
    pub fn implements_interface_with_name(&self, interner: &ThreadedInterner, other: &StringIdentifier) -> bool {
        let identifier = interner.lowered(other);
        let Some(other) = self.names.get(&identifier) else {
            return false;
        };

        self.all_implemented_interfaces.contains(other)
    }
}