Skip to content

Add lexer regression tests to the parser crate - #132

Merged
adampetro merged 1 commit into
mainfrom
ap.lexer-parser-more-tests
Aug 11, 2026
Merged

Add lexer regression tests to the parser crate#132
adampetro merged 1 commit into
mainfrom
ap.lexer-parser-more-tests

Conversation

@adampetro

@adampetro adampetro commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Why

We will upgrade the logos crate soon. The lexer gets untrusted input. A change in logos behavior can cause incorrect tokens, incorrect spans, out-of-bounds data access, or a denial of service. These tests pin the current behavior and make regressions visible.

This change adds tests only. It does not change production code.

What

Unit tests in src/lexer/logos_lexer.rs:

  • Test all punctuators, variable names, and ignored tokens, with exact spans.
  • Test edge cases in strings, block strings, and numbers.
  • Compare the full token stream for a document with all token types and all ignored token types.
  • Test the spans of tokens that come after strings. String parsing moves the outer lexer with remainder() and bump(). These logos APIs are the most likely place for subtle changes between versions.
  • Test the LogosLexer iterator, the token count, and the max tokens limit.

Safety tests in src/lexer/logos_lexer/safety_tests.rs:

  • Lex a corpus of hostile inputs. Assert that the lexer terminates, makes progress, consumes the full input, and returns spans that are in bounds and on char boundaries. Spans with incorrect bounds can cause panics in error formatting, which slices the source by span.
  • Lex each prefix and each suffix of a document. This simulates unexpected end of input in each lexer state.
  • Lex large hostile inputs that must complete in linear-like time.
  • Pin the current stack usage for long numeric tokens and long runs of ignored characters.

Integration tests in tests/lexer_adversarial_test.rs:

  • Parse hostile documents through the public API and convert the errors to GraphQL errors.
  • Show that the max tokens limit bounds the work for large documents.

Known issue found while writing these tests

With logos 0.15, the generated matchers for numeric tokens and for runs of ignored characters use stack space proportional to the run length in unoptimized builds. Optimized builds compile the recursion into loops and are not affected. As a result of this, one numeric token with approximately 5,000 or more digits, or one run of approximately 50,000 or more ignored characters, causes a stack overflow in unoptimized builds. The max tokens limit does not protect against this, because each input is a single token.

Two tests document this issue. They are marked with #[ignore] because a failure aborts the test process. After each logos upgrade, run this command in a debug build:

cargo test -p bluejay-parser -- --ignored

If the tests pass, remove the #[ignore] attributes.

How to verify

cargo test -p bluejay-parser --features format-errors

These tests prepare for an upgrade of the logos crate. The lexer
gets untrusted input. A change in logos behavior can cause incorrect
tokens, incorrect spans, out-of-bounds data access, or a denial of
service. These tests pin the current behavior and make regressions
visible. This change adds tests only. It does not change production
code.

Unit tests in src/lexer/logos_lexer.rs:
- Test all punctuators, variable names, and ignored tokens, with
  exact spans.
- Test edge cases in strings, block strings, and numbers.
- Compare the full token stream for a document with all token types.
- Test the LogosLexer iterator, the token count, and the max tokens
  limit.

Safety tests in src/lexer/logos_lexer/safety_tests.rs:
- Lex a corpus of hostile inputs. Assert that the lexer terminates,
  makes progress, consumes the full input, and returns spans that
  are in bounds and on char boundaries.
- Lex each prefix and each suffix of a document to simulate
  unexpected end of input in each lexer state.
- Lex large hostile inputs that must complete in linear-like time.
- Pin the current stack usage for long numeric tokens and long runs
  of ignored characters.

Integration tests in tests/lexer_adversarial_test.rs:
- Parse hostile documents through the public API and convert the
  errors, which slices the source by span.
- Show that the max tokens limit bounds the work for large
  documents.

Known issue: with logos 0.15, the matchers for numeric tokens and
for runs of ignored characters use stack space proportional to the
run length in unoptimized builds. One numeric token with
approximately 5,000 or more digits causes a stack overflow in
unoptimized builds. Two ignored tests document this issue. Run
`cargo test -p bluejay-parser -- --ignored` in a debug build after
each logos upgrade. If the tests pass, remove the ignore attributes.

Assisted-By: devx/a4890344-dddf-4733-a94e-71230a3953d1
@adampetro
adampetro requested a review from swalkinshaw August 10, 2026 20:09

@swalkinshaw swalkinshaw left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea 🚀

@adampetro
adampetro merged commit b45ba31 into main Aug 11, 2026
5 checks passed
@adampetro
adampetro deleted the ap.lexer-parser-more-tests branch August 11, 2026 14:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants