ezno_parser/functions/
mod.rs

1use std::{fmt::Debug, marker::PhantomData};
2
3use crate::property_key::PropertyKeyKind;
4use crate::visiting::{ImmutableVariableOrProperty, MutableVariableOrProperty};
5use crate::{
6	derive_ASTNode, parse_bracketed, to_string_bracketed, ASTNode, Block,
7	ExpressionOrStatementPosition, ExpressionPosition, ParseOptions, ParseResult, TSXToken,
8	TypeAnnotation, TypeParameter, VisitOptions, Visitable, WithComment,
9};
10use crate::{PropertyKey, TSXKeyword};
11use derive_partial_eq_extras::PartialEqExtras;
12use source_map::{Nullable, Span, ToString};
13use tokenizer_lib::sized_tokens::TokenStart;
14use tokenizer_lib::{Token, TokenReader};
15
16mod parameters;
17pub use parameters::*;
18
19pub mod bases {
20	pub use crate::{
21		declarations::{
22			classes::{ClassConstructorBase, ClassFunctionBase},
23			StatementFunctionBase,
24		},
25		expressions::{
26			arrow_function::ArrowFunctionBase, object_literal::ObjectLiteralMethodBase,
27			ExpressionFunctionBase,
28		},
29	};
30}
31
32pub type HeadingAndPosition<T> = (Option<TokenStart>, <T as FunctionBased>::Header);
33
34/// Specialization information for [`FunctionBase`]
35pub trait FunctionBased: Debug + Clone + PartialEq + Send + Sync {
36	/// Includes a keyword and/or modifiers
37	type Header: Debug + Clone + PartialEq + Send + Sync;
38
39	/// A name of the function
40	type Name: Debug + Clone + PartialEq + Send + Sync;
41
42	/// For `this` constraint
43	#[cfg(not(feature = "serde-serialize"))]
44	type LeadingParameter: LeadingParameter;
45
46	/// Cfg to make up for the fact `serde_derive` does not use `syn_helpers`
47	#[cfg(feature = "serde-serialize")]
48	type LeadingParameter: LeadingParameter + serde::Serialize;
49
50	/// For constructors
51	#[cfg(not(feature = "serde-serialize"))]
52	type ParameterVisibility: ParameterVisibility;
53
54	/// Cfg to make up for the fact `serde_derive` does not use `syn_helpers`
55	#[cfg(feature = "serde-serialize")]
56	type ParameterVisibility: ParameterVisibility + serde::Serialize;
57
58	/// The body of the function
59	type Body: ASTNode;
60
61	/// For debugging only
62	fn get_name(name: &Self::Name) -> Option<&str>;
63
64	fn header_and_name_from_reader(
65		reader: &mut impl TokenReader<TSXToken, crate::TokenStart>,
66		state: &mut crate::ParsingState,
67		options: &ParseOptions,
68	) -> ParseResult<(HeadingAndPosition<Self>, Self::Name)>;
69
70	fn header_and_name_to_string_from_buffer<T: ToString>(
71		buf: &mut T,
72		header: &Self::Header,
73		name: &Self::Name,
74		options: &crate::ToStringOptions,
75		local: crate::LocalToStringInformation,
76	);
77
78	/// For [`crate::ArrowFunction`]
79	#[must_use]
80	fn get_parameter_body_boundary_token() -> Option<TSXToken> {
81		None
82	}
83
84	/// For overloading
85	#[must_use]
86	fn has_body(_: &Self::Body) -> bool {
87		true
88	}
89
90	/// For [`crate::ArrowFunction`]
91	fn parameters_from_reader<T: ToString>(
92		reader: &mut impl TokenReader<TSXToken, crate::TokenStart>,
93		state: &mut crate::ParsingState,
94		options: &ParseOptions,
95	) -> ParseResult<FunctionParameters<Self::LeadingParameter, Self::ParameterVisibility>> {
96		FunctionParameters::from_reader(reader, state, options)
97	}
98
99	/// For [`crate::ArrowFunction`]
100	fn parameters_to_string_from_buffer<T: ToString>(
101		buf: &mut T,
102		parameters: &FunctionParameters<Self::LeadingParameter, Self::ParameterVisibility>,
103		options: &crate::ToStringOptions,
104		local: crate::LocalToStringInformation,
105	) {
106		parameters.to_string_from_buffer(buf, options, local);
107	}
108
109	/// For [`crate::ArrowFunction`]
110	fn parameter_body_boundary_token_to_string_from_buffer<T: ToString>(
111		buf: &mut T,
112		options: &crate::ToStringOptions,
113	) {
114		options.push_gap_optionally(buf);
115	}
116
117	fn visit_name<TData>(
118		name: &Self::Name,
119		visitors: &mut (impl crate::VisitorReceiver<TData> + ?Sized),
120		data: &mut TData,
121		options: &VisitOptions,
122		chain: &mut temporary_annex::Annex<crate::Chain>,
123	);
124
125	fn visit_name_mut<TData>(
126		name: &mut Self::Name,
127		visitors: &mut (impl crate::VisitorMutReceiver<TData> + ?Sized),
128		data: &mut TData,
129		options: &VisitOptions,
130		chain: &mut temporary_annex::Annex<crate::Chain>,
131	);
132}
133
134/// Base for all function based structures with bodies (no interface, type reference etc)
135///
136/// Note: the [`PartialEq`] implementation is based on syntactical representation rather than [`FunctionId`] equality
137#[derive(Debug, Clone, PartialEqExtras, get_field_by_type::GetFieldByType)]
138#[get_field_by_type_target(Span)]
139#[cfg_attr(feature = "self-rust-tokenize", derive(self_rust_tokenize::SelfRustTokenize))]
140#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
141pub struct FunctionBase<T: FunctionBased> {
142	pub header: T::Header,
143	pub name: T::Name,
144	pub type_parameters: Option<Vec<TypeParameter>>,
145	pub parameters: FunctionParameters<T::LeadingParameter, T::ParameterVisibility>,
146	pub return_type: Option<TypeAnnotation>,
147	pub body: T::Body,
148	pub position: Span,
149}
150
151#[cfg_attr(target_family = "wasm", wasm_bindgen::prelude::wasm_bindgen(typescript_custom_section))]
152#[allow(dead_code)]
153const TYPES: &str = r"
154	export interface FunctionBase {
155		type_parameters?: TypeParameter[],
156		return_type?: TypeAnnotation,
157		position: Span
158	}
159";
160
161impl<T: FunctionBased> Eq for FunctionBase<T> {}
162
163impl<T: FunctionBased + 'static> ASTNode for FunctionBase<T> {
164	#[allow(clippy::similar_names)]
165	fn from_reader(
166		reader: &mut impl TokenReader<TSXToken, crate::TokenStart>,
167		state: &mut crate::ParsingState,
168		options: &ParseOptions,
169	) -> ParseResult<Self> {
170		let (header_and_left, name) = T::header_and_name_from_reader(reader, state, options)?;
171		Self::from_reader_with_header_and_name(reader, state, options, header_and_left, name)
172	}
173
174	fn to_string_from_buffer<TS: source_map::ToString>(
175		&self,
176		buf: &mut TS,
177		options: &crate::ToStringOptions,
178		local: crate::LocalToStringInformation,
179	) {
180		// Don't print overloads
181		#[cfg(feature = "full-typescript")]
182		if !options.include_type_annotations && !T::has_body(&self.body) {
183			return;
184		}
185
186		T::header_and_name_to_string_from_buffer(buf, &self.header, &self.name, options, local);
187		if let (true, Some(type_parameters)) =
188			(options.include_type_annotations, &self.type_parameters)
189		{
190			to_string_bracketed(type_parameters, ('<', '>'), buf, options, local);
191		}
192		T::parameters_to_string_from_buffer(buf, &self.parameters, options, local);
193		if let (true, Some(return_type)) = (options.include_type_annotations, &self.return_type) {
194			buf.push_str(": ");
195			return_type.to_string_from_buffer(buf, options, local);
196		}
197		if T::has_body(&self.body) {
198			T::parameter_body_boundary_token_to_string_from_buffer(buf, options);
199		}
200		self.body.to_string_from_buffer(buf, options, local.next_level());
201	}
202
203	fn get_position(&self) -> Span {
204		self.position
205	}
206}
207
208#[allow(clippy::similar_names)]
209impl<T: FunctionBased> FunctionBase<T> {
210	pub(crate) fn from_reader_with_header_and_name(
211		reader: &mut impl TokenReader<TSXToken, crate::TokenStart>,
212		state: &mut crate::ParsingState,
213		options: &ParseOptions,
214		(header_left, header): (Option<TokenStart>, T::Header),
215		name: T::Name,
216	) -> ParseResult<Self> {
217		let type_parameters = reader
218			.conditional_next(|token| *token == TSXToken::OpenChevron)
219			.is_some()
220			.then(|| {
221				parse_bracketed(reader, state, options, None, TSXToken::CloseChevron)
222					.map(|(params, _, _)| params)
223			})
224			.transpose()?;
225		let parameters = FunctionParameters::from_reader(reader, state, options)?;
226		let return_type = reader
227			.conditional_next(|tok| options.type_annotations && matches!(tok, TSXToken::Colon))
228			.is_some()
229			.then(|| TypeAnnotation::from_reader(reader, state, options))
230			.transpose()?;
231
232		if let Some(token) = T::get_parameter_body_boundary_token() {
233			reader.expect_next(token)?;
234		}
235		let body = T::Body::from_reader(reader, state, options)?;
236		let body_pos = body.get_position();
237		let end_pos = if body_pos == Span::NULL {
238			return_type.as_ref().map_or(parameters.position, ASTNode::get_position)
239		} else {
240			body_pos
241		};
242
243		let position =
244			header_left.unwrap_or_else(|| parameters.position.get_start()).union(end_pos);
245
246		Ok(Self { header, name, type_parameters, parameters, return_type, body, position })
247	}
248}
249
250/// Visiting logic: TODO make visiting macro better and remove
251impl<T: FunctionBased> Visitable for FunctionBase<T>
252where
253	T::Body: Visitable,
254	// T::Parameters: Visitable,
255{
256	fn visit<TData>(
257		&self,
258		visitors: &mut (impl crate::VisitorReceiver<TData> + ?Sized),
259		data: &mut TData,
260		options: &VisitOptions,
261		chain: &mut temporary_annex::Annex<crate::Chain>,
262	) {
263		// Don't think there is anything useful about visiting header
264		// self.header.visit(visitors, data, options, chain);
265		T::visit_name(&self.name, visitors, data, options, chain);
266		if options.visit_nested_blocks || chain.is_empty() {
267			self.parameters.visit(visitors, data, options, chain);
268			self.body.visit(visitors, data, options, chain);
269		}
270	}
271
272	fn visit_mut<TData>(
273		&mut self,
274		visitors: &mut (impl crate::VisitorMutReceiver<TData> + ?Sized),
275		data: &mut TData,
276		options: &VisitOptions,
277		chain: &mut temporary_annex::Annex<crate::Chain>,
278	) {
279		// Don't think there is anything useful about visiting header
280		// self.header.visit_mut(visitors, data, options, chain);
281		T::visit_name_mut(&mut self.name, visitors, data, options, chain);
282		if options.visit_nested_blocks || chain.is_empty() {
283			self.parameters.visit_mut(visitors, data, options, chain);
284			self.body.visit_mut(visitors, data, options, chain);
285		}
286	}
287}
288
289/// Base for all functions with the `function` keyword
290#[derive(Debug, Clone, PartialEq, Hash)]
291pub struct GeneralFunctionBase<T: ExpressionOrStatementPosition>(PhantomData<T>);
292
293pub type ExpressionFunction = FunctionBase<GeneralFunctionBase<ExpressionPosition>>;
294#[cfg_attr(target_family = "wasm", wasm_bindgen::prelude::wasm_bindgen(typescript_custom_section))]
295#[allow(dead_code)]
296const TYPES_EXPRESSION_FUNCTION: &str = r"
297	export interface ExpressionFunction extends FunctionBase {
298		header: FunctionHeader,
299		body: Block,
300		name: ExpressionPosition,
301		parameters: FunctionParameters<null, null>
302	}
303";
304
305#[allow(clippy::similar_names)]
306impl<T: ExpressionOrStatementPosition> FunctionBased for GeneralFunctionBase<T> {
307	type Name = T;
308	type Header = FunctionHeader;
309	type LeadingParameter = Option<ThisParameter>;
310	type ParameterVisibility = ();
311	type Body = T::FunctionBody;
312
313	fn has_body(body: &Self::Body) -> bool {
314		T::has_function_body(body)
315	}
316
317	fn header_and_name_from_reader(
318		reader: &mut impl TokenReader<TSXToken, crate::TokenStart>,
319		state: &mut crate::ParsingState,
320		options: &crate::ParseOptions,
321	) -> ParseResult<(HeadingAndPosition<Self>, Self::Name)> {
322		let header = FunctionHeader::from_reader(reader, state, options)?;
323		let name = T::from_reader(reader, state, options)?;
324		Ok(((Some(header.get_position().get_start()), header), name))
325	}
326
327	fn header_and_name_to_string_from_buffer<U: source_map::ToString>(
328		buf: &mut U,
329		header: &Self::Header,
330		name: &Self::Name,
331		options: &crate::ToStringOptions,
332		local: crate::LocalToStringInformation,
333	) {
334		header.to_string_from_buffer(buf, options, local);
335		if let Some(name) = name.as_option_str() {
336			buf.push_str(name);
337		}
338	}
339
340	fn visit_name<TData>(
341		name: &Self::Name,
342		visitors: &mut (impl crate::VisitorReceiver<TData> + ?Sized),
343		data: &mut TData,
344		_options: &VisitOptions,
345		chain: &mut temporary_annex::Annex<crate::Chain>,
346	) {
347		visitors.visit_variable(
348			&ImmutableVariableOrProperty::FunctionName(name.as_option_variable_identifier()),
349			data,
350			chain,
351		);
352	}
353
354	fn visit_name_mut<TData>(
355		name: &mut Self::Name,
356		visitors: &mut (impl crate::VisitorMutReceiver<TData> + ?Sized),
357		data: &mut TData,
358		_options: &VisitOptions,
359		chain: &mut temporary_annex::Annex<crate::Chain>,
360	) {
361		visitors.visit_variable_mut(
362			&mut MutableVariableOrProperty::FunctionName(name.as_option_variable_identifier_mut()),
363			data,
364			chain,
365		);
366	}
367
368	fn get_name(name: &Self::Name) -> Option<&str> {
369		name.as_option_str()
370	}
371}
372
373#[cfg(feature = "extras")]
374#[derive(Debug, PartialEq, Clone)]
375#[apply(derive_ASTNode)]
376pub enum FunctionLocationModifier {
377	Server,
378	Worker,
379}
380
381#[derive(Debug, PartialEq, Clone)]
382#[apply(derive_ASTNode)]
383pub enum FunctionHeader {
384	VirginFunctionHeader {
385		is_async: bool,
386		#[cfg(feature = "extras")]
387		location: Option<FunctionLocationModifier>,
388		generator_star_token_position: Option<Span>,
389		position: Span,
390	},
391	#[cfg(feature = "extras")]
392	ChadFunctionHeader {
393		is_async: bool,
394		is_generator: bool,
395		location: Option<FunctionLocationModifier>,
396		position: Span,
397	},
398}
399
400impl ASTNode for FunctionHeader {
401	fn get_position(&self) -> Span {
402		match self {
403			FunctionHeader::VirginFunctionHeader { position, .. } => *position,
404			#[cfg(feature = "extras")]
405			FunctionHeader::ChadFunctionHeader { position, .. } => *position,
406		}
407	}
408
409	fn from_reader(
410		reader: &mut impl TokenReader<TSXToken, crate::TokenStart>,
411		state: &mut crate::ParsingState,
412		_options: &ParseOptions,
413	) -> ParseResult<Self> {
414		let async_start =
415			state.optionally_expect_keyword(reader, TSXKeyword::Async).map(|kw| kw.get_start());
416		parse_special_then_regular_header(reader, state, async_start)
417	}
418
419	fn to_string_from_buffer<T: source_map::ToString>(
420		&self,
421		buf: &mut T,
422		_options: &crate::ToStringOptions,
423		_local: crate::LocalToStringInformation,
424	) {
425		if self.is_async() {
426			buf.push_str("async ");
427		}
428		buf.push_str("function");
429		if self.is_generator() {
430			buf.push_str("* ");
431		} else {
432			buf.push(' ');
433		}
434	}
435}
436
437pub(crate) fn parse_special_then_regular_header(
438	reader: &mut impl TokenReader<TSXToken, TokenStart>,
439	state: &mut crate::ParsingState,
440	async_kw_pos: Option<TokenStart>,
441) -> Result<FunctionHeader, crate::ParseError> {
442	#[cfg(feature = "extras")]
443	{
444		let next_generator = reader
445			.conditional_next(|tok| matches!(tok, TSXToken::Keyword(crate::TSXKeyword::Generator)));
446
447		if let Some(token) = next_generator {
448			let span = token.get_span();
449			let location = parse_function_location(reader);
450
451			let pos = state.expect_keyword_get_full_span(reader, TSXKeyword::Function)?;
452			let position = span.union(pos);
453
454			Ok(FunctionHeader::ChadFunctionHeader {
455				is_async: async_kw_pos.is_some(),
456				location,
457				is_generator: true,
458				position,
459			})
460		} else {
461			parse_regular_header(reader, state, async_kw_pos)
462		}
463	}
464
465	#[cfg(not(feature = "extras"))]
466	parse_regular_header(reader, state, async_kw_pos)
467}
468
469#[cfg(feature = "extras")]
470pub(crate) fn parse_function_location(
471	reader: &mut impl TokenReader<TSXToken, TokenStart>,
472) -> Option<FunctionLocationModifier> {
473	if let Some(Token(TSXToken::Keyword(TSXKeyword::Server | TSXKeyword::Worker), _)) =
474		reader.peek()
475	{
476		Some(match reader.next().unwrap() {
477			Token(TSXToken::Keyword(TSXKeyword::Server), _) => FunctionLocationModifier::Server,
478			Token(TSXToken::Keyword(TSXKeyword::Worker), _) => FunctionLocationModifier::Worker,
479			_ => unreachable!(),
480		})
481	} else {
482		None
483	}
484}
485
486fn parse_regular_header(
487	reader: &mut impl TokenReader<TSXToken, TokenStart>,
488	state: &mut crate::ParsingState,
489	async_kw_pos: Option<TokenStart>,
490) -> Result<FunctionHeader, crate::ParseError> {
491	#[cfg(feature = "extras")]
492	let location = parse_function_location(reader);
493
494	let function_start = state.expect_keyword(reader, TSXKeyword::Function)?;
495	let is_async = async_kw_pos.is_some();
496
497	let generator_star_token_position = reader
498		.conditional_next(|tok| matches!(tok, TSXToken::Multiply))
499		.map(|token| token.get_span());
500
501	let start = async_kw_pos.unwrap_or(function_start);
502
503	let position = if let Some(ref generator_star_token_position) = generator_star_token_position {
504		start.union(generator_star_token_position)
505	} else {
506		function_start.with_length(TSXKeyword::Function.length() as usize)
507	};
508
509	Ok(FunctionHeader::VirginFunctionHeader {
510		is_async,
511		generator_star_token_position,
512		position,
513		#[cfg(feature = "extras")]
514		location,
515	})
516}
517
518impl FunctionHeader {
519	#[must_use]
520	pub fn is_generator(&self) -> bool {
521		match self {
522			FunctionHeader::VirginFunctionHeader {
523				generator_star_token_position: generator_star_token_pos,
524				..
525			} => generator_star_token_pos.is_some(),
526			#[cfg(feature = "extras")]
527			FunctionHeader::ChadFunctionHeader { is_generator, .. } => *is_generator,
528		}
529	}
530
531	#[must_use]
532	pub fn is_async(&self) -> bool {
533		match self {
534			FunctionHeader::VirginFunctionHeader { is_async, .. } => *is_async,
535			#[cfg(feature = "extras")]
536			FunctionHeader::ChadFunctionHeader { is_async, .. } => *is_async,
537		}
538	}
539
540	#[cfg(feature = "extras")]
541	#[must_use]
542	pub fn get_location(&self) -> Option<&FunctionLocationModifier> {
543		match self {
544			FunctionHeader::VirginFunctionHeader { location, .. }
545			| FunctionHeader::ChadFunctionHeader { location, .. } => location.as_ref(),
546		}
547	}
548}
549
550/// This structure removes possible invalid combinations with async
551#[derive(Eq, PartialEq, Clone, Debug)]
552#[apply(derive_ASTNode)]
553pub enum MethodHeader {
554	Get,
555	Set,
556	Regular { is_async: bool, generator: Option<GeneratorSpecifier> },
557}
558
559impl Default for MethodHeader {
560	fn default() -> Self {
561		Self::Regular { is_async: false, generator: None }
562	}
563}
564
565impl MethodHeader {
566	pub(crate) fn to_string_from_buffer<T: source_map::ToString>(&self, buf: &mut T) {
567		match self {
568			MethodHeader::Get => buf.push_str("get "),
569			MethodHeader::Set => buf.push_str("set "),
570			MethodHeader::Regular { is_async, generator } => {
571				if *is_async {
572					buf.push_str("async ");
573				}
574				if let Some(_generator) = generator {
575					buf.push('*');
576				}
577			}
578		}
579	}
580
581	pub(crate) fn from_reader(reader: &mut impl TokenReader<TSXToken, crate::TokenStart>) -> Self {
582		match reader.peek() {
583			Some(Token(TSXToken::Keyword(TSXKeyword::Get), _)) => {
584				let _ = reader.next();
585				MethodHeader::Get
586			}
587			Some(Token(TSXToken::Keyword(TSXKeyword::Set), _)) => {
588				let _ = reader.next();
589				MethodHeader::Set
590			}
591			_ => {
592				let is_async = reader
593					.conditional_next(|tok| matches!(tok, TSXToken::Keyword(TSXKeyword::Async)))
594					.is_some();
595
596				let generator = GeneratorSpecifier::from_reader(reader);
597				MethodHeader::Regular { is_async, generator }
598			}
599		}
600	}
601
602	#[must_use]
603	pub fn is_async(&self) -> bool {
604		matches!(self, Self::Regular { is_async: true, .. })
605	}
606
607	#[must_use]
608	pub fn is_generator(&self) -> bool {
609		matches!(self, Self::Regular { generator: Some(_), .. })
610	}
611
612	#[must_use]
613	pub fn is_no_modifiers(&self) -> bool {
614		matches!(self, Self::Regular { is_async: false, generator: None })
615	}
616}
617
618#[derive(Eq, PartialEq, Clone, Debug)]
619#[apply(derive_ASTNode)]
620pub enum GeneratorSpecifier {
621	Star(Span),
622	#[cfg(feature = "extras")]
623	Keyword,
624}
625
626impl GeneratorSpecifier {
627	pub(crate) fn from_reader(
628		reader: &mut impl TokenReader<TSXToken, crate::TokenStart>,
629	) -> Option<Self> {
630		match reader.peek() {
631			Some(Token(TSXToken::Multiply, _)) => {
632				Some(GeneratorSpecifier::Star(reader.next().unwrap().get_span()))
633			}
634			#[cfg(feature = "extras")]
635			Some(Token(TSXToken::Keyword(TSXKeyword::Generator), _)) => Some(GeneratorSpecifier::Keyword),
636			_ => None,
637		}
638	}
639}
640
641/// Accounts for methods named `get` and `set` etc
642pub(crate) fn get_method_name<T: PropertyKeyKind + 'static>(
643	reader: &mut impl TokenReader<TSXToken, TokenStart>,
644	state: &mut crate::ParsingState,
645	options: &ParseOptions,
646) -> Result<(MethodHeader, WithComment<PropertyKey<T>>), crate::ParseError> {
647	let is_named_get_set_or_async = matches!(
648		reader.peek(),
649		Some(Token(TSXToken::Keyword(kw), _))
650		if kw.is_in_method_header()
651	) && matches!(
652		reader.peek_n(1),
653		Some(Token(
654			TSXToken::OpenParentheses
655				| TSXToken::Colon
656				| TSXToken::OpenChevron
657				| TSXToken::CloseBrace
658				| TSXToken::Comma
659				| TSXToken::QuestionMark
660				| TSXToken::OptionalMember,
661			_
662		))
663	);
664
665	let (function_header, key) = if is_named_get_set_or_async {
666		let token = reader.next().unwrap();
667		let position = token.get_span();
668		let name = match token.0 {
669			TSXToken::Keyword(TSXKeyword::Get) => "get",
670			TSXToken::Keyword(TSXKeyword::Set) => "set",
671			TSXToken::Keyword(TSXKeyword::Async) => "async",
672			#[cfg(feature = "extras")]
673			TSXToken::Keyword(TSXKeyword::Generator) => "generator",
674			_ => unreachable!(),
675		};
676		// TODO
677		let new_public = T::new_public();
678		(
679			MethodHeader::default(),
680			WithComment::None(PropertyKey::Identifier(name.to_owned(), position, new_public)),
681		)
682	} else {
683		(MethodHeader::from_reader(reader), WithComment::from_reader(reader, state, options)?)
684	};
685	Ok((function_header, key))
686}
687
688// #[cfg(feature = "full-typescript")]
689/// None if overloaded (declaration only)
690#[apply(derive_ASTNode)]
691#[derive(Debug, Clone, PartialEq, visitable_derive::Visitable)]
692pub struct FunctionBody(pub Option<Block>);
693
694// #[cfg(not(feature = "full-typescript"))]
695// pub type FunctionBody = Block;
696
697impl ASTNode for FunctionBody {
698	fn get_position(&self) -> Span {
699		self.0.as_ref().map_or(source_map::Nullable::NULL, |Block(_, pos)| *pos)
700	}
701
702	fn from_reader(
703		reader: &mut impl TokenReader<TSXToken, crate::TokenStart>,
704		state: &mut crate::ParsingState,
705		options: &ParseOptions,
706	) -> ParseResult<Self> {
707		let inner = if !options.type_annotations
708			|| matches!(reader.peek(), Some(Token(TSXToken::OpenBrace, _)))
709		{
710			Some(Block::from_reader(reader, state, options)?)
711		} else {
712			None
713		};
714
715		Ok(Self(inner))
716	}
717
718	fn to_string_from_buffer<T: source_map::ToString>(
719		&self,
720		buf: &mut T,
721		options: &crate::ToStringOptions,
722		local: crate::LocalToStringInformation,
723	) {
724		if let Some(ref b) = self.0 {
725			b.to_string_from_buffer(buf, options, local);
726		}
727	}
728}