Skip to content

Commit 30c0bfc

Browse files
Report invalid \uXXXX escape position at the u character
CPython's json decoder reports the position of the `u` specifier when a \uXXXX escape fails to parse, but RustPython was reporting the preceding `\`. For surrogate-pair cases (\uXXXX\uYYYY) the second call was passing char_offset + next_char_i + 1, which lands on the first hex digit of the first escape -- unrelated to the actual failure site. Pass next_char_i (position of the primary `u`) to the primary decode_unicode call, and capture the second `u`'s char index from the next_tuple peek to pass to the surrogate-pair decode_unicode call. Verified: 13 targeted probes across invalid-hex, short, and pair cases now all match CPython positions. test.test_json 214 tests pass with no regressions.
1 parent 2e5c2be commit 30c0bfc

1 file changed

Lines changed: 7 additions & 4 deletions

File tree

crates/stdlib/src/json/machinery.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -231,17 +231,20 @@ pub fn scanstring<'a>(
231231
'r' => "\r",
232232
't' => "\t",
233233
'u' => {
234-
let mut uni = decode_unicode(&mut chars, char_offset + char_i)?;
234+
// Error position for an invalid \uXXXX escape points at the
235+
// `u`, not the `\` -- matches CPython's json.decoder.
236+
let mut uni = decode_unicode(&mut chars, char_offset + next_char_i)?;
235237
chunk_start = byte_i + 6;
236238
if let Some(lead) = uni.to_lead_surrogate() {
237239
// uni is a surrogate -- try to find its pair
238240
let mut chars2 = chars.clone();
239-
if let Some(((_, (byte_pos2, _)), (_, _))) = chars2
241+
if let Some(((_, (byte_pos2, _)), (u2_char_i, (_, _)))) = chars2
240242
.next_tuple()
241243
.filter(|((_, (_, c1)), (_, (_, c2)))| *c1 == '\\' && *c2 == 'u')
242244
{
243-
let uni2 =
244-
decode_unicode(&mut chars2, char_offset + next_char_i + 1)?;
245+
// u2_char_i is the `u` of the second \uXXXX; same
246+
// position convention as the primary call above.
247+
let uni2 = decode_unicode(&mut chars2, char_offset + u2_char_i)?;
245248
if let Some(trail) = uni2.to_trail_surrogate() {
246249
// ok, we found what we were looking for -- \uXXXX\uXXXX, both surrogates
247250
uni = lead.merge(trail).into();

0 commit comments

Comments
 (0)