//! Best-effort error recovery utilities.
//!
//! The parser recovers from errors by skipping tokens until a synchronization
//! token is found. The sync target is chosen from [`SyncLevel`] based on
//! the error kind. Methods on [`ParseResult`] let callers widen the error span,
//! synchronize, report, and produce fallback values.
//!
//! General idea is that any method that returns something other than an error
//! can be assumed to have reported it.

#![allow(dead_code)]
// TODO: remove dead code

use crate::ast::{CallableKind, IdentifierToken, QualifiedIdentifier};
use crate::diagnostics::diagnostic_from_parse_error;
use crate::lexer::{Token, TokenPosition, TokenSpan};
use crate::parser::{ParseError, ParseResult, Parser};

/// Synchronization groups the parser can stop at during recovery.
///
/// The variant order defines recovery strength: later variants are treated as
/// "stronger" boundaries, so synchronizing to a weaker level will also stop
/// at any stronger one.
///
/// This enum is intentionally coarse-grained and semantic. It is not meant to
/// encode arbitrary token sets.
#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq, PartialOrd, Ord)]
pub enum SyncLevel {
    /// Tokens that can reasonably continue or restart an expression.
    ///
    /// This is the loosest recovery level.
    ExpressionStart,

    /// Separator between homogeneous list elements, e.g. `,`.
    ///
    /// Synchronizing here also stops at closing delimiters and stronger
    /// structural boundaries.
    ListSeparator,

    /// Closing `>` of an angle-bracket-delimited type/class argument list.
    CloseAngleBracket,

    /// Closing `]` of an index or bracket-delimited construct.
    CloseBracket,

    /// Closing `)` of a parenthesized/grouped construct.
    CloseParenthesis,
    ColonDelimiter,

    /// A statement boundary or statement starter.
    ///
    /// Includes keywords that begin standalone statements /
    /// statement-like control-flow forms.
    StatementStart,

    /// Statement terminator `;`.
    StatementTerminator,

    /// Start of a `switch` arm.
    ///
    /// This is useful because `case` / `default` are stronger boundaries than
    /// ordinary statements inside switch parsing.
    SwitchSectionBoundary,

    /// Start of a declaration-like item.
    ///
    /// Used for recovery in declaration-containing bodies where the next
    /// sensible point is "the next member/declaration" rather than merely
    /// "some statement".
    DeclarationStart,

    /// A hard block boundary.
    ///
    /// This is the strongest normal recovery point.
    BlockBoundary,
}

impl SyncLevel {
    /// Converts a token to its synchronization class, if any.
    const fn for_token(token: Token) -> Option<Self> {
        use crate::lexer::Keyword;
        use SyncLevel::{
            BlockBoundary, CloseAngleBracket, CloseBracket, CloseParenthesis, ColonDelimiter,
            DeclarationStart, ExpressionStart, ListSeparator, StatementStart, StatementTerminator,
            SwitchSectionBoundary,
        };

        match token {
            // List / delimiter boundaries
            Token::Comma => Some(ListSeparator),
            Token::Greater => Some(CloseAngleBracket),
            Token::RightParenthesis => Some(CloseParenthesis),
            Token::RightBracket => Some(CloseBracket),

            // Statement-level boundaries
            Token::Keyword(
                Keyword::If
                | Keyword::Else
                | Keyword::Switch
                | Keyword::For
                | Keyword::ForEach
                | Keyword::While
                | Keyword::Do
                | Keyword::Until
                | Keyword::Return
                | Keyword::Break
                | Keyword::Continue
                | Keyword::Local,
            ) => Some(StatementStart),

            Token::Colon => Some(ColonDelimiter),

            Token::Semicolon => Some(StatementTerminator),

            // Switch-specific stronger boundary
            Token::Keyword(Keyword::Case | Keyword::Default) => Some(SwitchSectionBoundary),

            // Declaration/member starts
            Token::Keyword(
                Keyword::Class
                | Keyword::Struct
                | Keyword::Enum
                | Keyword::State
                | Keyword::Function
                | Keyword::Event
                | Keyword::Delegate
                | Keyword::Operator
                | Keyword::Var
                | Keyword::Replication
                | Keyword::NativeReplication
                | Keyword::DefaultProperties
                | Keyword::CppText
                | Keyword::CppStruct,
            )
            | Token::ExecDirective => Some(DeclarationStart),

            // Hard structural stop
            Token::LeftBrace | Token::CppBlock | Token::RightBrace => Some(BlockBoundary),

            _ => {
                if token.is_definitely_not_expression_start() {
                    None
                } else {
                    Some(ExpressionStart)
                }
            }
        }
    }

    fn fallback_sync_level_for_delimiter_start(token: Option<Token>) -> SyncLevel {
        match token {
            Some(Token::LeftParenthesis) => SyncLevel::CloseParenthesis,
            Some(Token::LeftBracket) => SyncLevel::CloseBracket,
            Some(Token::LeftBrace) => SyncLevel::BlockBoundary,
            _ => SyncLevel::CloseParenthesis,
        }
    }

    fn is_opening_delimiter(token: Token) -> bool {
        matches!(
            token,
            Token::LeftParenthesis | Token::LeftBracket | Token::LeftBrace
        )
    }
}

impl<'src, 'arena> Parser<'src, 'arena> {
    /// Converts a parse error into a diagnostic and queues it.
    ///
    /// Placeholder implementation.
    pub fn report_error(&mut self, error: ParseError) {
        //self.diagnostics.push(Diagnostic::from(error));
        self.diagnostics
            .push(diagnostic_from_parse_error(error, self.file()));
    }

    /// Reports a parser error with [`crate::parser::ParseErrorKind`] at
    /// the current location and queues an appropriate diagnostic.
    pub fn report_error_here(&mut self, error_kind: crate::parser::ParseErrorKind) {
        let new_error = self.make_error_at_last_consumed(error_kind);
        self.report_error(new_error);
    }

    /// Skips tokens until a token with `min_sync` level or stronger is found.
    ///
    /// Reaches end-of-file if no qualifying token is found.
    pub(crate) fn recover_until(&mut self, min_sync: SyncLevel) {
        while let Some(next_token) = self.peek_token() {
            if let Some(next_token_sync_level) = SyncLevel::for_token(next_token)
                && next_token_sync_level >= min_sync
            {
                break;
            }
            self.advance();
        }
    }

    /// Skips tokens until a token with exactly `level` is found, then consumes
    /// that token.
    ///
    /// This mirrors the behavior used by [`ResultRecoveryExt::sync_error_at`]:
    /// stronger sync tokens can stop [`Self::recover_until`], but they are not
    /// consumed unless they are exactly the requested level.
    fn recover_at_sync_level(&mut self, level: SyncLevel) {
        self.recover_until(level);

        if self
            .peek_token()
            .and_then(SyncLevel::for_token)
            .is_some_and(|next_level| next_level == level)
        {
            self.advance();
        }
    }

    /// Recovers up to the lexer-produced matching delimiter for
    /// `delimiter_start`, if possible, but does not consume it.
    ///
    /// If `delimiter_start` is not an opening delimiter, if no match is known,
    /// or if the parser has already moved past the matching delimiter, this
    /// falls back to ordinary sync-level recovery inferred from
    /// `delimiter_start`.
    pub(crate) fn recover_until_matching_delimiter_or_sync(
        &mut self,
        delimiter_start: TokenPosition,
    ) {
        let start_token = self.file().token_at(delimiter_start).map(|data| data.token);
        let fallback_level = SyncLevel::fallback_sync_level_for_delimiter_start(start_token);

        let Some(start_token) = start_token else {
            self.recover_until(fallback_level);
            return;
        };

        if !SyncLevel::is_opening_delimiter(start_token) {
            self.recover_until(fallback_level);
            return;
        }

        let Some(target) = self.file().matching_delimiter(delimiter_start) else {
            self.recover_until(fallback_level);
            return;
        };

        if self.peek_position_or_eof() > target {
            self.recover_until(fallback_level);
            return;
        }

        while let Some(position) = self.peek_position() {
            if position >= target {
                break;
            }
            self.advance();
        }
    }

