Skip to main content

mago_syntax/cst/cst/function_like/
parameter.rs

1use mago_span::HasSpan;
2use mago_span::Span;
3
4use crate::cst::cst::attribute::AttributeList;
5use crate::cst::cst::class_like::property::PropertyHookList;
6use crate::cst::cst::expression::Expression;
7use crate::cst::cst::modifier::Modifier;
8use crate::cst::cst::type_hint::Hint;
9use crate::cst::cst::variable::DirectVariable;
10use crate::cst::sequence::Sequence;
11use crate::cst::sequence::TokenSeparatedSequence;
12
13/// Represents a parameter list in PHP.
14#[derive(Debug, Clone, Eq, PartialEq, Hash, PartialOrd, Ord)]
15#[cfg_attr(feature = "serde", derive(serde::Serialize))]
16pub struct FunctionLikeParameterList<'arena> {
17    pub left_parenthesis: Span,
18    pub parameters: TokenSeparatedSequence<'arena, FunctionLikeParameter<'arena>>,
19    pub right_parenthesis: Span,
20}
21
22/// Represents a function-like parameter in PHP.
23///
24/// Example: `int $foo`, `string &$bar`, `bool ...$baz`, `mixed $qux = null`
25#[derive(Debug, Clone, Eq, PartialEq, Hash, PartialOrd, Ord)]
26#[cfg_attr(feature = "serde", derive(serde::Serialize))]
27pub struct FunctionLikeParameter<'arena> {
28    pub attribute_lists: Sequence<'arena, AttributeList<'arena>>,
29    pub modifiers: Sequence<'arena, Modifier<'arena>>,
30    pub hint: Option<Hint<'arena>>,
31    pub ampersand: Option<Span>,
32    pub ellipsis: Option<Span>,
33    pub variable: DirectVariable<'arena>,
34    pub default_value: Option<FunctionLikeParameterDefaultValue<'arena>>,
35    pub hooks: Option<PropertyHookList<'arena>>,
36}
37
38/// Represents the default value of a function-like parameter.
39#[derive(Debug, Clone, Eq, PartialEq, Hash, PartialOrd, Ord)]
40#[cfg_attr(feature = "serde", derive(serde::Serialize))]
41pub struct FunctionLikeParameterDefaultValue<'arena> {
42    pub equals: Span,
43    pub value: &'arena Expression<'arena>,
44}
45
46impl FunctionLikeParameter<'_> {
47    /// Returns whether the parameter is a promoted property.
48    ///
49    /// A promoted property is a property that is declared in a constructor parameter list.
50    ///
51    /// A parameter is considered a promoted property if it has at least one modifier or a hook.
52    ///
53    /// [RFC: Constructor Property Promotion](https://wiki.php.net/rfc/constructor_promotion)
54    /// [RFC: Property Hooks](https://wiki.php.net/rfc/property-hooks)
55    #[must_use]
56    pub fn is_promoted_property(&self) -> bool {
57        !self.modifiers.is_empty() || self.hooks.is_some()
58    }
59
60    #[inline]
61    #[must_use]
62    pub const fn is_variadic(&self) -> bool {
63        self.ellipsis.is_some()
64    }
65
66    #[inline]
67    #[must_use]
68    pub const fn is_reference(&self) -> bool {
69        self.ampersand.is_some()
70    }
71}
72
73impl HasSpan for FunctionLikeParameterList<'_> {
74    fn span(&self) -> Span {
75        Span::between(self.left_parenthesis, self.right_parenthesis)
76    }
77}
78
79impl HasSpan for FunctionLikeParameter<'_> {
80    fn span(&self) -> Span {
81        let right = self.hooks.as_ref().map_or_else(
82            || self.default_value.as_ref().map_or_else(|| self.variable.span(), HasSpan::span),
83            HasSpan::span,
84        );
85
86        if let Some(attribute) = self.attribute_lists.first() {
87            return Span::between(attribute.span(), right);
88        }
89
90        if let Some(modifier) = self.modifiers.first() {
91            return Span::between(modifier.span(), right);
92        }
93
94        if let Some(type_hint) = &self.hint {
95            return Span::between(type_hint.span(), right);
96        }
97
98        if let Some(ellipsis) = self.ellipsis {
99            return Span::between(ellipsis, right);
100        }
101
102        if let Some(ampersand) = self.ampersand {
103            return Span::between(ampersand, right);
104        }
105
106        Span::between(self.variable.span(), right)
107    }
108}
109
110impl HasSpan for FunctionLikeParameterDefaultValue<'_> {
111    fn span(&self) -> Span {
112        Span::between(self.equals, self.value.span())
113    }
114}