Skip to content

fix(parse): handle variadic ellipsis inside brackets like [args...] - #481

Merged
jdx merged 1 commit into
mainfrom
fix/variadic-arg-inside-brackets
Jan 31, 2026
Merged

fix(parse): handle variadic ellipsis inside brackets like [args...]#481
jdx merged 1 commit into
mainfrom
fix/variadic-arg-inside-brackets

Conversation

@jdx

@jdx jdx commented Jan 31, 2026

Copy link
Copy Markdown
Owner

Summary

  • Fix spec parser to recognize [args...] and <args...> as variadic arguments
  • Previously, only [args]... and <args>... (ellipsis outside brackets) were recognized as variadic
  • The ellipsis check ran before bracket stripping, so [args...] (ending with ]) was missed

Problem

When a usage spec defines arg "[args...]", the ... inside brackets was not parsed as making the arg variadic (var=true). This caused unknown flags like --host localhost to fail with "unexpected word: localhost" instead of being captured into the variadic arg.

Example failing spec:

flag "-v --verbose" var=#true
arg "[database]" default="myapp_dev"
arg "[args...]"

mydb --host localhost would error instead of setting database="mydb" and args=["--host", "localhost"].

Fix

Add a second ellipsis check after bracket stripping in From<&str> for SpecArg, so both [args]... and [args...] correctly set var=true.

Test plan

  • New unit tests for [args...], <args...>, and [args…] syntax in arg.rs
  • New integration parse tests verifying unknown flags are captured by variadic args
  • New unit test parsing spec from string (end-to-end spec parse → arg parse)
  • Updated dump round-trip test for <-- shell...> which is now correctly parsed as variadic
  • All 197 existing tests pass

🤖 Generated with Claude Code


Note

Low Risk
Low risk: small, targeted change to spec-string parsing for variadic args plus added coverage; main risk is minor behavior change for specs that previously treated [args...] as a literal name.

Overview
Fixes spec parsing so variadic positional args can be declared as [args...]/<args...> (including unicode ) by detecting ellipses after stripping the surrounding brackets.

Adds regression tests covering end-to-end parsing where unknown flags/words are captured into the variadic arg, and updates the dump round-trip expectation for a double_dash variadic arg representation.

Written by Cursor Bugbot for commit 600edf5. This will update automatically on new commits. Configure here.

The spec parser was not recognizing `[args...]` or `<args...>` as
variadic arguments. The ellipsis check only ran before bracket
stripping, so `[args...]` (ending with `]` not `...`) was missed.

Add a second ellipsis check after brackets are stripped so both
`[args]...` and `[args...]` correctly set var=true.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings January 31, 2026 11:34

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes the spec parser to correctly recognize variadic arguments when the ellipsis appears inside brackets (e.g., [args...] or <args...>). Previously, only the syntax with ellipsis outside brackets (e.g., [args]...) was supported, causing unknown flags to fail parsing instead of being captured by variadic arguments.

Changes:

  • Added a second ellipsis check in SpecArg::From<&str> that runs after bracket stripping
  • Added comprehensive test coverage for the new syntax variants
  • Updated an existing test case that now correctly parses <-- shell...> as variadic

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.

File Description
lib/src/spec/arg.rs Added logic to detect and strip ellipsis from inside brackets after initial bracket processing
lib/tests/parse.rs Added integration tests verifying unknown flags are captured by variadic args with [args...] syntax
lib/tests/dump.rs Updated test to reflect correct parsing of <-- shell...> as variadic with explicit var=#true
lib/src/parse.rs Added unit tests for end-to-end spec parsing and variadic argument behavior

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello @jdx, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request resolves a critical parsing bug that prevented command-line argument specifications from correctly identifying variadic arguments when the ellipsis was placed inside brackets. The fix enhances the argument parsing logic to accurately detect these patterns, ensuring that subsequent command-line inputs, such as unknown flags, are correctly captured by the variadic argument instead of causing parsing errors. This improves the robustness and flexibility of command-line interface definitions.

Highlights

  • Variadic Argument Parsing Fix: Corrected a parsing issue where variadic arguments with an ellipsis inside brackets (e.g., [args...] or <args...>) were not properly recognized. Previously, only [args]... or <args>... (ellipsis outside brackets) were handled.
  • Improved Ellipsis Detection Logic: A second ellipsis check has been added to the From<&str> for SpecArg implementation, occurring after bracket stripping. This ensures that both [args]... and [args...] syntaxes correctly set the var=true property for variadic arguments.
  • Enhanced Test Coverage: New unit tests were introduced for [args...], <args...>, and [args…] syntax, along with integration tests to verify that variadic arguments correctly capture unknown flags and their values. An existing dump round-trip test was also updated.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@codecov

codecov Bot commented Jan 31, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 90.74074% with 10 lines in your changes missing coverage. Please review.
✅ Project coverage is 70.71%. Comparing base (35bdd17) to head (600edf5).
⚠️ Report is 2 commits behind head on main.

Files with missing lines Patch % Lines
lib/src/spec/arg.rs 65.21% 1 Missing and 7 partials ⚠️
lib/src/parse.rs 97.64% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #481      +/-   ##
==========================================
+ Coverage   70.57%   70.71%   +0.13%     
==========================================
  Files          47       47              
  Lines        6597     6716     +119     
  Branches     6597     6716     +119     
==========================================
+ Hits         4656     4749      +93     
- Misses       1264     1277      +13     
- Partials      677      690      +13     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request correctly implements support for variadic arguments with the ellipsis inside the brackets (e.g., [args...]). The changes are well-structured, and the addition of unit and integration tests ensures the new syntax is parsed correctly and doesn't introduce regressions. I've added a couple of suggestions to make the new tests even more robust by asserting all parsed values. Overall, this is a great improvement to the parser's flexibility.

Comment thread lib/src/parse.rs
Comment on lines +1796 to +1808
assert_eq!(parsed.args.len(), 2);
let args_val = parsed
.args
.iter()
.find(|(a, _)| a.name == "args")
.unwrap()
.1;
match args_val {
ParseValue::MultiString(v) => {
assert_eq!(v, &vec!["--host".to_string(), "localhost".to_string()]);
}
_ => panic!("Expected MultiString, got {:?}", args_val),
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This test is great for verifying that unknown flags are captured by the variadic argument. To make it more robust and readable, you could assert the values of both database and args arguments. Converting the parsed arguments into a HashMap can simplify the assertions.

        assert_eq!(parsed.args.len(), 2);
        let args: std::collections::HashMap<_, _> = parsed.args.iter().map(|(k, v)| (k.name.as_str(), v)).collect();
        assert_eq!(args["database"].to_string(), "mydb");
        match args["args"] {
            ParseValue::MultiString(v) => {
                assert_eq!(v, &vec!["--host".to_string(), "localhost".to_string()]);
            }
            other => panic!("Expected MultiString for 'args', got {:?}", other),
        }

Comment thread lib/src/parse.rs
Comment on lines +1838 to +1850
assert_eq!(parsed.args.len(), 2);
let args_val = parsed
.args
.iter()
.find(|(a, _)| a.name == "args")
.unwrap()
.1;
match args_val {
ParseValue::MultiString(v) => {
assert_eq!(v, &vec!["--host".to_string(), "localhost".to_string()]);
}
_ => panic!("Expected MultiString, got {:?}", args_val),
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Similar to the previous test, this one could be made more robust by asserting the value of the database argument in addition to the args argument. Using a HashMap can make the assertions cleaner and more comprehensive.

        assert_eq!(parsed.args.len(), 2);
        let args: std::collections::HashMap<_, _> = parsed.args.iter().map(|(k, v)| (k.name.as_str(), v)).collect();
        assert_eq!(args["database"].to_string(), "mydb");
        match args["args"] {
            ParseValue::MultiString(v) => {
                assert_eq!(v, &vec!["--host".to_string(), "localhost".to_string()]);
            }
            other => panic!("Expected MultiString for 'args', got {:?}", other),
        }

@jdx
jdx merged commit ca0e5ac into main Jan 31, 2026
11 checks passed
@jdx
jdx deleted the fix/variadic-arg-inside-brackets branch January 31, 2026 11:45
tmeijn pushed a commit to tmeijn/dotfiles that referenced this pull request Feb 2, 2026
This MR contains the following updates:

| Package | Update | Change |
|---|---|---|
| [usage](https://github.com/jdx/usage) | minor | `2.15.1` → `2.16.1` |

MR created with the help of [el-capitano/tools/renovate-bot](https://gitlab.com/el-capitano/tools/renovate-bot).

**Proposed changes to behavior should be submitted there as MRs.**

---

### Release Notes

<details>
<summary>jdx/usage (usage)</summary>

### [`v2.16.1`](https://github.com/jdx/usage/blob/HEAD/CHANGELOG.md#2161---2026-01-31)

[Compare Source](jdx/usage@v2.16.0...v2.16.1)

##### 🐛 Bug Fixes

- **(docs)** increase gap between feature grid and action buttons on landing page by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;482](jdx/usage#482)
- **(parse)** handle variadic ellipsis inside brackets like \[args...] by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;481](jdx/usage#481)

##### 📚 Documentation

- add bash array pattern for variadic args by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;480](jdx/usage#480)

##### 📦️ Dependency Updates

- update rust crate clap to v4.5.56 by [@&#8203;renovate\[bot\]](https://github.com/renovate\[bot]) in [#&#8203;474](jdx/usage#474)
- update apple-actions/import-codesign-certs action to v6 by [@&#8203;renovate\[bot\]](https://github.com/renovate\[bot]) in [#&#8203;477](jdx/usage#477)
- update dependency node to v24 by [@&#8203;renovate\[bot\]](https://github.com/renovate\[bot]) in [#&#8203;478](jdx/usage#478)
- update rust crate criterion to 0.8 by [@&#8203;renovate\[bot\]](https://github.com/renovate\[bot]) in [#&#8203;475](jdx/usage#475)

### [`v2.16.0`](https://github.com/jdx/usage/blob/HEAD/CHANGELOG.md#2160---2026-01-29)

[Compare Source](jdx/usage@v2.15.1...v2.16.0)

##### 🚀 Features

- **(windows)** add Windows binaries and fix completion support by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;472](jdx/usage#472)

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever MR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this MR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this MR, check this box

---

This MR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0Mi45NC42IiwidXBkYXRlZEluVmVyIjoiNDIuOTUuMSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiUmVub3ZhdGUgQm90IiwiYXV0b21hdGlvbjpib3QtYXV0aG9yZWQiLCJkZXBlbmRlbmN5LXR5cGU6Om1pbm9yIl19-->
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.

2 participants