1use std::fmt::Debug;
2
3use crate::{
4 derive_ASTNode, ASTNode, Expression, ParseError, ParseErrors, ParseResult, TSXKeyword,
5 TSXToken, TypeAnnotation, VariableField, WithComment,
6};
7
8use derive_partial_eq_extras::PartialEqExtras;
9use iterator_endiate::EndiateIteratorExt;
10use source_map::Span;
11use tokenizer_lib::{
12 sized_tokens::{TokenReaderWithTokenEnds, TokenStart},
13 Token, TokenReader,
14};
15use visitable_derive::Visitable;
16
17#[apply(derive_ASTNode)]
18#[derive(Debug, Clone, PartialEq, Visitable, get_field_by_type::GetFieldByType)]
19#[get_field_by_type_target(Span)]
20pub struct Parameter<V> {
21 #[visit_skip_field]
22 pub visibility: V,
23 pub name: WithComment<VariableField>,
24 pub type_annotation: Option<TypeAnnotation>,
25 pub additionally: Option<ParameterData>,
26 pub position: Span,
27}
28
29pub trait ParameterVisibility: Send + Sync + Sized + Debug + PartialEq + Clone + 'static {
30 fn from_reader(
31 reader: &mut impl TokenReader<TSXToken, crate::TokenStart>,
32 state: &mut crate::ParsingState,
33 options: &crate::ParseOptions,
34 ) -> Self;
35}
36
37impl ParameterVisibility for () {
38 fn from_reader(
39 _: &mut impl TokenReader<TSXToken, crate::TokenStart>,
40 _: &mut crate::ParsingState,
41 _: &crate::ParseOptions,
42 ) -> Self {
43 }
44}
45
46impl ParameterVisibility for Option<crate::types::Visibility> {
47 fn from_reader(
48 reader: &mut impl TokenReader<TSXToken, crate::TokenStart>,
49 _: &mut crate::ParsingState,
50 options: &crate::ParseOptions,
51 ) -> Option<crate::types::Visibility> {
52 if !options.type_annotations {
53 None
54 } else if let Some(Token(TSXToken::Keyword(t), _)) =
55 reader.conditional_next(crate::types::Visibility::token_is_visibility_specifier)
56 {
57 Some(match t {
58 TSXKeyword::Private => crate::types::Visibility::Private,
59 TSXKeyword::Public => crate::types::Visibility::Public,
60 TSXKeyword::Protected => crate::types::Visibility::Protected,
61 _ => unreachable!(),
62 })
63 } else {
64 None
65 }
66 }
67}
68
69#[derive(Debug, Clone, PartialEq, Visitable)]
70#[apply(derive_ASTNode)]
71pub enum ParameterData {
72 Optional,
73 WithDefaultValue(Box<Expression>),
74}
75
76#[cfg(feature = "extras")]
77#[cfg_attr(target_family = "wasm", tsify::declare)]
78pub type SpreadParameterName = VariableField;
79
80#[cfg(not(feature = "extras"))]
81#[cfg_attr(target_family = "wasm", tsify::declare)]
82pub type SpreadParameterName = crate::VariableIdentifier;
83
84#[apply(derive_ASTNode)]
85#[derive(Debug, Clone, PartialEq, Visitable)]
86pub struct SpreadParameter {
87 pub name: SpreadParameterName,
88 pub type_annotation: Option<TypeAnnotation>,
89 pub position: Span,
90}
91
92#[apply(derive_ASTNode)]
93#[derive(Debug, Clone, PartialEqExtras, Visitable)]
94#[partial_eq_ignore_types(Span)]
95pub struct FunctionParameters<L, V> {
96 #[visit_skip_field]
97 pub leading: L,
98 pub parameters: Vec<Parameter<V>>,
99 pub rest_parameter: Option<Box<SpreadParameter>>,
100 pub position: Span,
101}
102
103pub trait LeadingParameter: Send + Sync + Sized + Debug + PartialEq + Clone + 'static {
104 fn try_make(
105 this_annotation: Option<ThisParameter>,
106 super_annotation: Option<SuperParameter>,
107 ) -> ParseResult<Self>;
108
109 fn get_this_parameter(&self) -> Option<&ThisParameter>;
110 fn get_super_parameter(&self) -> Option<&SuperParameter>;
111}
112
113#[apply(derive_ASTNode)]
114#[derive(Debug, Clone, PartialEqExtras, Visitable)]
115#[partial_eq_ignore_types(Span)]
116pub struct ThisParameter {
117 pub constraint: TypeAnnotation,
118 pub position: Span,
119}
120
121#[apply(derive_ASTNode)]
123#[derive(Debug, Clone, PartialEqExtras, Visitable)]
124#[partial_eq_ignore_types(Span)]
125pub struct SuperParameter {
126 pub constraint: TypeAnnotation,
127 pub position: Span,
128}
129
130impl LeadingParameter for () {
131 fn try_make(
132 this_annotation: Option<ThisParameter>,
133 super_annotation: Option<SuperParameter>,
134 ) -> ParseResult<Self> {
135 if this_annotation.is_some() || super_annotation.is_some() {
136 let position =
137 this_annotation.map_or(super_annotation.unwrap().position, |a| a.position);
138
139 Err(ParseError::new(ParseErrors::CannotUseLeadingParameterHere, position))
140 } else {
141 Ok(())
142 }
143 }
144
145 fn get_this_parameter(&self) -> Option<&ThisParameter> {
146 None
147 }
148 fn get_super_parameter(&self) -> Option<&SuperParameter> {
149 None
150 }
151}
152
153impl LeadingParameter for Option<ThisParameter> {
154 fn try_make(
155 this_annotation: Option<ThisParameter>,
156 super_annotation: Option<SuperParameter>,
157 ) -> ParseResult<Self> {
158 if let Some(s) = super_annotation {
159 Err(ParseError::new(ParseErrors::CannotUseLeadingParameterHere, s.position))
160 } else {
161 Ok(this_annotation)
162 }
163 }
164
165 fn get_this_parameter(&self) -> Option<&ThisParameter> {
166 self.as_ref()
167 }
168 fn get_super_parameter(&self) -> Option<&SuperParameter> {
169 None
170 }
171}
172
173impl LeadingParameter for (Option<ThisParameter>, Option<SuperParameter>) {
174 fn try_make(
175 this_annotation: Option<ThisParameter>,
176 super_annotation: Option<SuperParameter>,
177 ) -> ParseResult<Self> {
178 Ok((this_annotation, super_annotation))
179 }
180
181 fn get_this_parameter(&self) -> Option<&ThisParameter> {
182 self.0.as_ref()
183 }
184 fn get_super_parameter(&self) -> Option<&SuperParameter> {
185 self.1.as_ref()
186 }
187}
188
189impl<L, V> ASTNode for FunctionParameters<L, V>
190where
191 L: LeadingParameter,
192 V: ParameterVisibility,
193{
194 fn get_position(&self) -> Span {
195 self.position
196 }
197
198 fn from_reader(
199 reader: &mut impl TokenReader<TSXToken, crate::TokenStart>,
200 state: &mut crate::ParsingState,
201 options: &crate::ParseOptions,
202 ) -> ParseResult<Self> {
203 let open_paren_span = reader.expect_next(TSXToken::OpenParentheses)?;
204 Self::from_reader_sub_open_parenthesis(reader, state, options, open_paren_span)
205 }
206
207 fn to_string_from_buffer<T: source_map::ToString>(
208 &self,
209 buf: &mut T,
210 options: &crate::ToStringOptions,
211 local: crate::LocalToStringInformation,
212 ) {
213 let FunctionParameters { parameters, rest_parameter, .. } = self;
214 let mut large = false;
215 if options.enforce_limit_length_limit() && local.should_try_pretty_print {
216 let room = options.max_line_length as usize;
217 let mut buf = source_map::StringWithOptionalSourceMap {
218 source: String::new(),
219 source_map: None,
220 quit_after: Some(room),
221 since_new_line: 0,
222 };
223 for parameter in parameters {
225 parameter.name.to_string_from_buffer(&mut buf, options, local);
226 let type_annotation = parameter.type_annotation.as_ref();
227 type_annotation.inspect(|v| v.to_string_from_buffer(&mut buf, options, local));
228 if let Some(ParameterData::WithDefaultValue(ref value)) = parameter.additionally {
229 value.to_string_from_buffer(&mut buf, options, local);
230 }
231 large = buf.source.len() > room;
232 if large {
233 break;
234 }
235 }
236 if let Some(rest_parameter) = rest_parameter {
237 rest_parameter.name.to_string_from_buffer(&mut buf, options, local);
238 let type_annotation = rest_parameter.type_annotation.as_ref();
239 type_annotation.inspect(|v| v.to_string_from_buffer(&mut buf, options, local));
240 large = buf.source.len() > room;
241 }
242 }
243
244 let inner_local = if large { local.next_level() } else { local };
245
246 buf.push('(');
247 for (at_end, Parameter { name, type_annotation, additionally, .. }) in
249 parameters.iter().endiate()
250 {
251 if large {
252 buf.push_new_line();
253 options.add_indent(inner_local.depth, buf);
254 }
255 name.to_string_from_buffer(buf, options, inner_local);
257 if let (true, Some(ref type_annotation)) =
258 (options.include_type_annotations, type_annotation)
259 {
260 if let Some(ParameterData::Optional) = additionally {
261 buf.push('?');
262 }
263 buf.push_str(": ");
264 type_annotation.to_string_from_buffer(buf, options, inner_local);
265 }
266 if let Some(ParameterData::WithDefaultValue(value)) = additionally {
267 buf.push_str(if options.pretty { " = " } else { "=" });
268 value.to_string_from_buffer(buf, options, inner_local);
269 }
270 if !at_end || rest_parameter.is_some() {
271 buf.push(',');
272 options.push_gap_optionally(buf);
273 }
274 }
275 if let Some(rest_parameter) = rest_parameter {
276 if large {
277 buf.push_new_line();
278 options.add_indent(inner_local.depth, buf);
279 }
280 buf.push_str("...");
281 rest_parameter.name.to_string_from_buffer(buf, options, inner_local);
282 if let Some(ref type_annotation) = rest_parameter.type_annotation {
283 buf.push_str(": ");
284 type_annotation.to_string_from_buffer(buf, options, inner_local);
285 }
286 }
287 if large {
288 buf.push_new_line();
289 options.add_indent(local.depth, buf);
290 }
291 buf.push(')');
292 }
293}
294
295impl<L, V> FunctionParameters<L, V>
296where
297 L: LeadingParameter,
298 V: ParameterVisibility,
299{
300 pub(crate) fn from_reader_sub_open_parenthesis(
301 reader: &mut impl TokenReader<TSXToken, crate::TokenStart>,
302 state: &mut crate::ParsingState,
303 options: &crate::ParseOptions,
304 start: TokenStart,
305 ) -> ParseResult<Self> {
306 let mut parameters = Vec::new();
307
308 let mut this_type = None::<ThisParameter>;
309 let mut super_type = None::<SuperParameter>;
310 let mut rest_parameter = None;
311
312 loop {
313 if let Some(Token(TSXToken::CloseParentheses, _)) = reader.peek() {
314 break;
315 }
316 while reader.conditional_next(TSXToken::is_comment).is_some() {}
318
319 if let Some(Token(_, spread_pos)) =
320 reader.conditional_next(|tok| matches!(tok, TSXToken::Spread))
321 {
322 let name = SpreadParameterName::from_reader(reader, state, options)?;
323 let name_position = name.get_position();
324
325 let type_annotation = if options.type_annotations
326 && reader.conditional_next(|tok| matches!(tok, TSXToken::Colon)).is_some()
327 {
328 Some(TypeAnnotation::from_reader(reader, state, options)?)
329 } else {
330 None
331 };
332
333 let position = spread_pos
334 .union(type_annotation.as_ref().map_or(name_position, ASTNode::get_position));
335
336 rest_parameter =
337 Some(Box::new(SpreadParameter { name, type_annotation, position }));
338 break;
339 } else if let Some(Token(_, start)) = reader.conditional_next(|tok| {
340 options.type_annotations
341 && parameters.is_empty()
342 && matches!(tok, TSXToken::Keyword(TSXKeyword::This))
343 }) {
344 reader.expect_next(TSXToken::Colon)?;
345 let constraint = TypeAnnotation::from_reader(reader, state, options)?;
346 let position = start.union(constraint.get_position());
347 this_type = Some(ThisParameter { constraint, position });
348 } else if let Some(Token(_, start)) = reader.conditional_next(|tok| {
349 options.type_annotations
350 && parameters.is_empty()
351 && matches!(tok, TSXToken::Keyword(TSXKeyword::Super))
352 }) {
353 reader.expect_next(TSXToken::Colon)?;
354 let constraint = TypeAnnotation::from_reader(reader, state, options)?;
355 let position = start.union(constraint.get_position());
356 super_type = Some(SuperParameter { constraint, position });
357 } else {
358 let visibility = V::from_reader(reader, state, options);
359
360 let name = WithComment::<VariableField>::from_reader(reader, state, options)?;
361
362 let (is_optional, type_annotation) = match reader.peek() {
363 Some(Token(TSXToken::Colon, _)) if options.type_annotations => {
364 reader.next();
365 let type_annotation = TypeAnnotation::from_reader(reader, state, options)?;
366 (false, Some(type_annotation))
367 }
368 Some(Token(TSXToken::OptionalMember, _)) if options.type_annotations => {
369 reader.next();
370 let type_annotation = TypeAnnotation::from_reader(reader, state, options)?;
371 (true, Some(type_annotation))
372 }
373 Some(Token(TSXToken::QuestionMark, _)) => {
374 let Token(_, _) = reader.next().unwrap();
375 (true, None)
376 }
377 _ => (false, None),
378 };
379
380 let value = if let Some(token) =
381 reader.conditional_next(|tok| matches!(tok, TSXToken::Assign))
382 {
383 if is_optional {
384 return Err(ParseError::new(
385 crate::ParseErrors::FunctionParameterOptionalAndDefaultValue,
386 token.get_span(),
387 ));
388 }
389 Some(Box::new(Expression::from_reader(reader, state, options)?))
390 } else {
391 None
392 };
393
394 let additionally = match (is_optional, value) {
395 (true, Some(_)) => unreachable!("caught earlier by error"),
396 (false, Some(value)) => Some(ParameterData::WithDefaultValue(value)),
398 (true, None) => Some(ParameterData::Optional),
400 (false, None) => None,
401 };
402
403 let end_position = if let Some(ParameterData::WithDefaultValue(e)) = &additionally {
404 e.get_position()
405 } else if let Some(type_annotation) = &type_annotation {
406 type_annotation.get_position()
407 } else {
408 name.get_position()
409 };
410
411 parameters.push(Parameter {
412 visibility,
413 position: name.get_position().union(end_position),
414 name,
415 type_annotation,
416 additionally,
417 });
418 }
419 if let Some(Token(TSXToken::Comma, _)) = reader.peek() {
420 reader.next();
421 } else {
422 break;
423 }
424 }
425
426 let close = reader.expect_next_get_end(TSXToken::CloseParentheses)?;
427
428 let leading = L::try_make(this_type, super_type)?;
429
430 Ok(FunctionParameters { position: start.union(close), parameters, rest_parameter, leading })
431 }
432}