Skip to content

Escape a lone surrogate in a quoted string literal [patch] - #139

Merged
matt-edmondson merged 1 commit into
mainfrom
claude/issue-130-lone-surrogate-literal
Oct 14, 2026
Merged

matt-edmondson merged 1 commit into
mainfrom
claude/issue-130-lone-surrogate-literal

Conversation

@matt-edmondson

Copy link
Copy Markdown
Contributor

Fixes #130

What changed

TemplateRendering.QuoteStringLiteral now writes any surrogate that is not half of a valid high/low pair as a \uXXXX escape. A valid pair is still written raw, so readable text such as emoji stays as it is. Before this change, an unpaired surrogate went into the generated source unescaped. It has no UTF-8 encoding, so File.WriteAllText threw an EncoderFallbackException, or a replacing encoder silently turned it into U+FFFD and the constant's value changed.

Tests

  • New test: TemplateTests.ParameterEscapesALoneSurrogateInAQuotedDefaultValue.
    • It covers a lone high surrogate, a lone low surrogate, a high surrogate at the end, a low surrogate at the start, a reversed pair (\uDE00\uD83D), and a valid pair.
    • For each case it asserts the exact literal and that the output encodes under a strict UTF-8 encoder.
    • The cases are built in code rather than with DataRow, because attribute arguments are stored as UTF-8 and cannot carry a lone surrogate.
  • I reverted the fix and reran the new test: it fails. With the fix, the full suite passes (218/218, net10.0).

🤖 Generated with Claude Code

https://claude.ai/code/session_014h6btvAuM1Ew7cCFSpuFWc


Generated by Claude Code

QuoteStringLiteral copied an unpaired surrogate into the literal raw.
It has no UTF-8 encoding, so saving the generated file either threw or
silently replaced the character with U+FFFD, changing the constant's
value. A surrogate that is not half of a valid pair now takes a \uXXXX
escape; valid pairs are still written as they are.

Fixes #130

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014h6btvAuM1Ew7cCFSpuFWc
@sonarqubecloud

Copy link
Copy Markdown

@matt-edmondson
matt-edmondson merged commit 3c644b5 into main Oct 14, 2026
14 checks passed
@matt-edmondson
matt-edmondson deleted the claude/issue-130-lone-surrogate-literal branch October 14, 2026 04:15
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.

Quoted DefaultValue with a lone surrogate makes File.WriteAllText throw, or silently becomes U+FFFD

2 participants