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    pub variance: mir_types::Variance,
41}
42
43#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
44pub struct FnParam {
45    pub name: Arc<str>,
46    pub ty: Option<Union>,
47    pub default: Option<Union>,
48    pub is_variadic: bool,
49    pub is_byref: bool,
50    pub is_optional: bool,
51}
52
53// ---------------------------------------------------------------------------
54// Location — file + byte offsets
55// ---------------------------------------------------------------------------
56
57#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
58pub struct Location {
59    pub file: Arc<str>,
60    /// Byte offset of the start of the declaration in the source.
61    pub start: u32,
62    pub end: u32,
63    /// 1-based line number of the declaration.
64    pub line: u32,
65    /// 0-based Unicode char-count (code-point) column offset of the declaration start.
66    pub col: u16,
67}
68
69impl Location {
70    pub fn new(file: Arc<str>, start: u32, end: u32) -> Self {
71        Self {
72            file,
73            start,
74            end,
75            line: 1,
76            col: 0,
77        }
78    }
79
80    pub fn with_line_col(file: Arc<str>, start: u32, end: u32, line: u32, col: u16) -> Self {
81        Self {
82            file,
83            start,
84            end,
85            line,
86            col,
87        }
88    }
89}
90
91// ---------------------------------------------------------------------------
92// Assertion — `@psalm-assert`, `@psalm-assert-if-true`, etc.
93// ---------------------------------------------------------------------------
94
95#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
96pub enum AssertionKind {
97    Assert,
98    AssertIfTrue,
99    AssertIfFalse,
100}
101
102#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
103pub struct Assertion {
104    pub kind: AssertionKind,
105    pub param: Arc<str>,
106    pub ty: Union,
107}
108
109// ---------------------------------------------------------------------------
110// MethodStorage
111// ---------------------------------------------------------------------------
112
113#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
114pub struct MethodStorage {
115    pub name: Arc<str>,
116    pub fqcn: Arc<str>,
117    pub params: Vec<FnParam>,
118    /// Type from annotation (`@return` / native type hint). `None` means unannotated.
119    pub return_type: Option<Union>,
120    /// Type inferred from body analysis (filled in during pass 2).
121    pub inferred_return_type: Option<Union>,
122    pub visibility: Visibility,
123    pub is_static: bool,
124    pub is_abstract: bool,
125    pub is_final: bool,
126    pub is_constructor: bool,
127    pub template_params: Vec<TemplateParam>,
128    pub assertions: Vec<Assertion>,
129    pub throws: Vec<Arc<str>>,
130    pub deprecated: Option<Arc<str>>,
131    pub is_internal: bool,
132    pub is_pure: bool,
133    pub location: Option<Location>,
134}
135
136impl MethodStorage {
137    pub fn effective_return_type(&self) -> Option<&Union> {
138        self.return_type
139            .as_ref()
140            .or(self.inferred_return_type.as_ref())
141    }
142}
143
144// ---------------------------------------------------------------------------
145// PropertyStorage
146// ---------------------------------------------------------------------------
147
148#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
149pub struct PropertyStorage {
150    pub name: Arc<str>,
151    pub ty: Option<Union>,
152    pub inferred_ty: Option<Union>,
153    pub visibility: Visibility,
154    pub is_static: bool,
155    pub is_readonly: bool,
156    pub default: Option<Union>,
157    pub location: Option<Location>,
158}
159
160// ---------------------------------------------------------------------------
161// ConstantStorage
162// ---------------------------------------------------------------------------
163
164#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
165pub struct ConstantStorage {
166    pub name: Arc<str>,
167    pub ty: Union,
168    pub visibility: Option<Visibility>,
169    #[serde(default)]
170    pub is_final: bool,
171    pub location: Option<Location>,
172}
173
174// ---------------------------------------------------------------------------
175// ClassStorage
176// ---------------------------------------------------------------------------
177
178#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
179pub struct ClassStorage {
180    pub fqcn: Arc<str>,
181    pub short_name: Arc<str>,
182    pub parent: Option<Arc<str>>,
183    pub interfaces: Vec<Arc<str>>,
184    pub traits: Vec<Arc<str>>,
185    pub own_methods: IndexMap<Arc<str>, Arc<MethodStorage>>,
186    pub own_properties: IndexMap<Arc<str>, PropertyStorage>,
187    pub own_constants: IndexMap<Arc<str>, ConstantStorage>,
188    #[serde(default)]
189    pub mixins: Vec<Arc<str>>,
190    pub template_params: Vec<TemplateParam>,
191    /// Type arguments from `@extends ParentClass<T1, T2>` — maps parent's template params to concrete types.
192    pub extends_type_args: Vec<Union>,
193    /// Type arguments from `@implements Interface<T1, T2>`.
194    #[serde(default)]
195    pub implements_type_args: Vec<(Arc<str>, Vec<Union>)>,
196    pub is_abstract: bool,
197    pub is_final: bool,
198    pub is_readonly: bool,
199    /// Populated during finalization: all ancestor FQCNs (parents + interfaces, transitively).
200    pub all_parents: Vec<Arc<str>>,
201    pub deprecated: Option<Arc<str>>,
202    pub is_internal: bool,
203    pub location: Option<Location>,
204    /// Type aliases declared on this class via `@psalm-type` / `@phpstan-type`.
205    #[serde(default)]
206    pub type_aliases: std::collections::HashMap<Arc<str>, Union>,
207    /// Raw import-type declarations (`(local_name, original_name, from_class)`) — resolved during finalization.
208    #[serde(default)]
209    pub pending_import_types: Vec<(Arc<str>, Arc<str>, Arc<str>)>,
210}
211
212impl ClassStorage {
213    pub fn get_method(&self, name: &str) -> Option<&MethodStorage> {
214        // PHP method names are case-insensitive; caller should pass lowercase name.
215        // Only searches own_methods — inherited method resolution is done by Codebase::get_method.
216        self.own_methods.get(name).map(Arc::as_ref).or_else(|| {
217            self.own_methods
218                .iter()
219                .find(|(k, _)| k.as_ref().eq_ignore_ascii_case(name))
220                .map(|(_, v)| v.as_ref())
221        })
222    }
223
224    pub fn get_property(&self, name: &str) -> Option<&PropertyStorage> {
225        self.own_properties.get(name)
226    }
227
228    pub fn implements_or_extends(&self, fqcn: &str) -> bool {
229        self.fqcn.as_ref() == fqcn || self.all_parents.iter().any(|p| p.as_ref() == fqcn)
230    }
231}
232
233// ---------------------------------------------------------------------------
234// InterfaceStorage
235// ---------------------------------------------------------------------------
236
237#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
238pub struct InterfaceStorage {
239    pub fqcn: Arc<str>,
240    pub short_name: Arc<str>,
241    pub extends: Vec<Arc<str>>,
242    pub own_methods: IndexMap<Arc<str>, Arc<MethodStorage>>,
243    pub own_constants: IndexMap<Arc<str>, ConstantStorage>,
244    pub template_params: Vec<TemplateParam>,
245    pub all_parents: Vec<Arc<str>>,
246    pub location: Option<Location>,
247}
248
249// ---------------------------------------------------------------------------
250// TraitStorage
251// ---------------------------------------------------------------------------
252
253#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
254pub struct TraitStorage {
255    pub fqcn: Arc<str>,
256    pub short_name: Arc<str>,
257    pub own_methods: IndexMap<Arc<str>, Arc<MethodStorage>>,
258    pub own_properties: IndexMap<Arc<str>, PropertyStorage>,
259    pub own_constants: IndexMap<Arc<str>, ConstantStorage>,
260    pub template_params: Vec<TemplateParam>,
261    /// Traits used by this trait (`use OtherTrait;` inside a trait body).
262    pub traits: Vec<Arc<str>>,
263    pub location: Option<Location>,
264    /// `@psalm-require-extends` / `@phpstan-require-extends` — FQCNs that using classes must extend.
265    #[serde(default)]
266    pub require_extends: Vec<Arc<str>>,
267    /// `@psalm-require-implements` / `@phpstan-require-implements` — FQCNs that using classes must implement.
268    #[serde(default)]
269    pub require_implements: Vec<Arc<str>>,
270}
271
272// ---------------------------------------------------------------------------
273// EnumStorage
274// ---------------------------------------------------------------------------
275
276#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
277pub struct EnumCaseStorage {
278    pub name: Arc<str>,
279    pub value: Option<Union>,
280    pub location: Option<Location>,
281}
282
283#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
284pub struct EnumStorage {
285    pub fqcn: Arc<str>,
286    pub short_name: Arc<str>,
287    pub scalar_type: Option<Union>,
288    pub interfaces: Vec<Arc<str>>,
289    pub cases: IndexMap<Arc<str>, EnumCaseStorage>,
290    pub own_methods: IndexMap<Arc<str>, Arc<MethodStorage>>,
291    pub own_constants: IndexMap<Arc<str>, ConstantStorage>,
292    pub location: Option<Location>,
293}
294
295// ---------------------------------------------------------------------------
296// FunctionStorage
297// ---------------------------------------------------------------------------
298
299#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
300pub struct FunctionStorage {
301    pub fqn: Arc<str>,
302    pub short_name: Arc<str>,
303    pub params: Vec<FnParam>,
304    pub return_type: Option<Union>,
305    pub inferred_return_type: Option<Union>,
306    pub template_params: Vec<TemplateParam>,
307    pub assertions: Vec<Assertion>,
308    pub throws: Vec<Arc<str>>,
309    pub deprecated: Option<Arc<str>>,
310    pub is_pure: bool,
311    pub location: Option<Location>,
312}
313
314impl FunctionStorage {
315    pub fn effective_return_type(&self) -> Option<&Union> {
316        self.return_type
317            .as_ref()
318            .or(self.inferred_return_type.as_ref())
319    }
320}
321
322// ---------------------------------------------------------------------------
323// StubSlice — serializable bundle of definitions from one extension's stubs
324// ---------------------------------------------------------------------------
325
326/// A snapshot of all PHP definitions contributed by a single stub file set.
327///
328/// Produced by `mir-stubs-gen` at code-generation time and deserialized at
329/// runtime to inject definitions into the `Codebase`.
330#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
331pub struct StubSlice {
332    pub classes: Vec<ClassStorage>,
333    pub interfaces: Vec<InterfaceStorage>,
334    pub traits: Vec<TraitStorage>,
335    pub enums: Vec<EnumStorage>,
336    pub functions: Vec<FunctionStorage>,
337    #[serde(default)]
338    pub constants: Vec<(Arc<str>, Union)>,
339    /// Source file this slice was collected from. `None` for bundled stub slices
340    /// that were pre-computed and are not tied to a specific on-disk file.
341    #[serde(default)]
342    pub file: Option<Arc<str>>,
343    /// Types of `@var`-annotated global variables collected from this file.
344    /// Populated by `DefinitionCollector`; merged into `Codebase::global_vars`
345    /// by [`crate::Codebase::inject_stub_slice`] when `file` is `Some`.
346    #[serde(default)]
347    pub global_vars: Vec<(Arc<str>, Union)>,
348}