Skip to content

A bare "-" argument silently discards all following arguments in parseFlags #2418

Description

@Poojax21

Summary

In parseFlags (v3 main), the branch handling a lone - argument appends it to posArgs and then executes a plain break, which exits the entire rearrangement loop. Every remaining command-line argument after the - is dropped without error: myapp - foo bar ends up with cmd.Args() == ["-"].

Static-analysis finding based on reading main; not executed here.

Location

  • File: command_parse.go
  • Function: (*Command).parseFlags
  • Relevant code:
	numMinuses := 1
	// this is same as firstArg == "-"
	if len(firstArg) == 1 {
		posArgs = append(posArgs, firstArg)
		break
	}

Problem

Compare this branch with every sibling path in the same loop:

  • empty argument ? append(posArgs, rargs[0]) + continue
  • "--" ? appends the arg (if completion mode) plus all of rargs[1:], then returns
  • positional (firstArg[0] != '-') ? append(posArgs, firstArg) + continue; if it names a subcommand, append(posArgs, rargs...) and return
  • non-letter after - (e.g. -3) ? append(posArgs, rargs...) + return

The lone - is the only hyphen-path that stops parsing without preserving the unconsumed remainder. Since the loop's post-statement only advances rargs between iterations, break here throws away rargs[1:] entirely.

Trigger / Reproduction

Based on source reading:

$ myapp - foo bar

Iteration 1: firstArg == "-" ? len(firstArg) == 1 matches ? posArgs = ["-"] ? break.
Result: cmd.Args().Len() == 1, containing only -. foo and bar never reach the action.

Expected: the arguments are retained - either ["-", "foo", "bar"] (matching Go's stdlib flag, which treats a bare - like an ordinary operand) or at minimum everything from - onward, mirroring the -- and non-letter branches.

Expected Behavior

A single hyphen is conventionally used as an operand (stdin placeholder) or a terminator; in both interpretations the arguments that follow must survive into cmd.Args().

Actual Behavior

They are silently discarded; no error is returned.

Impact

Any CLI invocation that passes - (very common for "read stdin") followed by further operands loses those operands. Because nothing errors, callers observe truncated Args() with no diagnostic, which is difficult to debug downstream.

Suggested Direction

Mirror the neighboring branches: replace break with appending the rest of rargs and returning (terminator semantics), or continue after appending just firstArg (operand semantics). Either preserves user data; which one matches intent should be confirmed against the v2 behavior.

Evidence

  • Loop structure at the top of parseFlags shows no other mechanism that would revisit discarded args.
  • All four alternative branches in the same function explicitly preserve the remainder, demonstrating that dropping it here is anomalous rather than intended.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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