Repository: typst/typst
File: crates/typst-syntax/src/parser.rs (Rust)

Code before:
    }

    /// Check if the maximum depth has been exceeded. If so, generate an error
    /// and try to make a best effort recovery.
    /// Otherwise increase the depth and return a handle that automatically
    /// decreases it again when dropped.
    fn increase_depth(&mut self) -> Option<impl DerefMut<Target = Self>> {
        if self.depth < MAX_DEPTH {
            self.depth += 1;
            return Some(defer(self, |p| p.depth -= 1));
        }

        self.depth_check_error(None);

        None
    }

    /// Generate an error for an exceeded maximum depth check.
    ///
    /// This function has to guarantee some sort of forward progress, otherwise
    /// the parser might loop indefinitely. One token is eaten in all cases, if
    /// that token is an opening delimiter, try to balance the opening and
    /// closing grouping delimiters before continuing.
    fn depth_check_error(&mut self, stop_set: Option<SyntaxSet>) {
        let m = self.marker();

        let mut balance: usize = 0;
        let mut eat_balanced = |p: &mut Parser| -> bool {
            if p.at_set(syntax_set!(LeftBracket, LeftBrace, LeftParen)) {
                balance = balance.saturating_add(1);
            } else if p.at_set(syntax_set!(RightBracket, RightBrace, RightParen)) {
                balance = balance.saturating_sub(1);
            }
            p.eat();
            balance == 0
        };
        self.with_nl_mode(AtNewline::Continue, |p| {
            while {
                let balanced = eat_balanced(p);
                let at_stop = stop_set.is_none_or(|s| p.at_set(s));
                !(balanced && at_stop || p.end())
            } {}
        });

        self.wrap(m, SyntaxKind::Text);
        let node = self.nodes.last_mut().unwrap();
        node.convert_to_error("maximum parsing depth exceeded");
    }
}


Review comment (suggestion):
```suggestion
    fn increase_depth(&mut self) -> Option<impl DerefMut<Target = Self>> {
        if self.depth < MAX_DEPTH {
            self.depth += 1;
            Some(defer(self, |p| p.depth -= 1))
        } else {
            self.depth_check_error(None);
            None
        }
    }
```

Code after:
    /// This function isn't strictly necessary, but it is an optimization to
    /// generate one combined error instead of an error message for every
    /// balanced set of tokens. In the pathological case an error would be
    /// generated by [`Self::increase_depth`] for every single token until
    /// the `stop_set` of the parent function is reached.
    fn check_depth_until(&mut self, stop_set: SyntaxSet) -> Option<&mut Self> {
        if self.depth < MAX_DEPTH {
            Some(self)
        } else {
            self.depth_check_error(Some(stop_set));
            None
        }
    }

    /// Check if the maximum depth has been exceeded. If so, generate an error
    /// and try to make a best effort recovery. Otherwise increase the depth and
    /// return a handle that automatically decreases it again when dropped.
    fn increase_depth(&mut self) -> Option<impl DerefMut<Target = Self>> {
        if self.depth < MAX_DEPTH {
            self.depth += 1;
            Some(defer(self, |p| p.depth -= 1))
        } else {
            self.depth_check_error(None);
            None
        }
    }

    /// Generate an error for an exceeded maximum depth check.
    fn depth_check_error(&mut self, stop_set: Option<SyntaxSet>) {
        let m = self.marker();

        let mut balance: usize = 0;
        // This function has to guarantee some sort of forward progress,
        // otherwise the parser might loop indefinitely. One token is eaten in
        // all cases, if that token is an opening delimiter, try to balance the
        // opening and closing grouping delimiters before continuing.
        self.with_nl_mode(AtNewline::Continue, |p| {
            loop {
                if p.at_set(syntax_set!(LeftBracket, LeftBrace, LeftParen)) {
                    balance = balance.saturating_add(1);
                } else if p.at_set(syntax_set!(RightBracket, RightBrace, RightParen)) {
                    balance = balance.saturating_sub(1);
                }
                p.eat();

                let at_stop = stop_set.is_none_or(|s| p.at_set(s));
                if (balance == 0 && at_stop) || p.end() {
                    break;
                }
            }
        });