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