Skip to content

Overpowered @Argument(parsing: .allUnrecognized) #796

@VaslD

Description

@VaslD

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

  • If possible, I've reproduced the issue using the main branch of this package
  • I've searched for existing GitHub 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.

@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.

./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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions