Skip to main content

mago_codex/metadata/
function_like.rs

1use std::collections::BTreeMap;
2
3use serde::Deserialize;
4use serde::Serialize;
5
6use mago_atom::Atom;
7use mago_atom::AtomMap;
8use mago_reporting::Issue;
9use mago_span::Span;
10
11use crate::assertion::Assertion;
12use crate::metadata::attribute::AttributeMetadata;
13use crate::metadata::flags::MetadataFlags;
14use crate::metadata::parameter::FunctionLikeParameterMetadata;
15use crate::metadata::ttype::TypeMetadata;
16use crate::misc::GenericParent;
17use crate::ttype::resolution::TypeResolutionContext;
18use crate::ttype::union::TUnion;
19use crate::visibility::Visibility;
20
21pub type TemplateTuple = (Atom, Vec<(GenericParent, TUnion)>);
22
23/// Contains metadata specific to methods defined within classes, interfaces, enums, or traits.
24///
25/// This complements the more general `FunctionLikeMetadata`.
26#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
27#[non_exhaustive]
28pub struct MethodMetadata {
29    /// Marks whether this method is declared as `final`, preventing further overriding.
30    pub is_final: bool,
31
32    /// Marks whether this method is declared as `abstract`, requiring implementation in subclasses.
33    pub is_abstract: bool,
34
35    /// Marks whether this method is declared as `static`, allowing it to be called without an instance.
36    pub is_static: bool,
37
38    /// Marks whether this method is a constructor (`__construct`).
39    pub is_constructor: bool,
40
41    /// Marks whether this method is declared as `public`, `protected`, or `private`.
42    pub visibility: Visibility,
43
44    /// A map of constraints defined by `@where` docblock tags.
45    ///
46    /// The key is the name of a class-level template parameter (e.g., `T`), and the value
47    /// is the `TUnion` type constraint that `T` must satisfy for this specific method
48    /// to be considered callable.
49    pub where_constraints: AtomMap<TypeMetadata>,
50}
51
52/// Distinguishes between different kinds of callable constructs in PHP.
53#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
54pub enum FunctionLikeKind {
55    /// Represents a standard function declared in the global scope or a namespace (`function foo() {}`).
56    Function,
57    /// Represents a method defined within a class, trait, enum, or interface (`class C { function bar() {} }`).
58    Method,
59    /// Represents an anonymous function created using `function() {}`.
60    Closure,
61    /// Represents an arrow function (short closure syntax) introduced in PHP 7.4 (`fn() => ...`).
62    ArrowFunction,
63}
64
65#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
66pub struct FunctionLikeMetadata {
67    /// The kind of function-like structure this metadata represents.
68    pub kind: FunctionLikeKind,
69
70    /// The source code location (span) covering the entire function/method/closure definition.
71    /// For closures/arrow functions, this covers the `function(...) { ... }` or `fn(...) => ...` part.
72    pub span: Span,
73
74    /// The name of the function or method, lowercased, if applicable.
75    /// `None` for closures and arrow functions unless assigned to a variable later.
76    /// Example: `processRequest`, `__construct`, `my_global_func`.
77    pub name: Option<Atom>,
78
79    /// The original name of the function or method, in its original case.
80    pub original_name: Option<Atom>,
81
82    /// The specific source code location (span) of the function or method name identifier.
83    /// `None` if the function/method has no name (closures/arrow functions).
84    pub name_span: Option<Span>,
85
86    /// Ordered list of metadata for each parameter defined in the signature.
87    pub parameters: Vec<FunctionLikeParameterMetadata>,
88
89    /// The explicit return type declaration (type hint).
90    ///
91    /// Example: For `function getName(): string`, this holds metadata for `string`.
92    /// `None` if no return type is specified.
93    pub return_type_declaration_metadata: Option<TypeMetadata>,
94
95    /// The explicit return type declaration (type hint) or docblock type (`@return`).
96    ///
97    /// Example: For `function getName(): string`, this holds metadata for `string`,
98    /// or for ` /** @return string */ function getName() { .. }`, this holds metadata for `string`.
99    /// `None` if neither is specified.
100    pub return_type_metadata: Option<TypeMetadata>,
101
102    /// Generic type parameters (templates) defined for the function/method (e.g., `@template T`).
103    /// Stores the template name and its constraints (parent type and bound type).
104    /// Example: `[("T", [(GenericParent::Function("funcName"), Arc<TUnion::object()>)])]`
105    pub template_types: Vec<TemplateTuple>,
106
107    /// Attributes attached to the function/method/closure declaration (`#[Attribute] function foo() {}`).
108    pub attributes: Vec<AttributeMetadata>,
109
110    /// Specific metadata relevant only to methods (visibility, final, static, etc.).
111    /// This is `Some` if `kind` is `FunctionLikeKind::Method`, `None` otherwise.
112    pub method_metadata: Option<MethodMetadata>,
113
114    /// Contains context information needed for resolving types within this function's scope
115    /// (e.g., `use` statements, current namespace, class context). Often populated during analysis.
116    pub type_resolution_context: Option<TypeResolutionContext>,
117
118    /// A list of types that this function/method might throw, derived from `@throws` docblock tags
119    /// or inferred from `throw` statements within the body.
120    pub thrown_types: Vec<TypeMetadata>,
121
122    /// List of issues specifically related to parsing or interpreting this function's docblock.
123    pub issues: Vec<Issue>,
124
125    /// Assertions about parameter types or variable types that are guaranteed to be true
126    /// *after* this function/method returns normally. From `@psalm-assert`, `@phpstan-assert`, etc.
127    /// Maps variable/parameter name to a list of type assertions.
128    pub assertions: BTreeMap<Atom, Vec<Assertion>>,
129
130    /// Assertions about parameter/variable types that are guaranteed to be true if this
131    /// function/method returns `true`. From `@psalm-assert-if-true`, etc.
132    pub if_true_assertions: BTreeMap<Atom, Vec<Assertion>>,
133
134    /// Assertions about parameter/variable types that are guaranteed to be true if this
135    /// function/method returns `false`. From `@psalm-assert-if-false`, etc.
136    pub if_false_assertions: BTreeMap<Atom, Vec<Assertion>>,
137
138    /// Tracks whether this function/method has a docblock comment.
139    /// Used to determine if docblock inheritance should occur implicitly.
140    pub has_docblock: bool,
141
142    pub flags: MetadataFlags,
143}
144
145impl FunctionLikeKind {
146    /// Checks if this kind represents a class/trait/enum/interface method.
147    #[inline]
148    #[must_use]
149    pub const fn is_method(&self) -> bool {
150        matches!(self, Self::Method)
151    }
152
153    /// Checks if this kind represents a globally/namespace-scoped function.
154    #[inline]
155    #[must_use]
156    pub const fn is_function(&self) -> bool {
157        matches!(self, Self::Function)
158    }
159
160    /// Checks if this kind represents an anonymous function (`function() {}`).
161    #[inline]
162    #[must_use]
163    pub const fn is_closure(&self) -> bool {
164        matches!(self, Self::Closure)
165    }
166
167    /// Checks if this kind represents an arrow function (`fn() => ...`).
168    #[inline]
169    #[must_use]
170    pub const fn is_arrow_function(&self) -> bool {
171        matches!(self, Self::ArrowFunction)
172    }
173}
174
175/// Contains comprehensive metadata for any function-like structure in PHP.
176impl FunctionLikeMetadata {
177    /// Creates new `FunctionLikeMetadata` with basic information and default flags.
178    #[must_use]
179    pub fn new(kind: FunctionLikeKind, span: Span, flags: MetadataFlags) -> Self {
180        let method_metadata = if kind.is_method() { Some(MethodMetadata::default()) } else { None };
181
182        Self {
183            kind,
184            span,
185            flags,
186            name: None,
187            original_name: None,
188            name_span: None,
189            parameters: vec![],
190            return_type_declaration_metadata: None,
191            return_type_metadata: None,
192            template_types: vec![],
193            attributes: vec![],
194            method_metadata,
195            type_resolution_context: None,
196            thrown_types: vec![],
197            assertions: BTreeMap::new(),
198            if_true_assertions: BTreeMap::new(),
199            if_false_assertions: BTreeMap::new(),
200            has_docblock: false,
201            issues: vec![],
202        }
203    }
204
205    /// Returns the kind of function-like (Function, Method, Closure, `ArrowFunction`).
206    #[inline]
207    #[must_use]
208    pub fn get_kind(&self) -> FunctionLikeKind {
209        self.kind
210    }
211
212    /// Returns a mutable slice of the parameter metadata.
213    #[inline]
214    pub fn get_parameters_mut(&mut self) -> &mut [FunctionLikeParameterMetadata] {
215        &mut self.parameters
216    }
217
218    /// Returns a reference to specific parameter metadata by name, if it exists.
219    #[inline]
220    #[must_use]
221    pub fn get_parameter(&self, name: Atom) -> Option<&FunctionLikeParameterMetadata> {
222        self.parameters.iter().find(|parameter| parameter.get_name().0 == name)
223    }
224
225    /// Returns a mutable reference to specific parameter metadata by name, if it exists.
226    #[inline]
227    pub fn get_parameter_mut(&mut self, name: Atom) -> Option<&mut FunctionLikeParameterMetadata> {
228        self.parameters.iter_mut().find(|parameter| parameter.get_name().0 == name)
229    }
230
231    /// Returns a mutable slice of the template type parameters.
232    #[inline]
233    pub fn get_template_types_mut(&mut self) -> &mut [TemplateTuple] {
234        &mut self.template_types
235    }
236
237    /// Returns a slice of the attributes.
238    #[inline]
239    #[must_use]
240    pub fn get_attributes(&self) -> &[AttributeMetadata] {
241        &self.attributes
242    }
243
244    /// Returns a mutable reference to the method-specific info, if this is a method.
245    #[inline]
246    pub fn get_method_metadata_mut(&mut self) -> Option<&mut MethodMetadata> {
247        self.method_metadata.as_mut()
248    }
249
250    /// Returns a mutable slice of docblock issues.
251    #[inline]
252    pub fn take_issues(&mut self) -> Vec<Issue> {
253        std::mem::take(&mut self.issues)
254    }
255
256    /// Sets the parameters, replacing existing ones.
257    #[inline]
258    pub fn set_parameters(&mut self, parameters: impl IntoIterator<Item = FunctionLikeParameterMetadata>) {
259        self.parameters = parameters.into_iter().collect();
260    }
261
262    /// Returns a new instance with the parameters replaced.
263    #[inline]
264    pub fn with_parameters(mut self, parameters: impl IntoIterator<Item = FunctionLikeParameterMetadata>) -> Self {
265        self.set_parameters(parameters);
266        self
267    }
268
269    #[inline]
270    pub fn set_return_type_metadata(&mut self, return_type: Option<TypeMetadata>) {
271        self.return_type_metadata = return_type;
272    }
273
274    #[inline]
275    pub fn set_return_type_declaration_metadata(&mut self, return_type: Option<TypeMetadata>) {
276        if self.return_type_metadata.is_none() {
277            self.return_type_metadata.clone_from(&return_type);
278        }
279
280        self.return_type_declaration_metadata = return_type;
281    }
282
283    /// Adds a single template type definition.
284    #[inline]
285    pub fn add_template_type(&mut self, template: TemplateTuple) {
286        self.template_types.push(template);
287    }
288}