tsv_ts 0.8.0

TypeScript/JS formatter, type stripper and acorn-compatible parser — part of tsv
Documentation
// Function declaration parsing

use crate::ast::*;
use crate::lexer::{KeywordKind, TokenKind};
use tsv_lang::{Error, Span};

use super::super::Parser;
use super::super::expression::alloc_expr;

impl<'a, 'arena> Parser<'a, 'arena> {
    pub(super) fn parse_return_statement(&mut self) -> Result<Statement<'arena>, Error> {
        let (start, _) = self.current_pos();

        // Consume 'return' keyword
        debug_assert!(matches!(
            self.current_kind(),
            TokenKind::Keyword(KeywordKind::Return)
        ));
        self.advance()?;

        // ASI Rule: If semicolon present or can insert semicolon, return has no argument.
        // This handles: `return\n1+2` → `return;` (then `1+2;` as separate statement)
        // The `1+2` becomes an unreachable expression statement.
        if self.eat(TokenKind::Semicolon)? || self.can_insert_semicolon() {
            let end = self.prev_token_end() as u32;
            return Ok(Statement {
                span: Span::new(start as u32, end),
                kind: StatementKind::ReturnStatement(ReturnStatement { argument: None }),
            });
        }

        // No ASI - parse the return value expression
        let argument_token = self.current_pos().0;
        let argument = self.parse_expression_ref()?;
        self.restricted_argument_pair(argument, argument_token)?;
        let end = self.semicolon_end()?;

        Ok(Statement {
            span: Span::new(start as u32, end),
            kind: StatementKind::ReturnStatement(ReturnStatement {
                argument: Some(argument),
            }),
        })
    }

    pub(super) fn parse_function_declaration(&mut self) -> Result<Statement<'arena>, Error> {
        self.parse_function_or_overload(false, None)
    }

    /// Parse function declaration or overload signature
    ///
    /// Function overloads end with semicolon instead of body:
    /// ```typescript
    /// function check(x: unknown): x is string;  // overload - TSDeclareFunction
    /// function check(x: unknown): x is number;  // overload - TSDeclareFunction
    /// function check(x: unknown) { ... }         // implementation - FunctionDeclaration
    /// ```
    ///
    /// `start_override` is the statement's own start when the caller consumed a
    /// leading token of its own (`async`), so the span is built right rather than
    /// patched afterwards — both variants are arena-boxed and a `&'arena` head has
    /// no `&mut` to patch through.
    fn parse_function_or_overload(
        &mut self,
        is_async: bool,
        start_override: Option<usize>,
    ) -> Result<Statement<'arena>, Error> {
        let start = start_override.unwrap_or_else(|| self.current_pos().0);

        // Consume 'function' keyword
        debug_assert!(matches!(
            self.current_kind(),
            TokenKind::Keyword(KeywordKind::Function)
        ));
        self.advance()?;

        // Check for generator: function*
        let is_generator = self.eat(TokenKind::Star)?;

        // Parse function name (required for declarations)
        // Keywords like `object` and `async` can be function names; `await` is a
        // valid name only at Script `[~Await]` (reserved at Module / `[+Await]`).
        let (id_start, id_end) = self.current_pos();
        let Some(name) = self.try_function_name() else {
            return Err(self.error_expected_after("function name", "function"));
        };
        self.advance()?;

        let id = Identifier::simple(name, Span::new(id_start as u32, id_end as u32));

        // Parse type parameters (TypeScript generics): function foo<T>()
        let type_parameters = self.parse_optional_type_parameters()?;

        // Capture paren position before parsing params (for comment detection)
        let (params_start, _) = self.current_pos();

        // Parse parameter list (in the function's own `[Await]`/`[Yield]` context —
        // async/generator params are `[+Await]`/`[+Yield]`).
        let params: &'arena [Expression<'arena>] = self
            .with_fn_context(is_async, is_generator, Self::parse_parameter_list)?
            .into_bump_slice();

        // Check for return type annotation
        let return_type = self.parse_optional_return_type()?;

        // Check if this is an overload (ends with ; or no body) or implementation (has body)
        // Overload signatures don't have a body block - they end with ; or ASI
        if !matches!(self.current_kind(), TokenKind::BraceOpen) {
            // Function overload signature - parse as TSDeclareFunction
            let end = self.semicolon_end()?;

            Ok(Statement::from_ts_declare_function(self.arena.alloc(
                TSDeclareFunction {
                    id,
                    type_parameters,
                    params,
                    return_type,
                    declare: false, // Not a `declare function`, just an overload
                    r#async: is_async,
                    generator: is_generator,
                    span: Span::new(start as u32, end),
                },
            )))
        } else {
            // Function implementation - parse body (the function's `[Await]`/`[Yield]` context).
            let body = self.with_fn_context(is_async, is_generator, Self::parse_function_body)?;
            let end = body.span.end;

            Ok(Statement::from_function_declaration(self.arena.alloc(
                FunctionDeclaration {
                    id: Some(id),
                    type_parameters,
                    params,
                    return_type,
                    body,
                    generator: is_generator,
                    r#async: is_async,
                    params_start: params_start as u32,
                    span: Span::new(start as u32, end),
                },
            )))
        }
    }

    /// Parse async function declaration: `async function foo() {}` or `async function* foo() {}`
    pub(super) fn parse_async_function_declaration(&mut self) -> Result<Statement<'arena>, Error> {
        let (start, _) = self.current_pos();

        // Consume 'async' keyword
        debug_assert!(matches!(
            self.current_kind(),
            TokenKind::Keyword(KeywordKind::Async)
        ));
        self.advance()?;

        // Parse the function (which may be an overload signature or implementation),
        // handing it the `async` keyword's position so its span covers it.
        self.parse_function_or_overload(true, Some(start))
    }

    /// Inner function that can return either FunctionDeclaration or TSDeclareFunction
    ///
    /// Used by export default and export named to handle both regular and ambient contexts
    pub(super) fn parse_function_declaration_or_declare(
        &mut self,
        is_async: bool,
    ) -> Result<ExportFunctionDeclaration<'arena>, Error> {
        let (start, _) = self.current_pos();

        // Consume 'function' keyword
        debug_assert!(matches!(
            self.current_kind(),
            TokenKind::Keyword(KeywordKind::Function)
        ));
        self.advance()?;

        // Check for generator: function*
        let is_generator = self.eat(TokenKind::Star)?;

        // Parse function name (required for declarations, optional for export default)
        // Keywords like `object` and `async` can be function names; `await` is a
        // valid name only at Script `[~Await]` (reserved at Module / `[+Await]`).
        let id = if let Some(name) = self.try_function_name() {
            let (id_start, id_end) = self.current_pos();
            self.advance()?;

            Some(Identifier::simple(
                name,
                Span::new(id_start as u32, id_end as u32),
            ))
        } else {
            None
        };

        // Parse type parameters
        let type_parameters = self.parse_optional_type_parameters()?;

        // Capture paren position before parsing params (for comment detection)
        let (params_start, _) = self.current_pos();

        // Parse parameter list (in the function's own `[Await]`/`[Yield]` context).
        let params: &'arena [Expression<'arena>] = self
            .with_fn_context(is_async, is_generator, Self::parse_parameter_list)?
            .into_bump_slice();

        // Check for return type annotation
        let return_type = self.parse_optional_return_type()?;

        // Check if this is an overload signature (no body) or implementation (has body)
        if !matches!(self.current_kind(), TokenKind::BraceOpen) {
            // No body - this is a declare function (ambient context)
            let end = self.semicolon_end()?;

            Ok(ExportFunctionDeclaration::Declare(TSDeclareFunction {
                id: id.unwrap_or_else(|| {
                    // Anonymous: empty name over a zero-width span (span-identity).
                    Identifier::simple(
                        IdentName {
                            escaped: None,
                            raw_len: 0,
                            // An empty name holds no byte, width-relevant or not.
                            plain_ascii: true,
                        },
                        Span::new(start as u32, start as u32),
                    )
                }),
                type_parameters,
                params,
                return_type,
                declare: false,
                r#async: is_async,
                generator: is_generator,
                span: Span::new(start as u32, end),
            }))
        } else {
            // Has body - regular function declaration (the function's `[Await]`/`[Yield]` context).
            let body = self.with_fn_context(is_async, is_generator, Self::parse_function_body)?;
            let end = body.span.end;

            Ok(ExportFunctionDeclaration::Declaration(
                FunctionDeclaration {
                    id,
                    type_parameters,
                    params,
                    return_type,
                    body,
                    generator: is_generator,
                    r#async: is_async,
                    params_start: params_start as u32,
                    span: Span::new(start as u32, end),
                },
            ))
        }
    }

    /// Parse a function expression: `function() {}` or `function name<T>() {}`
    ///
    /// Function expressions are similar to function declarations but:
    /// - The name is always optional
    /// - They appear in expression position
    pub(in crate::parser) fn parse_function_expression(
        &mut self,
    ) -> Result<&'arena Expression<'arena>, Error> {
        let (start, _) = self.current_pos();
        self.parse_function_expression_inner(start, false)
    }

    /// Parse an async function expression: `async function() {}` or `async function*() {}`
    ///
    /// Called when we've already seen `async` and are at `function`.
    pub(in crate::parser) fn parse_async_function_expression(
        &mut self,
        start: usize,
    ) -> Result<&'arena Expression<'arena>, Error> {
        self.parse_function_expression_inner(start, true)
    }

    /// Core function expression parsing logic
    fn parse_function_expression_inner(
        &mut self,
        start: usize,
        is_async: bool,
    ) -> Result<&'arena Expression<'arena>, Error> {
        // Consume 'function' keyword
        debug_assert!(matches!(
            self.current_kind(),
            TokenKind::Keyword(KeywordKind::Function)
        ));
        self.advance()?;

        // Check for generator: function*
        let is_generator = self.eat(TokenKind::Star)?;

        // Parse the optional name in the function expression's own `[Await]`
        // context: a `FunctionExpression` name is `[~Await]` (non-async) /
        // `[+Await]` (async), so `await` is a valid name in a non-async function
        // expression even inside a `[+Await]` enclosing scope (e.g. a static
        // block), but not in an async one.
        //
        // `try_function_name` is the same channel the DECLARATION path above takes,
        // and carries that `await` rule itself. Reading the token kind here instead
        // saw only the words the lexer never made a `Keyword`, so the declaration
        // `function string() {}` parsed while the expression form did not.
        //
        // ⚠️ Sharing the channel inherits its width, deliberately: `can_be_identifier`
        // also admits `let` / `yield`, whose bar is a strict-mode early error tsv defers
        // (tsc's parser accepts them too). That is one question to answer in one place;
        // re-narrowing here would mint a third name channel and restore the very
        // asymmetry this call avoids. `void` does NOT belong —
        // it is a genuine `ReservedWord`, and this is the only position that can see
        // the difference, which is why the answer lives in `can_be_identifier` itself.
        let id = self.with_fn_context(is_async, is_generator, |p| {
            if let Some(name) = p.try_function_name() {
                let (id_start, id_end) = p.current_pos();
                p.advance()?;
                Ok(Some(Identifier::simple(
                    name,
                    Span::new(id_start as u32, id_end as u32),
                )))
            } else {
                Ok(None)
            }
        })?;

        // Parse type parameters (TypeScript generics): function<T>()
        let type_parameters = self.parse_optional_type_parameters()?;

        // Capture paren position before parsing params (for comment detection)
        let (params_start, _) = self.current_pos();

        // Param defaults (`[+In]`) and the function body are a fresh `[+In]`
        // context, so `in` is the binary operator even when this function
        // expression sits in a for-header init.
        let (params, return_type, body) = self.with_fn_context(is_async, is_generator, |p| {
            p.with_body_frame(|p| {
                let params: &'arena [Expression<'arena>] =
                    p.parse_parameter_list()?.into_bump_slice();
                let return_type = p.parse_optional_return_type()?;
                // three wire levels: the `FunctionExpression`, its body's `BlockStatement`
                // and its statements — a declaration's own level is its statement's
                let body = p.parse_function_body_levels(3)?;
                Ok((params, return_type, body))
            })
        })?;
        let end = body.span.end;

        Ok(alloc_expr(
            self.arena,
            Expression::from_function_expression(self.arena.alloc(FunctionExpression {
                id,
                type_parameters,
                params,
                return_type,
                body,
                generator: is_generator,
                r#async: is_async,
                params_start: params_start as u32,
                span: Span::new(start as u32, end),
            })),
        ))
    }
}