fix(parse): enforce double_dash="required" for positional args - #762
fix(parse): enforce double_dash="required" for positional args#762JamBalaya56562 wants to merge 1 commit into
Conversation
|
Warning Review limit reached
Next review available in: 1 minute Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Central YAML (base), Organization UI (inherited) Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (9)
📝 WalkthroughWalkthroughThe parser routes values to arguments that require ChangesRequired double-dash argument support
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant CLI
participant Parser
participant ParseOutput
CLI->>Parser: parse partial command line
Parser->>ParseOutput: publish next_arg and double_dash_seen
ParseOutput-->>CLI: return completion state
CLI->>CLI: offer `--` or complete the positional argument
Possibly related PRs
Suggested reviewers: Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches 💡 1🛠️ Fix failing CI checks 💡
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Greptile SummaryThis PR enforces
Confidence Score: 5/5The PR appears safe to merge. No blocking failure remains within the scope of the available follow-up review threads. Important Files Changed
Reviews (2): Last reviewed commit: "fix(parse): enforce double_dash="require..." | Re-trigger Greptile |
7d82ea0 to
bf9bb63
Compare
|
Why each one is there
The structural bit
Options, either of which I'm happy to push
Option 2 is maybe 30 minutes of work if you'd rather not spend a major on this. Just say which and I'll update the branch. This comment was generated by Claude Code. |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
lib/src/parse.rs (1)
749-757: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueConsider documenting the no-rewind case for an earlier required arg.
The search finds the first unfilled
double_dash="required"argument anywhere inout.cmd.args, but the jump applies only whentarget > next_arg_idx. If such an argument is declared before the current cursor and the cursor already moved past it, the--does not route to it. The argument is then reported asMissingArg.This shape is unusual, because
double_dash="required"mirrors clapArg::last(true), which is the final positional. The behavior is safe, but the reason for the>guard is not stated in the comment. Add a sentence that records this decision.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@lib/src/parse.rs` around lines 749 - 757, Add a concise comment beside the `target > next_arg_idx` guard explaining that required double-dash arguments declared before the current cursor are intentionally not revisited, preserving forward-only routing and allowing them to remain reported as `MissingArg`.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@cli/src/cli/complete_word.rs`:
- Around line 146-158: Centralize positional completion in a helper that applies
the `parsed.next_arg` and `parsed.double_dash_seen` rules, then use it from the
flag branch, `after_restart_token` path, and default-subcommand fallback instead
of calling `complete_arg` directly. When `double_dash_seen` is true, do not
classify dash-prefixed tokens as flags; when the next argument requires a
separator and it has not been seen, offer only `--` for an empty token and
suppress value/file completion. Add regressions covering dash-prefixed values
after `--`, `restart_token`, and `default_subcommand`.
---
Nitpick comments:
In `@lib/src/parse.rs`:
- Around line 749-757: Add a concise comment beside the `target > next_arg_idx`
guard explaining that required double-dash arguments declared before the current
cursor are intentionally not revisited, preserving forward-only routing and
allowing them to remain reported as `MissingArg`.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Central YAML (base), Organization UI (inherited)
Review profile: CHILL
Plan: Pro Plus
Run ID: e7144fcd-3818-4ed2-9de2-0952e680467a
⛔ Files ignored due to path filters (1)
mise.lockis excluded by!**/*.lock
📒 Files selected for processing (8)
cli/src/cli/complete_word.rscli/tests/complete_word.rsdocs/spec/reference/arg.mdexamples/double-dash.usage.kdllib/src/error.rslib/src/lib.rslib/src/parse.rslib/tests/parse.rs
bf9bb63 to
f4f00c7
Compare
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@cli/src/cli/complete_word.rs`:
- Around line 178-186: Preserve and propagate the constrained result returned by
complete_positional in this branch instead of discarding it. Update the
surrounding completion flow so a constrained default-subcommand argument that
matches no choices after -- remains constrained, preventing the fallback at the
later choices handling from offering file paths.
- Around line 122-124: Update the positional-token completion logic near
complete_path so dash-prefixed tokens after parsed.double_dash_seen still use
file completion when no explicit choices are configured. Keep flag completion
disabled after -- while removing the unconditional ctoken.starts_with('-')
exclusion from the positional file fallback.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Central YAML (base), Organization UI (inherited)
Review profile: CHILL
Plan: Pro Plus
Run ID: 6829058e-75bf-4b83-b091-c19a9cbcf3ab
📒 Files selected for processing (9)
cli/src/cli/complete_word.rscli/tests/complete_word.rsdocs/spec/reference/arg.mdexamples/double-dash-default-subcommand.usage.kdlexamples/double-dash.usage.kdllib/src/error.rslib/src/lib.rslib/src/parse.rslib/tests/parse.rs
🚧 Files skipped from review as they are similar to previous changes (5)
- lib/src/lib.rs
- lib/tests/parse.rs
- lib/src/error.rs
- docs/spec/reference/arg.md
- lib/src/parse.rs
f4f00c7 to
c5f8b7d
Compare
The parser never looked at `SpecDoubleDashChoices::Required`. Seeing a `--`
only disabled flag parsing, so an arg declared
arg "[file]" double_dash="required"
was filled whether or not the separator was given, and an arg sitting behind a
greedy variadic was never filled at all — even after a `--`. Reported against
mise in jdx/mise#5759, where `mise run echo test` and `mise run echo -- test`
both succeed.
Both halves of clap's `Arg::last(true)` are now implemented, which is what
`double_dash="required"` is generated from:
- A word offered to such an arg before an explicit `--` is rejected with
`ArgRequiresDoubleDash` and not assigned. A variadic arg reports this once
rather than once per word, and an arg that is both `required` and
`double_dash="required"` no longer also collects `MissingArg`.
- An explicit `--` moves the positional cursor onto the arg that requires it,
past earlier args including a greedy variadic.
Routing can leave an earlier arg empty, so "next arg" and "how many args are
filled" no longer coincide. The cursor becomes an explicit index and the two
loops that used `skip(out.args.len())` to find unfilled args — the `MissingArg`
check and the default/env application in `Parser::parse` — now ask `out.args`
by key instead of counting.
A `--` that `double_dash="preserve"` keeps as a value stays a value: it does not
count as a separator and does not unlock a required arg. `restart_token` resets
the separator, so each invocation after it needs its own `--`.
`ParseOutput` gains `next_arg` and `double_dash_seen` so completions follow the
same cursor as the parser. Every path that completes a positional — the one at
the cursor, the first arg after a `restart_token`, and a default subcommand's —
goes through one helper, so the rule cannot be honoured in one and forgotten in
another. Past a `--` the completion also stops offering flags, since the parser
reads everything after it as a positional value.
c5f8b7d to
22273ee
Compare
Fixes the root cause behind jdx/mise#5759.
Problem
The parser never looked at
SpecDoubleDashChoices::Required. Seeing a--only setenable_flags = false; nothing consulteddouble_dashwhen assigning positionals. So forboth of these succeeded:
The reverse half was missing too: an arg requiring
--that sits behind a greedy variadic was never reachable, even with a separator. Inexamples/mise.usage.kdlthat is[-- TASK_ARGS_LAST]...,[-- ARGS_LAST]...andexec's[-- COMMAND]...— all declared, none ever filled.Fix
double_dash="required"is generated from clap'sArg::last(true)(lib/src/spec/arg.rs,is_last_set() → Required), so both halves of that semantic are now implemented:--is reported asArgRequiresDoubleDashand not assigned. A variadic arg reports this once rather than once per word, and an arg that is bothrequiredanddouble_dash="required"no longer also collectsMissingArg. The check runs ahead ofvalidate_choicesso a discarded word is not additionally reported as an invalid choice.--moves the positional cursor onto the arg that requires it, past earlier args including a greedy variadic.Routing can leave an earlier arg empty, so "next arg" and "how many args are filled" no longer coincide. The cursor became an explicit index, and the two loops that used
skip(out.args.len())to find unfilled args — theMissingArgcheck and the default/env application inParser::parse— now askout.argsby key instead of counting. Without that, a skipped arg would silently lose its default and its missing-arg diagnostic.Interactions, both covered by tests:
--thatdouble_dash="preserve"keeps as a value stays a value. It does not count as a separator and does not unlock a required arg — one token cannot be both.restart_tokenresets the separator, so each invocation after it needs its own--.Specs with no
double_dash="required"arg are bit-for-bit unaffected: the cursor jump looks for such an arg and finds none.Completions
ParseOutputgainsnext_arg(where the cursor stopped) anddouble_dash_seen.complete-word's localnext_arg_for_completionre-derived the next arg fromargsand knew nothing about--, so with routing in place it would have disagreed with the parser; it now reads the parser's own cursor. While an arg is still locked behind a separator, the completion offers--itself rather than values the parser would reject.#[non_exhaustive], so this is technically a breaking API change. The only construction site is insideparse.rs. Happy to add#[non_exhaustive]in the same commit if you'd prefer to close that off now — say the word.Behavior changes
Measured against
examples/mise.usage.kdl:tasks add <TASK>+[-- RUN]...<TASK>(non-var)mise tasks add t echo hiis now an error — clap already rejects it, so this closes a divergence[-- TASK_ARGS_LAST]...[TASK_ARGS]...(greedy)--values move fromTASK_ARGStoTASK_ARGS_LASTtasks run [-- ARGS_LAST]...[ARGS]...(greedy)--values move fromARGStoARGS_LASTexec [-- COMMAND]...[TOOL@VERSION]...(greedy)--values move fromTOOL@VERSIONtoCOMMANDThe last three are the args finally doing what they were declared to do, but they change which
usage_*variable a consumer reads. @jdx — worth confirming this is what mise expects before release, since mise is the main consumer of these specs. Spec authors who want the old permissiveness can drop back todouble_dash="optional"(the default).double_dash="automatic"remains unimplemented in the parser; this PR does not change that, anddocs/spec/reference/arg.mdnow says so explicitly.Tests
29 new tests, no existing test modified:
lib/tests/parse.rs— 17 end-to-end cases over the three spellings (double_dash="required",arg "[-- x]",arg "<-- x>"), covering rejection, routing past a greedy variadic, single-error guarantees, choices non-interference, andoptionalstaying untouched.lib/src/parse.rs— 10 unit tests assertingParseOutputinternals: error counts and kinds, the gap left by routing (defaults andMissingArgstill correct),restart_tokenin both directions,preservenon-interference, andparse_partialstayingOkwhereparseerrors.cli/tests/complete_word.rs— 2 tests on a new choices-only fixtureexamples/double-dash.usage.kdl(no mount, no external completer, so it runs on every platform).cargo test -p usage-lib --all-featuresis green (313 + 52 + 5).cargo fmtandcargo clippyare clean on the touched targets. I could not run the full CLI suite locally — the.sh-fixture and/dev/stdouttests fail on Windows for reasons unrelated to this change — so CI is the real check there.This pull request was generated by Claude Code.
Summary by CodeRabbit
New Features
--separator.--are routed correctly, including with variadic arguments, restart tokens, and default subcommands.Bug Fixes
--when required and resume relevant suggestions afterward.Documentation