    /// Recovers by using the lexer-produced matching delimiter for
    /// `delimiter_start`, if possible.
    ///
    /// If `delimiter_start` is not an opening delimiter, if no match is known,
    /// or if the parser has already moved past the matching delimiter, this
    /// falls back to ordinary sync-level recovery inferred from
    /// `delimiter_start`.
    pub(crate) fn recover_at_matching_delimiter_or_sync(&mut self, delimiter_start: TokenPosition) {
        let start_token = self.file().token_at(delimiter_start).map(|data| data.token);
        let fallback_level = SyncLevel::fallback_sync_level_for_delimiter_start(start_token);

        let Some(start_token) = start_token else {
            self.recover_at_sync_level(fallback_level);
            return;
        };

        if !SyncLevel::is_opening_delimiter(start_token) {
            self.recover_at_sync_level(fallback_level);
            return;
        }

        let Some(target) = self.file().matching_delimiter(delimiter_start) else {
            self.recover_at_sync_level(fallback_level);
            return;
        };

        if self.peek_position_or_eof() > target {
            self.recover_at_sync_level(fallback_level);
            return;
        }

        while let Some(position) = self.peek_position() {
            if position >= target {
                break;
            }
            self.advance();
        }

        if self.peek_position_or_eof() == target {
            self.advance();
        } else {
            self.recover_at_sync_level(fallback_level);
        }
    }

    /// Reports `error` and returns the recovery fallback for `T`.
    ///
    /// This is the primitive used when parsing must keep going with a
    /// best-effort placeholder value of the expected type.
    #[must_use]
    pub(crate) fn report_error_with_fallback<T>(&mut self, error: ParseError) -> T
    where
        T: RecoveryFallback<'src, 'arena>,
    {
        let fallback = T::fallback_value(self, &error);
        self.report_error(error);
        fallback
    }
}

/// Supplies a fallback value after a parse error so parsing can continue and
/// reveal further errors.
pub trait RecoveryFallback<'src, 'arena>: Sized {
    fn fallback_value(parser: &Parser<'src, 'arena>, err: &ParseError) -> Self;
}

/// Extends [`ParseResult`] with recovery-related methods for
/// fluent error handling.
/// !!!! Can we store a parser reference instead of passing it into every method?
pub trait ResultRecoveryExt<'src, 'arena, T>: Sized {
    /// Extends the left end of the error span to `from`.
    ///
    /// Does nothing if `Self` is `Ok(...)`.
    #[must_use]
    fn widen_error_span_from(self, from: TokenPosition) -> Self;

    fn blame(self, blame_span: TokenSpan) -> Self;
    fn related(self, tag: impl Into<String>, related_span: TokenSpan) -> Self;
    fn with_flag(self, tag: impl Into<String>) -> Self;

    fn blame_token(self, blame_position: TokenPosition) -> Self {
        self.blame(TokenSpan::new(blame_position))
    }

    fn extend_blame_to_next_token(self, parser: &mut Parser<'src, 'arena>) -> Self;

    fn extend_blame_start_to_covered_start(self) -> Self;
    fn extend_blame_end_to_covered_end(self) -> Self;

    // TODO: say that we use textual tags because they are very local to each error and read better
    // than some kind of constant.
    fn related_token(self, tag: impl Into<String>, related_position: TokenPosition) -> Self {
        self.related(tag, TokenSpan::new(related_position))
    }

    fn maybe_related(self, tag: impl Into<String>, related_span: Option<TokenSpan>) -> Self {
        if let Some(related_span) = related_span {
            self.related(tag, related_span)
        } else {
            self
        }
    }

    fn maybe_related_token(
        self,
        tag: impl Into<String>,
        related_position: Option<TokenPosition>,
    ) -> Self {
        if let Some(related_position) = related_position {
            self.related(tag, TokenSpan::new(related_position))
        } else {
            self
        }
    }

    /// Extends the right end of the error span up to but not including
    /// the next token of the given sync `level`.
    ///
    /// Does nothing if `Self` is `Ok(...)`.
    #[must_use]
    fn sync_error_until(self, parser: &mut Parser<'src, 'arena>, level: SyncLevel) -> Self;

