mago_syntax/ast/ast/function_like/
parameter.rs

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