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