Detect locale code that leaves a string literal open - #328
Merged
Merged
Conversation
A translation pass can drop a \n escape, turning printf("%f\n", x) into a real
newline inside the literal, which no longer compiles. Nothing caught this: the
Dart validator never executes type-1 code, check_outputs.py skips type 1, and
check_locales.py blanks comment text rather than quotes.
strip_comments already lexes each language properly - triple-quoted strings,
// inside a string, quotes in char literals and comments, backslash escapes -
so the checker borrows it rather than matching quotes itself. Two new optional
out-params collect what that scan already sees: every line leaving a literal
open, and the offset of every /* that never closes.
That second one matters because an unclosed /* is deliberately not comment
state - the opener is emitted as text and scanning continues - so an apostrophe
in the prose behind it would read as a string opener. en/c/comments/5.md asks
the learner to close such a /*, and it/c/comments/5.md writes "il doppio
dell'input" behind it. Findings from that offset on are dropped; taking the
offset from the lexer keeps a /* inside a string or behind a // from counting.
Seeds holding a [/] placeholder are skipped, since a placeholder can stand for
a quote and balance is undecidable; the --solutions-- copy is checked instead.
Findings are addressed by (fence, line) so the en baseline excuses only the
line en itself trips on, not the whole fence around it.
The 12 files this found were fixed in #312; it now reports zero.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A translation pass can drop a
\nescape, turningprintf("%f\n", x)into a real newline inside the literal, which no longer compiles. Nothing caught this today: the Dart validator never executes type-1 code,check_outputs.pyskips type 1, andcheck_locales.pyblanks comment text rather than quotes.The 12 files this found were already fixed in #312, so it reports zero now — this lands the detector that found them.
How it works
strip_commentsincheck_locales.pyalready lexes each language properly (triple-quoted strings,//inside a string, quotes in char literals and comments, backslash escapes), so the checker borrows it rather than matching quotes itself. Two new optional out-params collect what that scan already sees: every line leaving a literal open, and the offset of every/*that never closes.That second one matters because an unclosed
/*is deliberately not comment state — the opener is emitted as text and scanning continues — so an apostrophe in the prose behind it would read as a string opener.en/c/comments/5.mdasks the learner to close such a/*, andit/c/comments/5.mdwrites "il doppio dell'input" behind it. Findings from that offset on are dropped; taking the offset from the lexer keeps a/*inside a string or behind a//from counting.Seeds holding a
[/]placeholder are skipped, since a placeholder can stand for a quote and balance is undecidable; the--solutions--copy is checked instead. Findings are addressed by(fence, line)so the en baseline excuses only the line en itself trips on, not the whole fence around it.Verification
python3 scripts/test_check_locales.py→okpython3 scripts/check_string_literals.py→0 unterminated string literal(s), exit 0Local-only tool, consistent with the other
scripts/check_*.py; not wired into CI.Reviewed independently (gpt-5.6-sol); both findings it raised — fence-wide suppression hiding locale slips, and non-lexical
/*counting truncating valid code — are fixed here with regression tests.