    /// Extends the right end of the error span to include the next token of
    /// the given sync `level`.
    ///
    /// Does nothing if `Self` is `Ok(...)`.
    #[must_use]
    fn sync_error_at(self, parser: &mut Parser<'src, 'arena>, level: SyncLevel) -> Self;

    /// Extends the right end of the error span up to, but not including, the
    /// known matching closing delimiter for `delimiter_start`, if that delimiter
    /// is known and has not already been passed.
    ///
    /// If no usable delimiter match exists, falls back to ordinary
    /// [`SyncLevel`]-based recovery. The fallback level is inferred from the
    /// token at `delimiter_start`.
    #[must_use]
    fn sync_error_until_matching_delimiter(
        self,
        parser: &mut Parser<'src, 'arena>,
        delimiter_start: TokenPosition,
    ) -> Self;

    /// Extends the right end of the error span to include the known matching
    /// closing delimiter for `delimiter_start`, if that delimiter is known and
    /// has not already been passed.
    ///
    /// If no usable delimiter match exists, falls back to ordinary
    /// [`SyncLevel`]-based recovery. The fallback level is inferred from the
    /// token at `delimiter_start`:
    ///
    /// - `(` -> [`SyncLevel::CloseParenthesis`]
    /// - `[` -> [`SyncLevel::CloseBracket`]
    /// - `{` -> [`SyncLevel::BlockBoundary`]
    /// - anything else -> [`SyncLevel::CloseParenthesis`]
    #[must_use]
    fn sync_error_at_matching_delimiter(
        self,
        parser: &mut Parser<'src, 'arena>,
        delimiter_start: TokenPosition,
    ) -> Self;

    /// Either returns expected value or its best effort fallback.
    #[must_use]
    fn unwrap_or_fallback(self, parser: &mut Parser<'src, 'arena>) -> T
    where
        T: RecoveryFallback<'src, 'arena>;

    /// Produces the contained value if successful,
    /// or a fallback if an error occurred.
    fn report_error(self, parser: &mut Parser<'src, 'arena>) -> bool;

    fn ok_or_report(self, parser: &mut Parser<'src, 'arena>) -> Option<T>;
}

impl<'src, 'arena, T> ResultRecoveryExt<'src, 'arena, T> for ParseResult<'src, 'arena, T> {
    fn widen_error_span_from(mut self, from: TokenPosition) -> Self {
        if let Err(ref mut error) = self {
            error.covered_span.start = std::cmp::min(error.covered_span.start, from);
        }
        self
    }

    fn blame(self, blame_span: TokenSpan) -> Self {
        self.map_err(|error| error.blame(blame_span))
    }

    fn extend_blame_to_next_token(self, parser: &mut Parser<'src, 'arena>) -> Self {
        self.map_err(|error| error.extend_blame_to_next_token(parser))
    }

    fn extend_blame_start_to_covered_start(self) -> Self {
        self.map_err(|error| error.extend_blame_start_to_covered_start())
    }

    fn extend_blame_end_to_covered_end(self) -> Self {
        self.map_err(|error| error.extend_blame_end_to_covered_end())
    }

    fn related(self, tag: impl Into<String>, related_span: TokenSpan) -> Self {
        self.map_err(|error| error.related(tag, related_span))
    }

    fn with_flag(self, tag: impl Into<String>) -> Self {
        self.map_err(|error| error.with_flag(tag))
    }

    fn sync_error_until(mut self, parser: &mut Parser<'src, 'arena>, level: SyncLevel) -> Self {
        if let Err(ref mut error) = self {
            parser.recover_until(level);
            error.covered_span.end = std::cmp::max(
                error.covered_span.end,
                parser.last_consumed_position_or_start(),
            );
        }
        self
    }

    fn sync_error_at(mut self, parser: &mut Parser<'src, 'arena>, level: SyncLevel) -> Self {
        if let Err(ref mut error) = self {
            parser.recover_until(level);
            // If we're at end-of-file, this'll simply do nothing.
            if parser
                .peek_token()
                .and_then(SyncLevel::for_token)
                .is_some_and(|next_level| next_level == level)
            {
                parser.advance();
            }
            error.covered_span.end = std::cmp::max(
                error.covered_span.end,
                parser.last_consumed_position_or_start(),
            );
        }
        self
    }

