apple / apple/swift-argument-parser

Overpowered @Argument(parsing: .allUnrecognized)

Open
#796 1 comment 0 reactions 0 assignees View on GitHub
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.