Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions crates/stdlib/src/json/machinery.rs
Original file line number Diff line number Diff line change
Expand Up @@ -231,17 +231,20 @@ pub fn scanstring<'a>(
'r' => "\r",
't' => "\t",
'u' => {
let mut uni = decode_unicode(&mut chars, char_offset + char_i)?;
// Error position for an invalid \uXXXX escape points at the
// `u`, not the `\` -- matches CPython's json.decoder.
let mut uni = decode_unicode(&mut chars, char_offset + next_char_i)?;
chunk_start = byte_i + 6;
if let Some(lead) = uni.to_lead_surrogate() {
// uni is a surrogate -- try to find its pair
let mut chars2 = chars.clone();
if let Some(((_, (byte_pos2, _)), (_, _))) = chars2
if let Some(((_, (byte_pos2, _)), (u2_char_i, (_, _)))) = chars2
.next_tuple()
.filter(|((_, (_, c1)), (_, (_, c2)))| *c1 == '\\' && *c2 == 'u')
{
let uni2 =
decode_unicode(&mut chars2, char_offset + next_char_i + 1)?;
// u2_char_i is the `u` of the second \uXXXX; same
// position convention as the primary call above.
let uni2 = decode_unicode(&mut chars2, char_offset + u2_char_i)?;
if let Some(trail) = uni2.to_trail_surrogate() {
// ok, we found what we were looking for -- \uXXXX\uXXXX, both surrogates
uni = lead.merge(trail).into();
Expand Down
16 changes: 16 additions & 0 deletions extra_tests/snippets/stdlib_json.py
Original file line number Diff line number Diff line change
Expand Up @@ -239,3 +239,19 @@ class Dict(dict):
RecursionError,
lambda: json.loads(('[{"x":' * _deep) + "1" + ("}]" * _deep)),
)


# Invalid \uXXXX escape: error position points at the 'u', matching CPython.
try:
json.loads('"\\uXYZW"')
except json.JSONDecodeError as e:
assert e.pos == 2, f"expected pos=2, got {e.pos}"
else:
raise AssertionError("expected JSONDecodeError")

try:
json.loads('"abc\\uZZZZ"')
except json.JSONDecodeError as e:
assert e.pos == 5, f"expected pos=5, got {e.pos}"
else:
raise AssertionError("expected JSONDecodeError")
Loading