    fn sync_error_until_matching_delimiter(
        mut self,
        parser: &mut Parser<'src, 'arena>,
        delimiter_start: TokenPosition,
    ) -> Self {
        if let Err(ref mut error) = self {
            parser.recover_until_matching_delimiter_or_sync(delimiter_start);
            error.covered_span.end = std::cmp::max(
                error.covered_span.end,
                parser.last_consumed_position_or_start(),
            );
        }
        self
    }

    fn sync_error_at_matching_delimiter(
        mut self,
        parser: &mut Parser<'src, 'arena>,
        delimiter_start: TokenPosition,
    ) -> Self {
        if let Err(ref mut error) = self {
            parser.recover_at_matching_delimiter_or_sync(delimiter_start);
            error.covered_span.end = std::cmp::max(
                error.covered_span.end,
                parser.last_consumed_position_or_start(),
            );
        }
        self
    }

    fn unwrap_or_fallback(self, parser: &mut Parser<'src, 'arena>) -> T
    where
        T: RecoveryFallback<'src, 'arena>,
    {
        self.unwrap_or_else(|error| parser.report_error_with_fallback(error))
    }

    fn report_error(self, parser: &mut Parser<'src, 'arena>) -> bool {
        if let Err(error) = self {
            parser.report_error(error);
            true
        } else {
            false
        }
    }

    fn ok_or_report(self, parser: &mut Parser<'src, 'arena>) -> Option<T> {
        match self {
            Ok(value) => Some(value),
            Err(error) => {
                parser.report_error(error);
                None
            }
        }
    }
}

impl<'src, 'arena> ResultRecoveryExt<'src, 'arena, ()> for ParseError {
    fn widen_error_span_from(mut self, from: TokenPosition) -> Self {
        self.covered_span.start = std::cmp::min(self.covered_span.start, from);
        self
    }

    fn blame(mut self, blame_span: TokenSpan) -> Self {
        self.blame_span = blame_span;
        self
    }

    fn extend_blame_to_next_token(mut self, parser: &mut Parser<'src, 'arena>) -> Self {
        self.blame_span.end = std::cmp::max(self.blame_span.end, parser.peek_position_or_eof());
        self
    }

    fn extend_blame_start_to_covered_start(mut self) -> Self {
        self.blame_span.start = std::cmp::min(self.blame_span.start, self.covered_span.start);
        self
    }

    fn extend_blame_end_to_covered_end(mut self) -> Self {
        self.blame_span.end = std::cmp::max(self.blame_span.end, self.covered_span.end);
        self
    }

    fn related(mut self, tag: impl Into<String>, related_span: TokenSpan) -> Self {
        self.related_spans.insert(tag.into(), related_span);
        self
    }

    fn with_flag(mut self, tag: impl Into<String>) -> Self {
        self.flags.insert(tag.into());
        self
    }

    fn sync_error_until(mut self, parser: &mut Parser<'src, 'arena>, level: SyncLevel) -> Self {
        parser.recover_until(level);
        self.covered_span.end = std::cmp::max(
            self.covered_span.end,
            parser.last_consumed_position_or_start(),
        );
        self
    }

    fn sync_error_at(mut self, parser: &mut Parser<'src, 'arena>, level: SyncLevel) -> Self {
        parser.recover_until(level);

        if parser
            .peek_token()
            .and_then(SyncLevel::for_token)
            .is_some_and(|next_level| next_level == level)
        {
            parser.advance();
        }

        self.covered_span.end = std::cmp::max(
            self.covered_span.end,
            parser.last_consumed_position_or_start(),
        );
        self
    }

    fn sync_error_until_matching_delimiter(
        mut self,
        parser: &mut Parser<'src, 'arena>,
        delimiter_start: TokenPosition,
    ) -> Self {
        parser.recover_until_matching_delimiter_or_sync(delimiter_start);
        self.covered_span.end = std::cmp::max(
            self.covered_span.end,
            parser.last_consumed_position_or_start(),
        );
        self
    }

