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
34pub trait FunctionBased: Debug + Clone + PartialEq + Send + Sync {
36 type Header: Debug + Clone + PartialEq + Send + Sync;
38
39 type Name: Debug + Clone + PartialEq + Send + Sync;
41
42 #[cfg(not(feature = "serde-serialize"))]
44 type LeadingParameter: LeadingParameter;
45
46 #[cfg(feature = "serde-serialize")]
48 type LeadingParameter: LeadingParameter + serde::Serialize;
49
50 #[cfg(not(feature = "serde-serialize"))]
52 type ParameterVisibility: ParameterVisibility;
53
54 #[cfg(feature = "serde-serialize")]
56 type ParameterVisibility: ParameterVisibility + serde::Serialize;
57
58 type Body: ASTNode;
60
61 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 #[must_use]
80 fn get_parameter_body_boundary_token() -> Option<TSXToken> {
81 None
82 }
83
84 #[must_use]
86 fn has_body(_: &Self::Body) -> bool {
87 true
88 }
89
90 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 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 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#[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 #[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
250impl<T: FunctionBased> Visitable for FunctionBase<T>
252where
253 T::Body: Visitable,
254 {
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 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 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#[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#[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
641pub(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 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#[apply(derive_ASTNode)]
691#[derive(Debug, Clone, PartialEq, visitable_derive::Visitable)]
692pub struct FunctionBody(pub Option<Block>);
693
694impl 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}