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.as_ref().or(self.inferred_return_type.as_ref())
118    }
119}
120
121// ---------------------------------------------------------------------------
122// PropertyStorage
123// ---------------------------------------------------------------------------
124
125#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
126pub struct PropertyStorage {
127    pub name: Arc<str>,
128    pub ty: Option<Union>,
129    pub inferred_ty: Option<Union>,
130    pub visibility: Visibility,
131    pub is_static: bool,
132    pub is_readonly: bool,
133    pub default: Option<Union>,
134    pub location: Option<Location>,
135}
136
137// ---------------------------------------------------------------------------
138// ConstantStorage
139// ---------------------------------------------------------------------------
140
141#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
142pub struct ConstantStorage {
143    pub name: Arc<str>,
144    pub ty: Union,
145    pub visibility: Option<Visibility>,
146    pub location: Option<Location>,
147}
148
149// ---------------------------------------------------------------------------
150// ClassStorage
151// ---------------------------------------------------------------------------
152
153#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
154pub struct ClassStorage {
155    pub fqcn: Arc<str>,
156    pub short_name: Arc<str>,
157    pub parent: Option<Arc<str>>,
158    pub interfaces: Vec<Arc<str>>,
159    pub traits: Vec<Arc<str>>,
160    pub own_methods: IndexMap<Arc<str>, MethodStorage>,
161    pub own_properties: IndexMap<Arc<str>, PropertyStorage>,
162    pub own_constants: IndexMap<Arc<str>, ConstantStorage>,
163    pub template_params: Vec<TemplateParam>,
164    pub is_abstract: bool,
165    pub is_final: bool,
166    pub is_readonly: bool,
167    /// Populated during codebase finalization: all inherited methods (parent chain + traits).
168    pub all_methods: IndexMap<Arc<str>, MethodStorage>,
169    /// Populated during finalization: all ancestor FQCNs (parents + interfaces, transitively).
170    pub all_parents: Vec<Arc<str>>,
171    pub is_deprecated: bool,
172    pub is_internal: bool,
173    pub location: Option<Location>,
174}
175
176impl ClassStorage {
177    pub fn get_method(&self, name: &str) -> Option<&MethodStorage> {
178        // PHP method names are case-insensitive; caller should pass lowercase name.
179        // Fast path: exact match (works when keys are stored lowercase).
180        if let Some(m) = self.all_methods.get(name).or_else(|| self.own_methods.get(name)) {
181            return Some(m);
182        }
183        // Fallback: case-insensitive scan (handles stubs stored with original case).
184        self.all_methods.iter()
185            .find(|(k, _)| k.as_ref().eq_ignore_ascii_case(name))
186            .map(|(_, v)| v)
187            .or_else(|| self.own_methods.iter()
188                .find(|(k, _)| k.as_ref().eq_ignore_ascii_case(name))
189                .map(|(_, v)| v))
190    }
191
192    pub fn get_property(&self, name: &str) -> Option<&PropertyStorage> {
193        self.own_properties.get(name)
194    }
195
196    pub fn implements_or_extends(&self, fqcn: &str) -> bool {
197        self.fqcn.as_ref() == fqcn || self.all_parents.iter().any(|p| p.as_ref() == fqcn)
198    }
199}
200
201// ---------------------------------------------------------------------------
202// InterfaceStorage
203// ---------------------------------------------------------------------------
204
205#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
206pub struct InterfaceStorage {
207    pub fqcn: Arc<str>,
208    pub short_name: Arc<str>,
209    pub extends: Vec<Arc<str>>,
210    pub own_methods: IndexMap<Arc<str>, MethodStorage>,
211    pub own_constants: IndexMap<Arc<str>, ConstantStorage>,
212    pub template_params: Vec<TemplateParam>,
213    pub all_parents: Vec<Arc<str>>,
214    pub location: Option<Location>,
215}
216
217// ---------------------------------------------------------------------------
218// TraitStorage
219// ---------------------------------------------------------------------------
220
221#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
222pub struct TraitStorage {
223    pub fqcn: Arc<str>,
224    pub short_name: Arc<str>,
225    pub own_methods: IndexMap<Arc<str>, MethodStorage>,
226    pub own_properties: IndexMap<Arc<str>, PropertyStorage>,
227    pub own_constants: IndexMap<Arc<str>, ConstantStorage>,
228    pub template_params: Vec<TemplateParam>,
229    pub location: Option<Location>,
230}
231
232// ---------------------------------------------------------------------------
233// EnumStorage
234// ---------------------------------------------------------------------------
235
236#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
237pub struct EnumCaseStorage {
238    pub name: Arc<str>,
239    pub value: Option<Union>,
240    pub location: Option<Location>,
241}
242
243#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
244pub struct EnumStorage {
245    pub fqcn: Arc<str>,
246    pub short_name: Arc<str>,
247    pub scalar_type: Option<Union>,
248    pub interfaces: Vec<Arc<str>>,
249    pub cases: IndexMap<Arc<str>, EnumCaseStorage>,
250    pub own_methods: IndexMap<Arc<str>, MethodStorage>,
251    pub own_constants: IndexMap<Arc<str>, ConstantStorage>,
252    pub location: Option<Location>,
253}
254
255// ---------------------------------------------------------------------------
256// FunctionStorage
257// ---------------------------------------------------------------------------
258
259#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
260pub struct FunctionStorage {
261    pub fqn: Arc<str>,
262    pub short_name: Arc<str>,
263    pub params: Vec<FnParam>,
264    pub return_type: Option<Union>,
265    pub inferred_return_type: Option<Union>,
266    pub template_params: Vec<TemplateParam>,
267    pub assertions: Vec<Assertion>,
268    pub throws: Vec<Arc<str>>,
269    pub is_deprecated: bool,
270    pub is_pure: bool,
271    pub location: Option<Location>,
272}
273
274impl FunctionStorage {
275    pub fn effective_return_type(&self) -> Option<&Union> {
276        self.return_type.as_ref().or(self.inferred_return_type.as_ref())
277    }
278}