Repository navigation
fix(jq): a destructuring ?// bind states its register per result, and a retry after a failed write resumes from null - #3900
Conversation
CoverageTotal: 94.88% ⚪ 0 pp vs Comparing No per-file coverage changes vs 🔇 0 ignored region(s), 383 tolerated region(s)
Excluded by ignore-filename-regex: 1 file (none of them touched by this diff). Patch coveragePatch: 100% (12/12 new lines covered)
|
CoverageTotal: 94.97% ⚪ 0 pp vs Comparing No per-file coverage changes vs 🔇 0 ignored region(s), 382 tolerated region(s)
Excluded by ignore-filename-regex: 1 file (none of them touched by this diff). Patch coveragePatch: 100% (12/12 new lines covered)
|
|
Sweep result: |
… a retry after a failed write resumes from null (#3859) A by-value true/false/null body after a destructuring ?// bind whose first alternative failed to destructure refused where jq answers a valid path: path(.x | (. as [$q] ?// $z | true)) on {"x":true} is ["x"]. The failed destructure is restored by the fork, so the bare-$var alternative that runs leaves the register at the stage's entry and states Unmoved, but the pipe stage discarded it because cannot_move_register is false for any destructuring pattern (a successful one moves it). stage_states_register_per_result now admits a destructuring AsPattern whose body cannot move the register. A successful destructure navigated and is read as before; only a result that navigated nothing is trusted. That widening exposed a write bug that was already there: after a write raises, jq's reduce state is the null its DUPN hands back, so a ?// retry writes the retried alternative's paths onto null. stream_path_writes kept the earlier paths' document instead, so (.x | .. | (. as [$q] ?// $z | $z)) = 9 on {"x":[null],"k":3} kept k where jq's result is {"x":[9]}. Gated against /usr/bin/jq 1.7.1 and a main build: 49,590 generated rows, 678 improved, 0 regressed, 0 accept-wrong.
…row, stash-meaning comment
36efd58 to
1f2222d
Compare
Summary
true/false/nullbody after a destructuring?//bind whose first alternative fails to destructure now answers jq's valid path:path(.x | (. as [$q] ?// $z | true))on{"x":true}is["x"](was exit 5). The failed destructure is restored by the fork, so the bare-$varalternative that runs leaves the register at the stage's entry; the pipe stage discarded that statement becausecannot_move_registerisfalsefor any destructuring pattern.stage_states_register_per_resultnow admits a destructuringAsPatternwhose body cannot move the register. A successful destructure navigated and is read as before.reducestate is thenullitsDUPNhands back, so a?//retry writes the retried alternative's paths ontonull.stream_path_writeskept the earlier paths' document.(.x | .. | (. as [$q] ?// $z | $z)) = 9on{"x":[null],"k":3}keptkonmain; jq gives{"x":[9]}.any/allanswers, and a compound body such as(.a?, true)), and updateslimitations.mdand the producer-contract note.Fixes #3859
Test plan
cargo build --features clicargo test --features cli,simd,regex,serde(10,146 passed, 0 failed)cargo test --no-default-features --lib,cargo check --no-default-featurescargo clippy --all-targets --all-features -- -D warningscargo clippy --all-targets --features std,simd,serde,cli,regex,bench-runner,large-tests,mmap-tests -- -D warningsRUSTDOCFLAGS="-D warnings" cargo doc --no-deps --features cli/usr/bin/jq1.7.1 vs amainbuild vs this branch), 49,590 generated rows over entries x binds x bodies xpath/del/=/|=: 678 improved, 0 regressed, 0 accept-wrong. The only changed rows that still differ from jq are|=error-to-error message differences with the same exit code (the documented_modifyretry quirk).scripts/jq-bind-origin-fuzz.py --destructure-bind-p 1.0 --fold-p 0.3 -n 6000 --baseline <main>: 5,994 agree, 0 fabricate, 0 mismatch; 6 refuse-only rows all reproduced byte-for-byte by the baselinetest_destructuring_alt_bind_with_a_failed_first_alternative_states_its_register_3859andtest_destructuring_alt_bind_writes_and_a_retry_resumes_from_null_3859(every row captured from jq 1.7.1, stdin and-nroutes); each pinned row that changes was confirmed to differ onmainscripts/jq-path-register-sweep.py, 150k-row seeded sample (result posted as a comment)