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}