Skip to content

jq: consider a clippy lint against unguarded Vec::with_capacity(a * b) — fourth recurrence of the #1612/#1634 bug class #1670

Description

@newhoggy

Severity: Low (process/tooling improvement, not a live bug)

Summary

Found during #1666's review (the #1634 fix). This is now the fourth independently-discovered instance of the same bug shape — an infallible Vec/String allocation sized by a product (or otherwise generator-controlled magnitude) of independently-controlled lengths, handed straight to the allocator instead of a try_reserve-style fallible guard:

Each occurrence was found independently, by a human or a multi-agent review pass noticing the pattern by eye — nothing in CI or clippy flags a raw Vec::with_capacity/String::with_capacity (or an implicit TrustedLen collect) whose argument is a multiplication of two or more independent lengths, or a scalar read from generator/document-controlled input. #1666 itself demonstrates the detection gap concretely: its first commit patched only eval.rs's copies of eval_index_expr/eval_slice_expr, and needed a second commit, prompted by a second multi-agent review pass, to catch that eval_generic.rs independently reimplements both functions (the actual CLI dispatch path) with the identical unguarded pattern.

Suggested fix direction (needs its own scoping pass)

A clippy::disallowed_methods (or similar) lint entry scoped to src/jq/ banning a bare Vec::with_capacity/String::with_capacity call would turn this into a compile-time gate instead of a review-time hope. This needs its own scoping work:

  • Enumerate the legitimate single-length (or length-times-constant) call sites that would need a per-call-site #[allow(...)] or a rewrite to use try_reserve_product/an equivalent single-factor helper (json/standard.rs, output.rs, yq_runner.rs, etc. all have benign with_capacity(x.len()) calls today).
  • Decide whether the lint should be src/jq/-wide or narrower (e.g. only eval.rs/eval_generic.rs, where the generator/cross-product pattern actually recurs).
  • Consider whether try_reserve_product's hardcoded "computed-index expansion" error wording (src/jq/eval.rs) should be parameterized (a &str/enum label) before other call sites (e.g. combinations(n) per jq: combinations(n)/cartesian_product have the same unguarded allocation-product crash as #1612/#1634 #1669) start reusing it, so the error text doesn't mislead for a differently-shaped caller.

A separate, larger structural question raised in the same review pass but explicitly out of scope for a lint-only fix: eval.rs and eval_generic.rs maintain independent, parallel reimplementations of eval_index_expr/eval_slice_expr (and likely other evaluator logic) with no mechanism keeping them in sync — this is the root cause #1666 needed a second commit to fully close. Whether to consolidate these two evaluators (or add some other cross-checking mechanism) is a much bigger question than this issue's own scope; noted here for visibility, not as this issue's own acceptance criterion.

Related

#1017, #1612, #1634 (closed by #1666), #1669 — the four known instances of this bug shape.

Metadata

Metadata

Assignees

No one assigned

    Labels

    SonnetSuitable for a Sonnet-class model to implement

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions