apple / apple/swift-argument-parser
Overpowered @Argument(parsing: .allUnrecognized)
- Dominant language
- Swift
- Stars
- 3.8k
- Forks
- 411
- Avg merge
- 7d 13h
- Merged PRs (30d)
- 17
Description
Supercommand with `@Argument(parsing: .allUnrecognized)` suppresses argument parsing in subcommands with `@Argument(parsing: .allUnrecognized)`.
**ArgumentParser version:** `1.6.1`
**Swift version:** swift-driver version: 1.120.5 Apple Swift version 6.1.2 (swiftlang-6.1.2.1.2 clang-1700.0.13.5)
### 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
I'm trying to make a SwiftUI app work with Terminal and automation by hijacking `@main` with ArgumentParser and subcommands. The basic setup goes: if there was any known subcommands passed-in, the app would switch to CLI mode without calling SwiftUI's `main`.
```swift
@main
struct Main: ParsableCommand {
static let configuration = CommandConfiguration(
version: "1.0.0",
subcommands: [
ListStationsCommand.self,
]
)
@Argument(parsing: .allUnrecognized)
private var additionalOptions: [String] = []
func run() {
print("Launching SwiftUI App:", self.additionalOptions)
MySwiftUIApp.main()
}
}
struct ListStationsCommand: ParsableCommand {
static let configuration = CommandConfiguration(
commandName: "list",
version: "1.0.0"
)
@Argument(parsing: .allUnrecognized)
private var additionalOptions: [String] = []
func run() {
print("Stations: 4")
}
}
struct MySwiftUIApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
```
### Expected behavior
Since both `Main` (supercommand) and `ListStationsCommand` (subcommand) have `@Argument(parsing: .allUnrecognized)` properties, I'm expecting that as long as the second argument matches `list`, it would be recognized as subcommand for `ListStationsCommand`, and all remaining arguments passed to `ListStationsCommand`. (So long as parsing for `ListStationsCommand` didn't fail, it wouldn't because of `@Argument(parsing: .allUnrecognized)`.)
### Actual behavior
The behavior I'm seeing seems to be that, `Main.run()` was always called (presumably due to `@Argument(parsing: .allUnrecognized)`) without consulting subcommands or even built-in features like `help` and `--version`. This is particularly troublesome when Xcode injects shadow arguments intended for system frameworks during debugging, e.g. `-NSDocumentRevisionsDebugMode`, but overall an incorrect behavior nonetheless.
```bash
./App
# Prints empty array and launches SwiftUI app, baseline that ArgumentParser is running.
./App --help
# Prints help manual. The only call that's correct.
./App help list
# Suggested by help but won't work. Prints ["help", "list"] and launches SwiftUI.
./App list 1
# Should be handled by `ListStationsCommand` but prints ["list", "1"] and launches SwiftUI.
./App list --help
./App list --version
# Nothing works.
```
Contributor guide
Research direction
Start by reproducing the listed command invocations with Swift Argument Parser 1.6.1 or the main branch, focusing on how a supercommand with @Argument(parsing: .allUnrecognized) selects subcommands. Compare the behavior of `list`, `help list`, `list --help`, and `list --version`; done means subcommands and built-in features are recognized while remaining arguments are preserved.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- swift
- Domain
- cli
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Stale
- Clarity
- Clearly specified
- Newbie friendliness
- 45/100