Skip to main content

mir_codebase/
storage.rs

1use std::sync::Arc;
2
3use indexmap::IndexMap;
4use mir_types::Union;
5use serde::{Deserialize, Serialize};
6
7// ---------------------------------------------------------------------------
8// Shared primitives
9// ---------------------------------------------------------------------------
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
12pub enum Visibility {
13    Public,
14    Protected,
15    Private,
16}
17
18impl Visibility {
19    pub fn is_at_least(&self, required: Visibility) -> bool {
20        *self <= required
21    }
22}
23
24impl std::fmt::Display for Visibility {
25    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
26        match self {
27            Visibility::Public => write!(f, "public"),
28            Visibility::Protected => write!(f, "protected"),
29            Visibility::Private => write!(f, "private"),
30        }
31    }
32}
33
34#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
35pub struct TemplateParam {
36    pub name: Arc<str>,
37    pub bound: Option<Union>,
38    /// The entity (class or function FQN) that declared this template param.
39    pub defining_entity: Arc<str>,
40}
41
42#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
43pub struct FnParam {
44    pub name: Arc<str>,
45    pub ty: Option<Union>,
46    pub default: Option<Union>,
47    pub is_variadic: bool,
48    pub is_byref: bool,
49    pub is_optional: bool,
50}
51
52// ---------------------------------------------------------------------------
53// Location — file + byte offsets
54// ---------------------------------------------------------------------------
55
56#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
57pub struct Location {
58    pub file: Arc<str>,
59    /// Byte offset of the start of the declaration in the source.
60    pub start: u32,
61    pub end: u32,
62}
63
64impl Location {
65    pub fn new(file: Arc<str>, start: u32, end: u32) -> Self {
66        Self { file, start, end }
67    }
68}
69
70// ---------------------------------------------------------------------------
71// Assertion — `@psalm-assert`, `@psalm-assert-if-true`, etc.
72// ---------------------------------------------------------------------------
73
74#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
75pub enum AssertionKind {
76    Assert,
77    AssertIfTrue,
78    AssertIfFalse,
79}
80
81#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
82pub struct Assertion {
83    pub kind: AssertionKind,
84    pub param: Arc<str>,
85    pub ty: Union,
86}
87
88// ---------------------------------------------------------------------------
89// MethodStorage
90// ---------------------------------------------------------------------------
91
92#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
93pub struct MethodStorage {
94    pub name: Arc<str>,
95    pub fqcn: Arc<str>,
96    pub params: Vec<FnParam>,
97    /// Type from annotation (`@return` / native type hint). `None` means unannotated.
98    pub return_type: Option<Union>,
99    /// Type inferred from body analysis (filled in during pass 2).
100    pub inferred_return_type: Option<Union>,
101    pub visibility: Visibility,
102    pub is_static: bool,
103    pub is_abstract: bool,
104    pub is_final: bool,
105    pub is_constructor: bool,
106    pub template_params: Vec<TemplateParam>,
107    pub assertions: Vec<Assertion>,
108    pub throws: Vec<Arc<str>>,
109    pub is_deprecated: bool,
110    pub is_internal: bool,
111    pub is_pure: bool,
112    pub location: Option<Location>,
113}
114
115impl MethodStorage {
116    pub fn effective_return_type(&self) -> Option<&Union> {
117        self.return_type
118            .as_ref()
119            .or(self.inferred_return_type.as_ref())
120    }
121}
122
123// ---------------------------------------------------------------------------
124// PropertyStorage
125// ---------------------------------------------------------------------------
126
127#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
128pub struct PropertyStorage {
129    pub name: Arc<str>,
130    pub ty: Option<Union>,
131    pub inferred_ty: Option<Union>,
132    pub visibility: Visibility,
133    pub is_static: bool,
134    pub is_readonly: bool,
135    pub default: Option<Union>,
136    pub location: Option<Location>,
137}
138
139// ---------------------------------------------------------------------------
140// ConstantStorage
141// ---------------------------------------------------------------------------
142
143#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
144pub struct ConstantStorage {
145    pub name: Arc<str>,
146    pub ty: Union,
147    pub visibility: Option<Visibility>,
148    pub location: Option<Location>,
149}
150
151// ---------------------------------------------------------------------------
152// ClassStorage
153// ---------------------------------------------------------------------------
154
155#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
156pub struct ClassStorage {
157    pub fqcn: Arc<str>,
158    pub short_name: Arc<str>,
159    pub parent: Option<Arc<str>>,
160    pub interfaces: Vec<Arc<str>>,
161    pub traits: Vec<Arc<str>>,
162    pub own_methods: IndexMap<Arc<str>, MethodStorage>,
163    pub own_properties: IndexMap<Arc<str>, PropertyStorage>,
164    pub own_constants: IndexMap<Arc<str>, ConstantStorage>,
165    pub template_params: Vec<TemplateParam>,
166    pub is_abstract: bool,
167    pub is_final: bool,
168    pub is_readonly: bool,
169    /// Populated during codebase finalization: all inherited methods (parent chain + traits).
170    pub all_methods: IndexMap<Arc<str>, MethodStorage>,
171    /// Populated during finalization: all ancestor FQCNs (parents + interfaces, transitively).
172    pub all_parents: Vec<Arc<str>>,
173    pub is_deprecated: bool,
174    pub is_internal: bool,
175    pub location: Option<Location>,
176}
177
178impl ClassStorage {
179    pub fn get_method(&self, name: &str) -> Option<&MethodStorage> {
180        // PHP method names are case-insensitive; caller should pass lowercase name.
181        // Fast path: exact match (works when keys are stored lowercase).
182        if let Some(m) = self
183            .all_methods
184            .get(name)
185            .or_else(|| self.own_methods.get(name))
186        {
187            return Some(m);
188        }
189        // Fallback: case-insensitive scan (handles stubs stored with original case).
190        self.all_methods
191            .iter()
192            .find(|(k, _)| k.as_ref().eq_ignore_ascii_case(name))
193            .map(|(_, v)| v)
194            .or_else(|| {
195                self.own_methods
196                    .iter()
197                    .find(|(k, _)| k.as_ref().eq_ignore_ascii_case(name))
198                    .map(|(_, v)| v)
199            })
200    }
201
202    pub fn get_property(&self, name: &str) -> Option<&PropertyStorage> {
203        self.own_properties.get(name)
204    }
205
206    pub fn implements_or_extends(&self, fqcn: &str) -> bool {
207        self.fqcn.as_ref() == fqcn || self.all_parents.iter().any(|p| p.as_ref() == fqcn)
208    }
209}
210
211// ---------------------------------------------------------------------------
212// InterfaceStorage
213// ---------------------------------------------------------------------------
214
215#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
216pub struct InterfaceStorage {
217    pub fqcn: Arc<str>,
218    pub short_name: Arc<str>,
219    pub extends: Vec<Arc<str>>,
220    pub own_methods: IndexMap<Arc<str>, MethodStorage>,
221    pub own_constants: IndexMap<Arc<str>, ConstantStorage>,
222    pub template_params: Vec<TemplateParam>,
223    pub all_parents: Vec<Arc<str>>,
224    pub location: Option<Location>,
225}
226
227// ---------------------------------------------------------------------------
228// TraitStorage
229// ---------------------------------------------------------------------------
230
231#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
232pub struct TraitStorage {
233    pub fqcn: Arc<str>,
234    pub short_name: Arc<str>,
235    pub own_methods: IndexMap<Arc<str>, MethodStorage>,
236    pub own_properties: IndexMap<Arc<str>, PropertyStorage>,
237    pub own_constants: IndexMap<Arc<str>, ConstantStorage>,
238    pub template_params: Vec<TemplateParam>,
239    pub location: Option<Location>,
240}
241
242// ---------------------------------------------------------------------------
243// EnumStorage
244// ---------------------------------------------------------------------------
245
246#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
247pub struct EnumCaseStorage {
248    pub name: Arc<str>,
249    pub value: Option<Union>,
250    pub location: Option<Location>,
251}
252
253#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
254pub struct EnumStorage {
255    pub fqcn: Arc<str>,
256    pub short_name: Arc<str>,
257    pub scalar_type: Option<Union>,
258    pub interfaces: Vec<Arc<str>>,
259    pub cases: IndexMap<Arc<str>, EnumCaseStorage>,
260    pub own_methods: IndexMap<Arc<str>, MethodStorage>,
261    pub own_constants: IndexMap<Arc<str>, ConstantStorage>,
262    pub location: Option<Location>,
263}
264
265// ---------------------------------------------------------------------------
266// FunctionStorage
267// ---------------------------------------------------------------------------
268
269#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
270pub struct FunctionStorage {
271    pub fqn: Arc<str>,
272    pub short_name: Arc<str>,
273    pub params: Vec<FnParam>,
274    pub return_type: Option<Union>,
275    pub inferred_return_type: Option<Union>,
276    pub template_params: Vec<TemplateParam>,
277    pub assertions: Vec<Assertion>,
278    pub throws: Vec<Arc<str>>,
279    pub is_deprecated: bool,
280    pub is_pure: bool,
281    pub location: Option<Location>,
282}
283
284impl FunctionStorage {
285    pub fn effective_return_type(&self) -> Option<&Union> {
286        self.return_type
287            .as_ref()
288            .or(self.inferred_return_type.as_ref())
289    }
290}