mago_codex/metadata/parameter.rs
1use serde::Deserialize;
2use serde::Serialize;
3
4use mago_span::HasSpan;
5use mago_span::Span;
6
7use crate::metadata::attribute::AttributeMetadata;
8use crate::metadata::ttype::TypeMetadata;
9use crate::misc::VariableIdentifier;
10
11/// Contains metadata associated with a single parameter within a function, method, or closure signature.
12///
13/// This captures details like the parameter's name, type hint, attributes, default value,
14/// pass-by-reference status, variadic nature, and other PHP features like property promotion.
15#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
16pub struct FunctionLikeParameterMetadata {
17 /// Attributes attached to the parameter declaration.
18 pub attributes: Vec<AttributeMetadata>,
19
20 /// The identifier (name) of the parameter, including the leading '$'.
21 pub name: VariableIdentifier,
22
23 /// `true` if the parameter is declared to be passed by reference using `&`.
24 pub is_by_reference: bool,
25
26 /// The explicit type declaration (type hint) or docblock type (`@param`).
27 ///
28 /// Can be `None` if no type is specified.
29 pub type_metadata: Option<TypeMetadata>,
30
31 /// `true` if the parameter has a default value specified in the signature.
32 pub has_default: bool,
33
34 /// The type specified by a `@param-out` docblock tag.
35 ///
36 /// This indicates the expected type of a pass-by-reference parameter *after* the function executes.
37 pub out_type: Option<TypeMetadata>,
38
39 /// The inferred type of the parameter's default value, if `has_default` is true and the
40 /// type could be determined.
41 ///
42 /// `None` if there is no default or the default value's type couldn't be inferred.
43 pub default_type: Option<TypeMetadata>,
44
45 /// The source code location (span) covering the entire parameter declaration.
46 pub span: Span,
47
48 /// The specific source code location (span) of the parameter's name identifier.
49 pub name_span: Span,
50
51 /// `true` if the parameter is variadic, declared using `...`.
52 pub is_variadic: bool,
53
54 /// `true` if this parameter uses constructor property promotion (PHP 8.0+).
55 pub is_promoted_property: bool,
56
57 /// `true` if the parameter is marked as deprecated.
58 pub is_deprecated: bool,
59}
60
61/// Contains metadata associated with a single parameter within a function, method, or closure signature.
62///
63/// This captures details like the parameter's name, type hint, attributes, default value,
64/// pass-by-reference status, variadic nature, and other PHP features like property promotion.
65impl FunctionLikeParameterMetadata {
66 /// Creates new `FunctionLikeParameterMetadata` for a basic parameter.
67 /// Initializes most flags to false and optional fields to None.
68 ///
69 /// # Arguments
70 ///
71 /// * `name`: The identifier (name) of the parameter (e.g., `$userId`).
72 /// * `span`: The source code location covering the entire parameter declaration.
73 /// * `name_span`: The source code location of the parameter's name identifier (`$userId`).
74 pub fn new(name: VariableIdentifier, span: Span, name_span: Span) -> Self {
75 Self {
76 attributes: Vec::new(),
77 name,
78 is_by_reference: false,
79 type_metadata: None,
80 has_default: false,
81 out_type: None,
82 default_type: None,
83 span,
84 name_span,
85 is_variadic: false,
86 is_promoted_property: false,
87 is_deprecated: false,
88 }
89 }
90
91 /// Returns a reference to the parameter's name identifier (e.g., `$userId`).
92 #[inline]
93 pub fn get_name(&self) -> &VariableIdentifier {
94 &self.name
95 }
96
97 /// Returns the span covering the entire parameter declaration.
98 #[inline]
99 pub fn get_span(&self) -> Span {
100 self.span
101 }
102
103 /// Returns the span covering the parameter's name identifier.
104 #[inline]
105 pub fn get_name_span(&self) -> Span {
106 self.name_span
107 }
108
109 /// Returns a reference to the explicit type signature, if available.
110 #[inline]
111 pub fn get_type_metadata(&self) -> Option<&TypeMetadata> {
112 self.type_metadata.as_ref()
113 }
114
115 /// Returns a reference to the inferred type of the default value, if known.
116 #[inline]
117 pub fn get_default_type(&self) -> Option<&TypeMetadata> {
118 self.default_type.as_ref()
119 }
120
121 /// Checks if the parameter is passed by reference (`&`).
122 #[inline]
123 pub const fn is_by_reference(&self) -> bool {
124 self.is_by_reference
125 }
126
127 /// Checks if the parameter has a default value specified in the signature.
128 #[inline]
129 pub const fn has_default(&self) -> bool {
130 self.has_default
131 }
132
133 /// Checks if the parameter is variadic (`...`).
134 #[inline]
135 pub const fn is_variadic(&self) -> bool {
136 self.is_variadic
137 }
138
139 /// Sets the attributes, replacing any existing ones.
140 pub fn set_attributes(&mut self, attributes: impl IntoIterator<Item = AttributeMetadata>) {
141 self.attributes = attributes.into_iter().collect();
142 }
143
144 /// Returns a new instance with the attributes replaced.
145 pub fn with_attributes(mut self, attributes: impl IntoIterator<Item = AttributeMetadata>) -> Self {
146 self.set_attributes(attributes);
147 self
148 }
149
150 /// Sets whether the parameter is passed by reference (`&`). Clears `out_type` if set to `false`.
151 pub fn set_is_by_reference(&mut self, is_by_reference: bool) {
152 self.is_by_reference = is_by_reference;
153 if !is_by_reference {
154 self.out_type = None; // Invariant
155 }
156 }
157
158 /// Returns a new instance with the `is_by_reference` flag set. Clears `out_type` if set to `false`.
159 pub fn with_is_by_reference(mut self, is_by_reference: bool) -> Self {
160 self.set_is_by_reference(is_by_reference);
161 self
162 }
163
164 /// Sets the explicit type signature (type hint or `@param` type).
165 pub fn set_type_signature(&mut self, type_signature: Option<TypeMetadata>) {
166 self.type_metadata = type_signature;
167 }
168
169 /// Returns a new instance with the explicit type signature set.
170 pub fn with_type_signature(mut self, type_signature: Option<TypeMetadata>) -> Self {
171 self.set_type_signature(type_signature);
172 self
173 }
174
175 /// Sets whether the parameter has a default value. Use with caution if also setting `default_type`.
176 pub fn set_has_default(&mut self, has_default: bool) {
177 self.has_default = has_default;
178 // If setting to false, should default_type also be cleared? Let's assume yes for consistency.
179 if !has_default {
180 self.default_type = None;
181 }
182 }
183
184 /// Returns a new instance with the `has_default` flag set. Clears `default_type` if set to `false`.
185 pub fn with_has_default(mut self, has_default: bool) -> Self {
186 self.set_has_default(has_default);
187 self
188 }
189
190 /// Sets the `@param-out` type. Only effective if `is_by_reference` is true.
191 pub fn set_out_type(&mut self, out_type: Option<TypeMetadata>) {
192 if self.is_by_reference {
193 self.out_type = out_type;
194 } else {
195 // Setting an out_type on non-reference is ignored or cleared
196 self.out_type = None;
197 }
198 }
199
200 /// Sets the inferred type of the default value. Also sets `has_default` to `true` if `Some`.
201 pub fn set_default_type(&mut self, default_type: Option<TypeMetadata>) {
202 self.default_type = default_type;
203 // If we know the type, it must have a default. If type is None, maybe it still has a default?
204 // Let's only set has_default=true if default_type is Some.
205 // To clear has_default, use set_has_default(false).
206 if self.default_type.is_some() {
207 self.has_default = true;
208 }
209 // If default_type is None, we *don't* automatically set has_default to false,
210 // because the syntax might exist (`= value`) but inference failed.
211 }
212
213 /// Returns a new instance with the inferred type of the default value set. Also sets `has_default` to `true` if `Some`.
214 pub fn with_default_type(mut self, default_type: Option<TypeMetadata>) -> Self {
215 self.set_default_type(default_type);
216 self
217 }
218
219 /// Sets whether the parameter is variadic (`...`).
220 pub fn set_is_variadic(&mut self, is_variadic: bool) {
221 self.is_variadic = is_variadic;
222 }
223
224 /// Returns a new instance with the `is_variadic` flag set.
225 pub fn with_is_variadic(mut self, is_variadic: bool) -> Self {
226 self.set_is_variadic(is_variadic);
227 self
228 }
229
230 /// Sets whether the parameter is a promoted property.
231 pub fn set_is_promoted_property(&mut self, is_promoted: bool) {
232 self.is_promoted_property = is_promoted;
233 }
234
235 /// Returns a new instance with the `is_promoted_property` flag set.
236 pub fn with_is_promoted_property(mut self, is_promoted: bool) -> Self {
237 self.set_is_promoted_property(is_promoted);
238 self
239 }
240}
241
242impl HasSpan for FunctionLikeParameterMetadata {
243 fn span(&self) -> Span {
244 self.span
245 }
246}