apple / apple/swift-argument-parser
@Argument(parsing:) can't properly handle arguments outside the leaf subcommand when using @OptionGroup to access the parent
- Dominant language
- Swift
- Stars
- 3.8k
- Forks
- 411
- Avg merge
- 7d 14h
- Merged PRs (30d)
- 15
Description
I have a CLI with two specific constraints:
- The top-level command has an argument that is passed *before* the subcommand name (to provide a uniform context). Doing this with `@OptionGroup` on the parent CLI type.
- Some subcommands want to collect free-form arguments so they can do more precise parsing (collecting related, order-sensitive flags) using `@Argument(parsing: .allUnrecognized)`.
When set up this way, Swift argument parser starts producing bogus errors when the command line is used correctly.
And further, when used incorrectly, the error messages don't reflect what's actually wrong.
**ArgumentParser version:** `1.5.0` and `main`
**Swift version:** Swift 5.11, multiple Swift 6 betas and pre-release toolchains.
### Checklist
- [x] If possible, I've reproduced the issue using the `main` branch of this package
- [x] I've searched for [existing GitHub issues](https://github.com/apple/swift-argument-parser/issues)
### Steps to Reproduce
It reproduces with this sample tool:
```swift
import ArgumentParser
@main
struct CLI: ParsableCommand {
@Argument
var context: String?
static let configuration = CommandConfiguration(subcommands: [Command.self])
struct Command: ParsableCommand {
@OptionGroup
var outer: CLI
@Option
var inner: Int?
@Argument(parsing: .allUnrecognized)
var query: [String]
mutating func run() throws {
if let context = outer.context {
print("Context: \(context)")
} else {
print("No context set")
}
if let inner {
print("Inner: \(inner)")
}
print("Query: \(query)")
}
}
}
```
### Behavior
Running:
```shell
$ swift run -- cli context-here command --include hello --inner 123 --exclude hi
```
Should output:
```
Context: context-here
Inner: 123
Query: ["--include", "hello", "--exclude", "hi"]
```
But instead fails with:
```
Error: Unknown option '--include'
Usage: cli command [] [--inner ] ...
See 'cli command --help' for more information.
```
---
And running:
```shell
$ swift run -- cli context-here command --include hello --inner 123 --exclude hi
```
Should output:
```
Context: context-here
Inner: 123
Query: ["--exclude", "hi"]
```
But instead fails with:
```
Error: Missing expected argument ' ...'
Help:
Usage: cli command [] [--inner ] ...
See 'cli command --help' for more information.
```
---
In both cases, the query fails to parse in different ways.
### Variations
The "Missing expected argument ' ...'" is caused by `query` being required.
If you make it `var query: [String] = []` then it doesn't produce that kind of error, it's always "Unknown option".
If you comment out the `@OptionGroup var outer: CLI`, then you can use the command line properly.
However, if you forget to pass the context argument on the outer CLI, like:
```shell
$ swift run -- cli command --inner 123 --exclude hi
```
Then you get the unhelpful error message:
```
Error: Unknown option '--inner'
Usage: cli []
See 'cli --help' for more information.
```
indicating that it just treated `command` as the context argument. It should probably give an error about the missing subcommand? But *also*, context is optional here, so it should maybe be treating the `command` as the subcommand? That might be more context-sensitive than the parsing is intended to provide though.
If you use `@Argument(parsing: .postTerminator)`, things work correctly, but you're forced to move all the flexible arguments to the end past the `--` terminator, which is dramatically worse UX.
Contributor guide
Research direction
Start with the sample CLI reproducer using @OptionGroup, an optional outer context, and @Argument(parsing: .allUnrecognized); compare its behavior with .postTerminator. Verify both command lines: flexible arguments should be collected without bogus errors, while missing context or subcommands should produce accurate diagnostics.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- swift
- Domain
- cli
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100