    fn sync_error_at_matching_delimiter(
        mut self,
        parser: &mut Parser<'src, 'arena>,
        delimiter_start: TokenPosition,
    ) -> Self {
        parser.recover_at_matching_delimiter_or_sync(delimiter_start);
        self.covered_span.end = std::cmp::max(
            self.covered_span.end,
            parser.last_consumed_position_or_start(),
        );
        self
    }

    fn unwrap_or_fallback(self, parser: &mut Parser<'src, 'arena>) {
        parser.report_error(self);
    }

    fn report_error(self, parser: &mut Parser<'src, 'arena>) -> bool {
        parser.report_error(self);
        true
    }

    fn ok_or_report(self, parser: &mut Parser<'src, 'arena>) -> Option<()> {
        parser.report_error(self);
        None
    }
}

impl<'src, 'arena> RecoveryFallback<'src, 'arena> for i128 {
    fn fallback_value(_: &Parser<'src, 'arena>, _: &ParseError) -> Self {
        Self::default()
    }
}

impl<'src, 'arena> RecoveryFallback<'src, 'arena> for f64 {
    fn fallback_value(_: &Parser<'src, 'arena>, _: &ParseError) -> Self {
        Self::default()
    }
}

impl<'src, 'arena> RecoveryFallback<'src, 'arena> for crate::ast::IdentifierToken {
    fn fallback_value(_: &Parser<'src, 'arena>, error: &ParseError) -> Self {
        Self(error.covered_span.start)
    }
}

impl<'src, 'arena> RecoveryFallback<'src, 'arena>
    for crate::ast::CallableDefinitionRef<'src, 'arena>
{
    fn fallback_value(parser: &Parser<'src, 'arena>, err: &ParseError) -> Self {
        // default return type: Named("") at error span
        let ret_id = crate::ast::IdentifierToken(err.covered_span.start);
        let return_type = crate::arena::ArenaNode::new_in(
            crate::ast::TypeSpecifier::Named(QualifiedIdentifier::from_ident(parser.arena, ret_id)),
            err.covered_span,
            parser.arena,
        );

        let def = crate::ast::CallableDefinition {
            name: crate::ast::CallableName::Identifier(IdentifierToken(err.covered_span.start)),
            kind: CallableKind::Function,
            return_type_specifier: Some(return_type),
            modifiers: parser.arena.vec(),
            parameters: parser.arena.vec(),
            body: None,
        };
        crate::arena::ArenaNode::new_in(def, err.covered_span, parser.arena)
    }
}

impl<'src, 'arena> RecoveryFallback<'src, 'arena> for crate::ast::StructDefRef<'src, 'arena> {
    fn fallback_value(parser: &Parser<'src, 'arena>, err: &ParseError) -> Self {
        let def = crate::ast::StructDefinition {
            name: None,
            base_type_name: None,
            modifiers: parser.arena.vec(),
            fields: parser.arena.vec(),
        };
        crate::arena::ArenaNode::new_in(def, err.covered_span, parser.arena)
    }
}

impl<'src, 'arena> RecoveryFallback<'src, 'arena> for crate::ast::ClassVarDeclRef<'src, 'arena> {
    fn fallback_value(parser: &Parser<'src, 'arena>, err: &ParseError) -> Self {
        let dummy_ident = crate::ast::IdentifierToken(err.covered_span.start);
        let type_spec = crate::arena::ArenaNode::new_in(
            crate::ast::TypeSpecifier::Named(QualifiedIdentifier::from_ident(
                parser.arena,
                dummy_ident,
            )),
            err.covered_span,
            parser.arena,
        );
        let def = crate::ast::ClassVarDecl {
            paren_specs: None,
            modifiers: parser.arena.vec(),
            type_spec,
            declarators: parser.arena.vec(),
            span: err.covered_span,
        };
        crate::arena::ArenaNode::new_in(def, err.covered_span, parser.arena)
    }
}

impl<'src, 'arena> RecoveryFallback<'src, 'arena>
    for crate::ast::ReplicationBlockRef<'src, 'arena>
{
    fn fallback_value(parser: &Parser<'src, 'arena>, err: &ParseError) -> Self {
        let def = crate::ast::ReplicationBlock {
            rules: parser.arena.vec(),
            span: err.covered_span,
        };
        crate::arena::ArenaNode::new_in(def, err.covered_span, parser.arena)
    }
}

