mago_codex/metadata/
function_like.rs

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