Skip to content

--help goes to default subcommand when not at top-level #865

@stackotter

Description

@stackotter

If your top-level command has a default subcommand, --help displays the top-level commands subcommands rather than the help for the default subcommand. This is expected behaviour.

If a subcommand has a default subcommand, --help displays the subcommand's help with no indication that you've actually invoked a default subcommand and there are other subcommands available. This is the behaviour that this issue is reporting.

I believe that #612 introduced this behaviour. I'm not entirely sure why this change fixes their original issue. It seems like it was more of a hack than a solution to me. It also feels like the change would've strictly decreased the set of situations that lead to help being invoked, which seems counter to what they were trying to fix.

ArgumentParser version: 0.1.0 or the main branch, for example.
Swift version: Paste the output of swift --version here.

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

The following argument parser setup reproduces the issue;

import ArgumentParser

@main
struct ArgumentParserReproducer: ParsableCommand {
    static let configuration = CommandConfiguration(
        commandName: "reproducer",
        subcommands: [
            DefaultSubcommand.self,
            OtherSubcommand.self
        ],
        defaultSubcommand: DefaultSubcommand.self
    )

    mutating func run() throws {
        print("Hello, world!")
    }
}

struct DefaultSubcommand: ParsableCommand {
    static let configuration = CommandConfiguration(
        commandName: "foo"
    )

    @Option
    var name: String

    mutating func run() throws {
        print("foo: \(name)")
    }
}

struct OtherSubcommand: ParsableCommand {
    static let configuration = CommandConfiguration(
        commandName: "bar",
        subcommands: [
            DefaultSubSubcommand.self,
            OtherSubSubcommand.self
        ],
        defaultSubcommand: DefaultSubSubcommand.self
    )

    struct DefaultSubSubcommand: ParsableCommand {
        static let configuration = CommandConfiguration(
            commandName: "baz"
        )

        @Option
        var user: String

        mutating func run() throws {
            print("baz: \(user)")
        }
    }

    struct OtherSubSubcommand: ParsableCommand {
        static let configuration = CommandConfiguration(
            commandName: "hello"
        )

        @Option
        var age: Int

        mutating func run() throws {
            print("hello: \(age)")
        }
    }
}

--help at the top level shows both available subcommands and highlights that foo is the default subcommand;

$ .build/debug/ArgumentParserReproducer --help
USAGE: reproducer <subcommand>

OPTIONS:
  -h, --help              Show help information.

SUBCOMMANDS:
  foo (default)
  bar

  See 'reproducer help <subcommand>' for detailed help.

--help on bar shows the help for bar baz (the default command of bar) rather than showing both of bar's subcommands;

$ .build/debug/ArgumentParserReproducer bar --help
USAGE: reproducer bar baz --user <user>

OPTIONS:
  --user <user>
  -h, --help              Show help information.

Expected behavior

--help should have the same behaviour when used on a subcommand as when used on the top-level command.

Actual behavior

When used on a subcommand that has a default subcommand, --help shows the default subcommand's help rather than the parent subcommand's help.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions