Skip to content

docs(yq): record that a quoted date is named !!timestamp in an arithmetic error - #6935

Merged
newhoggy merged 1 commit into
mainfrom
issue-6904-yq-arith-ledger-residue
Nov 11, 2026
Merged

newhoggy merged 1 commit into
mainfrom
issue-6904-yq-arith-ledger-residue

Conversation

@newhoggy

Copy link
Copy Markdown
Contributor

Summary

Docs only. #6931 fixed #6904; the limitations ledger there says every yq arithmetic error now follows yq's tag wording and that the key in parentheses is the one difference. One more difference exists and is not recorded: a quoted string that reads as a date is named !!timestamp, where yq says !!str, because OwnedValue has no timestamp kind and the tag is decided from the text (#5930 already does this for *, / and %).

Probed on main @ c791066 against yq v4.53.3:

filter yq succinctly yq
.i - "2001-01-01T00:00:00Z" !!int cannot be added to !!str !!int cannot be added to !!timestamp
.l - "2001-01-01T00:00:00Z" !!str () cannot be subtracted from !!seq !!timestamp () cannot be subtracted from !!seq
.m + "2001-01-01T00:00:00Z" !!str () cannot be added to a !!map (m) !!timestamp () cannot be added to a !!map ()
.i / "2001-01-01T00:00:00Z" !!int cannot be divided by !!str !!int cannot be divided by !!timestamp

Found by /code-review of my own duplicate PR #6929, closed in favour of #6931.

[no changelog]

Test plan

  • each row above run against yq v4.53.3 and a release build of main
  • python3 scripts/doc_lines.py check

…etic error (#6904)

yq names a quoted date !!str in the error of + - / % over kinds that do not combine,
and succinctly yq names it !!timestamp, because OwnedValue has no timestamp kind and the
tag is decided from the text. The ledger said so for * only.
@github-actions

Copy link
Copy Markdown

Coverage

Total: 92.97% ⚪ 0 pp vs main

Comparing c791066..6dbd951 (merge-base → PR head)

No per-file coverage changes vs main.

🔇 0 ignored region(s), 682 tolerated region(s)

ignore removes the lines from both reports; tolerate keeps them in the reported percentage but scores them against the baseline, so a cross-run flip cannot move a delta. Regions are read from each revision's own source.

File Kind Lines Rev Reason
src/bin/succinctly/jq_path_mode.rs tolerate 81 both unreachable: the same filter compiled above
src/bin/succinctly/jq_path_mode.rs tolerate 89-92 both defensive: reached only if the interpreter panics, which no test provokes
src/bin/succinctly/jq_runner.rs tolerate 1930 both unreachable: widening the shadow-candidate set never rejects a program the first parse accepted, for the identical reason run_jq's own analogous fallback (#2395) is tolerated -- see that line's own comment (#2950)
src/bin/succinctly/jq_runner.rs tolerate 2319 both unreachable while the walk and the loader agree on what fails: both resolve with resolve_module_in/resolve_data_file_in, parse with parse_program and name a cycle by canonical file, which the fuzz in #3573 held over thousands of programs; kept so drift prints the loader's failure and not 0 compile errors
src/bin/succinctly/jq_runner.rs tolerate 3046-3058 both unreachable: try_parse_meta_op only fires under ParserMode::Yq (src/jq/parser.rs), and rewrite_namespaced_calls is only reached via ModuleProcessor::process_program, which jq_runner's own jq-mode run is the sole caller of -- so a MetaAssign node can never reach this function (#798)
src/bin/succinctly/jq_runner.rs tolerate 3662 both unreachable by construction: every error this wrapper receives today is a decode or nesting-depth failure the evaluator raised itself, never error(v); kept so a future one is rendered rather than dropped (#2999)
src/bin/succinctly/jq_runner.rs tolerate 3663 both see the arm above (#2999)
src/bin/succinctly/jq_runner.rs tolerate 4092 both unreachable in practice today: def is only None when occurrences.module_def() is None, which (given origin is Some) would require an open run whose innermost frame has no def set at a check point that isn't itself inside a module-level def body -- but every module run is a strict chain of def nodes (the loader's own defs, each wrapping its dependency stubs INSIDE its own body via wrap_defs/dep_stubs_for) terminated by the run's own end marker, so a diagnosable call/var/break site is always reached either inside a module-level def's body (module_def Some) or outside every run (origin None) -- and even when def is Some, self.defs is always ModuleSource::read's own re-parse of the exact same file run_id_for interned this origin's id from, so collect_def_sites always finds the matching (name, arity, ordinal) span. Kept as a defensive fallback rather than a panic/unwrap in case that invariant is ever violated (#3085)
src/bin/succinctly/jq_runner.rs tolerate 4341-4344 both unreachable in a single-process run by construction: run_id_for (the sole source of an origin id) always inserts a run_origins entry for the id it hands back -- from a real load's canonical path, or its own literal-path fallback on a resolve failure -- and a def body only ever gets stamped with an origin after its module loaded successfully, so at always names a file that existed and was readable moments earlier. Reaching this arm needs that same file to vanish (or become unreadable) in the narrow window between that load and this re-read, entirely outside this process's control (#2964)
src/bin/succinctly/jq_runner.rs tolerate 4769 both unreachable: widening the shadow-candidate set never rejects a program the first parse accepted -- a newly covered name only wraps an already-successful dedicated parse, and a failing one would have propagated its error in the first parse too, so the retry budget is charged at the identical sites in both (#2395)
src/bin/succinctly/jq_runner.rs tolerate 5339-5348 both defensive: the splitter stops only at a fault jq's parser also meets, so slurp_fault names one; kept so a disagreement between them prints the old line-0 message rather than none (#5555)
src/bin/succinctly/jq_runner.rs tolerate 5850-5858 both defensive: the splitter stops only at a fault jq's parser also meets, so jq_stream_fault names one; kept so a disagreement between them prints the old line placement rather than none (#5555)
src/bin/succinctly/jq_runner.rs tolerate 5950-5954 both defensive: the only malformed document get_inputs returns here is a slurped stream, which carries the read that met its fault unless slurp_fault_at finds none (unreachable, see there); kept as the line-0 placement every other malformed document gets (#5555)
src/bin/succinctly/jq_runner.rs tolerate 6976 both untestable: stdin failing part-way
src/bin/succinctly/jq_runner.rs tolerate 8564 both unreachable: the prefix parse stopped at value parsed, which the splitter either delimits or stopped at, so a read is found; kept so a disagreement falls back to line 0 (#5555)
src/bin/succinctly/jq_runner.rs tolerate 10711 both untestable: stdin failing part-way
src/bin/succinctly/jq_runner.rs tolerate 12225-12232 both unreachable: the splitter is the more lenient of the two and the prefix parse materializes from its spans, so it cannot find fewer values -- the same internal-error guard as the non-resuming branch below (#1064), kept per segment (#4311)
src/bin/succinctly/jq_runner.rs tolerate 12329-12331 both unreachable: the read starts on a UTF-8 continuation byte, which no JSON token begins with, so the parser always names a fault in it; kept so a read it could not name is still an error and not silently dropped (#5975)
src/bin/succinctly/jq_runner.rs tolerate 12346-12348 both unreachable with the arm above: the parser always names the fault, so the dropped bytes are never counted by hand (#5975)
src/bin/succinctly/jq_runner.rs tolerate 12508-12514 both untestable: a read that fails after the file opened
src/bin/succinctly/jq_runner.rs tolerate 12551-12555 both unreachable: the bytes were validated above
src/bin/succinctly/jq_runner.rs tolerate 12560-12565 both untestable: a read that fails after the file opened
src/bin/succinctly/jq_runner.rs tolerate 12579-12583 both untestable: a read that fails after the file opened
src/bin/succinctly/jq_runner.rs tolerate 12853 both unreachable: called with the source just read at the front
src/bin/succinctly/jq_runner.rs tolerate 12962-12968 both unreachable: the splitter is the more lenient of the two parsers and the prefix parse materializes from its spans, so it cannot find fewer values (the guard read_json_tail keeps per segment, #4311)
src/bin/succinctly/jq_runner.rs tolerate 13053-13060 both unreachable: the splitter is the more lenient of the two parsers and the prefix parse materializes from its spans, so it cannot find fewer values (the guard read_json_tail keeps per segment, #4311)
src/bin/succinctly/jq_runner.rs tolerate 13220 both unreachable: a stitch cuts at a token's first byte, which is ASCII
src/bin/succinctly/jq_runner.rs tolerate 13877 both unreachable: the one source pushed above
src/bin/succinctly/jq_runner.rs tolerate 13921 both defensive: an RS inside a value makes the walk warn (a truncated record), which returns above; kept so a walk that stopped warning cannot leave an RS in a value the lazy route then reads
src/bin/succinctly/jq_runner.rs tolerate 14003 both unreachable: a file holds a document, so there is a last one
src/bin/succinctly/jq_runner.rs tolerate 16282 both unreachable: no M2-streamable program halts, so stats.halt is None here, as in the uncolored arm below (#5415)
src/bin/succinctly/jq_runner.rs tolerate 16283 both unreachable: see the line above (#5415)
src/bin/succinctly/jq_runner.rs tolerate 16284 both unreachable: see the line above (#5415)
src/bin/succinctly/jq_runner.rs tolerate 21263 both unreachable: this test builds only ResolveError::Call values (#3313)
src/bin/succinctly/jq_runner.rs tolerate 25819 both unreachable in a passing suite by design -- the fixed b\
src/bin/succinctly/main.rs tolerate 1519 both unreachable given clap 4.6's own unknown_argument() error constructor: every ErrorKind::UnknownArgument it builds sets ContextKind::InvalidArg to ContextValue::String(arg) in the same call, so this arm only guards a future clap release changing that invariant
src/bin/succinctly/main.rs tolerate 1688 both llvm-cov line-attribution artifact, not unreachable: test_negative_filter_accepted_via_sjq_multicall_alias_3389's '-x' row demonstrably reaches the e.exit() two lines below (exit code 2, confirmed by that test passing), which this if-let's own closing brace sits directly above -- the brace itself is never credited a hit, the same class of artifact eval.rs's own tolerate list documents for other closing braces (#3389)
src/bin/succinctly/main.rs tolerate 1944 both llvm-cov line-attribution artifact, not unreachable: test_negative_filter_boundary_characters_3389's '-x'/'-n1'/'--bogus' rows demonstrably reach the e.exit() two lines below (exit code 2, confirmed by that test passing), which this if-let's own closing brace sits directly above -- the brace itself is never credited a hit, the same class of artifact eval.rs's own tolerate list documents for other closing braces (#3389)
src/bin/succinctly/main.rs tolerate 1968 both the CLI suites drive yq through the syq multi-call arm above; this arm is the same call reached only when spelled succinctly yq (#2999)
src/bin/succinctly/output.rs tolerate 1384 both unreachable: a writer emits a placeholder whole, so the text never ends inside one (#5497)

642 more not shown: the full report is in the coverage-comment-x86_64 artifact.

Touched files absent from every coverage report (1)
  • docs/compliance/yq/limitations.md

These files may contain no executable code or may have been omitted from the coverage run.

Patch coverage

No new executable lines added by this diff.

📦 Full per-file coverage summary · run summary

@github-actions

Copy link
Copy Markdown

Coverage

Total: 93.02% ⚪ 0 pp vs main

Comparing c791066..6dbd951 (merge-base → PR head)

No per-file coverage changes vs main.

🔇 0 ignored region(s), 681 tolerated region(s)

ignore removes the lines from both reports; tolerate keeps them in the reported percentage but scores them against the baseline, so a cross-run flip cannot move a delta. Regions are read from each revision's own source.

File Kind Lines Rev Reason
src/bin/succinctly/jq_path_mode.rs tolerate 81 both unreachable: the same filter compiled above
src/bin/succinctly/jq_path_mode.rs tolerate 89-92 both defensive: reached only if the interpreter panics, which no test provokes
src/bin/succinctly/jq_runner.rs tolerate 1930 both unreachable: widening the shadow-candidate set never rejects a program the first parse accepted, for the identical reason run_jq's own analogous fallback (#2395) is tolerated -- see that line's own comment (#2950)
src/bin/succinctly/jq_runner.rs tolerate 2319 both unreachable while the walk and the loader agree on what fails: both resolve with resolve_module_in/resolve_data_file_in, parse with parse_program and name a cycle by canonical file, which the fuzz in #3573 held over thousands of programs; kept so drift prints the loader's failure and not 0 compile errors
src/bin/succinctly/jq_runner.rs tolerate 3046-3058 both unreachable: try_parse_meta_op only fires under ParserMode::Yq (src/jq/parser.rs), and rewrite_namespaced_calls is only reached via ModuleProcessor::process_program, which jq_runner's own jq-mode run is the sole caller of -- so a MetaAssign node can never reach this function (#798)
src/bin/succinctly/jq_runner.rs tolerate 3662 both unreachable by construction: every error this wrapper receives today is a decode or nesting-depth failure the evaluator raised itself, never error(v); kept so a future one is rendered rather than dropped (#2999)
src/bin/succinctly/jq_runner.rs tolerate 3663 both see the arm above (#2999)
src/bin/succinctly/jq_runner.rs tolerate 4092 both unreachable in practice today: def is only None when occurrences.module_def() is None, which (given origin is Some) would require an open run whose innermost frame has no def set at a check point that isn't itself inside a module-level def body -- but every module run is a strict chain of def nodes (the loader's own defs, each wrapping its dependency stubs INSIDE its own body via wrap_defs/dep_stubs_for) terminated by the run's own end marker, so a diagnosable call/var/break site is always reached either inside a module-level def's body (module_def Some) or outside every run (origin None) -- and even when def is Some, self.defs is always ModuleSource::read's own re-parse of the exact same file run_id_for interned this origin's id from, so collect_def_sites always finds the matching (name, arity, ordinal) span. Kept as a defensive fallback rather than a panic/unwrap in case that invariant is ever violated (#3085)
src/bin/succinctly/jq_runner.rs tolerate 4341-4344 both unreachable in a single-process run by construction: run_id_for (the sole source of an origin id) always inserts a run_origins entry for the id it hands back -- from a real load's canonical path, or its own literal-path fallback on a resolve failure -- and a def body only ever gets stamped with an origin after its module loaded successfully, so at always names a file that existed and was readable moments earlier. Reaching this arm needs that same file to vanish (or become unreadable) in the narrow window between that load and this re-read, entirely outside this process's control (#2964)
src/bin/succinctly/jq_runner.rs tolerate 4769 both unreachable: widening the shadow-candidate set never rejects a program the first parse accepted -- a newly covered name only wraps an already-successful dedicated parse, and a failing one would have propagated its error in the first parse too, so the retry budget is charged at the identical sites in both (#2395)
src/bin/succinctly/jq_runner.rs tolerate 5339-5348 both defensive: the splitter stops only at a fault jq's parser also meets, so slurp_fault names one; kept so a disagreement between them prints the old line-0 message rather than none (#5555)
src/bin/succinctly/jq_runner.rs tolerate 5850-5858 both defensive: the splitter stops only at a fault jq's parser also meets, so jq_stream_fault names one; kept so a disagreement between them prints the old line placement rather than none (#5555)
src/bin/succinctly/jq_runner.rs tolerate 5950-5954 both defensive: the only malformed document get_inputs returns here is a slurped stream, which carries the read that met its fault unless slurp_fault_at finds none (unreachable, see there); kept as the line-0 placement every other malformed document gets (#5555)
src/bin/succinctly/jq_runner.rs tolerate 6976 both untestable: stdin failing part-way
src/bin/succinctly/jq_runner.rs tolerate 8564 both unreachable: the prefix parse stopped at value parsed, which the splitter either delimits or stopped at, so a read is found; kept so a disagreement falls back to line 0 (#5555)
src/bin/succinctly/jq_runner.rs tolerate 10711 both untestable: stdin failing part-way
src/bin/succinctly/jq_runner.rs tolerate 12225-12232 both unreachable: the splitter is the more lenient of the two and the prefix parse materializes from its spans, so it cannot find fewer values -- the same internal-error guard as the non-resuming branch below (#1064), kept per segment (#4311)
src/bin/succinctly/jq_runner.rs tolerate 12329-12331 both unreachable: the read starts on a UTF-8 continuation byte, which no JSON token begins with, so the parser always names a fault in it; kept so a read it could not name is still an error and not silently dropped (#5975)
src/bin/succinctly/jq_runner.rs tolerate 12346-12348 both unreachable with the arm above: the parser always names the fault, so the dropped bytes are never counted by hand (#5975)
src/bin/succinctly/jq_runner.rs tolerate 12508-12514 both untestable: a read that fails after the file opened
src/bin/succinctly/jq_runner.rs tolerate 12551-12555 both unreachable: the bytes were validated above
src/bin/succinctly/jq_runner.rs tolerate 12560-12565 both untestable: a read that fails after the file opened
src/bin/succinctly/jq_runner.rs tolerate 12579-12583 both untestable: a read that fails after the file opened
src/bin/succinctly/jq_runner.rs tolerate 12853 both unreachable: called with the source just read at the front
src/bin/succinctly/jq_runner.rs tolerate 12962-12968 both unreachable: the splitter is the more lenient of the two parsers and the prefix parse materializes from its spans, so it cannot find fewer values (the guard read_json_tail keeps per segment, #4311)
src/bin/succinctly/jq_runner.rs tolerate 13053-13060 both unreachable: the splitter is the more lenient of the two parsers and the prefix parse materializes from its spans, so it cannot find fewer values (the guard read_json_tail keeps per segment, #4311)
src/bin/succinctly/jq_runner.rs tolerate 13220 both unreachable: a stitch cuts at a token's first byte, which is ASCII
src/bin/succinctly/jq_runner.rs tolerate 13877 both unreachable: the one source pushed above
src/bin/succinctly/jq_runner.rs tolerate 13921 both defensive: an RS inside a value makes the walk warn (a truncated record), which returns above; kept so a walk that stopped warning cannot leave an RS in a value the lazy route then reads
src/bin/succinctly/jq_runner.rs tolerate 14003 both unreachable: a file holds a document, so there is a last one
src/bin/succinctly/jq_runner.rs tolerate 16282 both unreachable: no M2-streamable program halts, so stats.halt is None here, as in the uncolored arm below (#5415)
src/bin/succinctly/jq_runner.rs tolerate 16283 both unreachable: see the line above (#5415)
src/bin/succinctly/jq_runner.rs tolerate 16284 both unreachable: see the line above (#5415)
src/bin/succinctly/jq_runner.rs tolerate 21263 both unreachable: this test builds only ResolveError::Call values (#3313)
src/bin/succinctly/jq_runner.rs tolerate 25819 both unreachable in a passing suite by design -- the fixed b\
src/bin/succinctly/main.rs tolerate 1519 both unreachable given clap 4.6's own unknown_argument() error constructor: every ErrorKind::UnknownArgument it builds sets ContextKind::InvalidArg to ContextValue::String(arg) in the same call, so this arm only guards a future clap release changing that invariant
src/bin/succinctly/main.rs tolerate 1688 both llvm-cov line-attribution artifact, not unreachable: test_negative_filter_accepted_via_sjq_multicall_alias_3389's '-x' row demonstrably reaches the e.exit() two lines below (exit code 2, confirmed by that test passing), which this if-let's own closing brace sits directly above -- the brace itself is never credited a hit, the same class of artifact eval.rs's own tolerate list documents for other closing braces (#3389)
src/bin/succinctly/main.rs tolerate 1944 both llvm-cov line-attribution artifact, not unreachable: test_negative_filter_boundary_characters_3389's '-x'/'-n1'/'--bogus' rows demonstrably reach the e.exit() two lines below (exit code 2, confirmed by that test passing), which this if-let's own closing brace sits directly above -- the brace itself is never credited a hit, the same class of artifact eval.rs's own tolerate list documents for other closing braces (#3389)
src/bin/succinctly/main.rs tolerate 1968 both the CLI suites drive yq through the syq multi-call arm above; this arm is the same call reached only when spelled succinctly yq (#2999)
src/bin/succinctly/output.rs tolerate 1384 both unreachable: a writer emits a placeholder whole, so the text never ends inside one (#5497)

641 more not shown: the full report is in the coverage-comment-ARM64 artifact.

Touched files absent from every coverage report (1)
  • docs/compliance/yq/limitations.md

These files may contain no executable code or may have been omitted from the coverage run.

Patch coverage

No new executable lines added by this diff.

📦 Full per-file coverage summary · run summary

@newhoggy
newhoggy added this pull request to the merge queue Nov 11, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Nov 11, 2026
@newhoggy
newhoggy added this pull request to the merge queue Nov 11, 2026
@newhoggy

Copy link
Copy Markdown
Contributor Author

Dequeued once by cancelling merge-group run 42973657522: its Test (ARM64) (simd-sve2-dispatch) job sat in Run for 45+ min (the same job takes ~5 min on every other run) and held up the three PRs behind it. Docs-only change, nothing re-run on a changed tree; re-enqueued unchanged.

Merged via the queue into main with commit 13def7c Nov 11, 2026
52 checks passed
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.

yq: arithmetic over two kinds that do not combine raises jq's wording where yq names the tags (!!float cannot be added to !!null) (#6887 follow-up)

1 participant