impl<'src, 'arena> RecoveryFallback<'src, 'arena> for crate::ast::StateDeclRef<'src, 'arena> {
    fn fallback_value(parser: &Parser<'src, 'arena>, err: &ParseError) -> Self {
        let def = crate::ast::StateDecl {
            name: crate::ast::IdentifierToken(err.covered_span.start),
            parent: None,
            modifiers: parser.arena.vec(),
            ignores: None,
            body: parser.arena.vec(),
            span: err.covered_span,
        };
        crate::arena::ArenaNode::new_in(def, err.covered_span, parser.arena)
    }
}

impl<'src, 'arena> RecoveryFallback<'src, 'arena> for TokenPosition {
    fn fallback_value(_: &Parser<'src, 'arena>, error: &ParseError) -> Self {
        error.covered_span.end
    }
}

impl<'src, 'arena> RecoveryFallback<'src, 'arena> for (Token, TokenPosition) {
    fn fallback_value(_: &Parser<'src, 'arena>, error: &ParseError) -> Self {
        (Token::Error, error.covered_span.end)
    }
}

impl<'src, 'arena> RecoveryFallback<'src, 'arena> for crate::ast::ExpressionRef<'src, 'arena> {
    fn fallback_value(parser: &Parser<'src, 'arena>, error: &ParseError) -> Self {
        crate::arena::ArenaNode::new_in(
            crate::ast::Expression::Error,
            error.covered_span,
            parser.arena,
        )
    }
}

impl<'src, 'arena> RecoveryFallback<'src, 'arena> for crate::ast::StatementRef<'src, 'arena> {
    fn fallback_value(parser: &Parser<'src, 'arena>, error: &ParseError) -> Self {
        crate::arena::ArenaNode::new_in(
            crate::ast::Statement::Error,
            error.covered_span,
            parser.arena,
        )
    }
}

impl<'src, 'arena, T> RecoveryFallback<'src, 'arena> for Option<T> {
    fn fallback_value(_: &Parser<'src, 'arena>, _: &ParseError) -> Self {
        None
    }
}

impl<'src, 'arena> RecoveryFallback<'src, 'arena> for crate::ast::ClassConstDeclRef<'src, 'arena> {
    fn fallback_value(parser: &Parser<'src, 'arena>, err: &ParseError) -> Self {
        let name = crate::ast::IdentifierToken(err.covered_span.start);
        let value = crate::ast::DeclarationLiteralRef {
            literal: crate::ast::DeclarationLiteral::None,
            position: err.covered_span.start,
        };
        let def = crate::ast::ClassConstDecl {
            name,
            value,
            span: err.covered_span,
        };
        crate::arena::ArenaNode::new_in(def, err.covered_span, parser.arena)
    }
}

impl<'src, 'arena> RecoveryFallback<'src, 'arena> for crate::ast::TypeSpecifierRef<'src, 'arena> {
    fn fallback_value(parser: &Parser<'src, 'arena>, err: &ParseError) -> Self {
        let dummy = crate::ast::IdentifierToken(err.covered_span.start);
        crate::arena::ArenaNode::new_in(
            crate::ast::TypeSpecifier::Named(QualifiedIdentifier::from_ident(parser.arena, dummy)),
            err.covered_span,
            parser.arena,
        )
    }
}

impl<'src, 'arena> RecoveryFallback<'src, 'arena> for crate::ast::ExecDirectiveRef<'arena> {
    fn fallback_value(parser: &Parser<'src, 'arena>, err: &ParseError) -> Self {
        let def = crate::ast::ExecDirective {
            text: parser.arena.string(""),
            span: err.covered_span,
        };
        crate::arena::ArenaNode::new_in(def, err.covered_span, parser.arena)
    }
}

impl ParseError {
    pub fn fallback<'src, 'arena, T>(self, parser: &mut Parser<'src, 'arena>) -> T
    where
        T: RecoveryFallback<'src, 'arena>,
    {
        parser.report_error_with_fallback(self)
    }

    pub fn report<'src, 'arena>(self, parser: &mut Parser<'src, 'arena>) {
        parser.report_error(self);
    }
}
