alecthomas / alecthomas/kingpin

Bare "-" argument is tokenized with an empty Value, so positional args receive "" instead of "-"

Open
#370 1 comment 0 reactions 0 assignees View on GitHub
Dominant language
Go
Stars
3.6k
Forks
279
PR merge metrics
No merged PRs in 30d

Description

## Summary

The tokenizer constructs the token for a bare `-` argument **without setting its `Value`**, so a lone dash on the command line reaches argument matching as an *empty string*. Applications consequently cannot receive `-` (the conventional "stdin" placeholder) as a positional argument value.

*This report is based on static analysis of master; I have not executed the code.*

## Location

- File: `parser.go`
- Function: `(*ParseContext).Next`, short-argument branch

```go
if strings.HasPrefix(arg, "-") {
if len(arg) == 1 {
return &Token{Index: p.argi, Type: TokenArg} // Value field omitted -> ""
}
...
}
```

and the consumption site in `parse`:

```go
case TokenArg:
...
context.matchedArg(arg, token.String())
```

where `(*Token).String()` for `TokenArg` returns `t.Value` — i.e. `""`.

## Problem

For input `-`, the tokenizer recognizes it as a non-flag argument but drops the actual character when building the token (`Token{Index: ..., Type: TokenArg}` has zero-valued `Value`). Every downstream consumer that uses `token.String()` as the argument value therefore sees an empty string instead of `"-"`.

Concretely, for:

```go
input := kingpin.Arg("input", "input file ('-' for stdin)").String()
kingpin.Parse()
```

running `myapp -` sets `*input` to `""`, not `"-"`.

Note the contrast with the surrounding code: every other path (`TokenLong`, combined short flags, `@file` expansion errors, plain arguments) explicitly propagates the value into the token.

## Trigger / Reproduction

Any application declaring a positional argument, invoked with a single `-` argument:

```sh
myapp -
```

(Static-analysis finding — derived from the token construction and `String()` implementations quoted above.)

## Expected Behavior

A bare `-` is treated as a regular argument whose value is `-` (this is the widespread POSIX convention for "read stdin"), i.e. the token should be `&Token{Index: p.argi, Type: TokenArg, Value: arg}`.

## Actual Behavior

The positional argument receives `""`.

## Impact

Apps built with kingpin cannot implement the standard `-`/stdin convention through declared args; the value is silently emptied rather than producing an error, so e.g. an `ExistingFile()`-validated arg reports the confusing error for `""` instead of `-`. Workarounds require pre-scanning `os.Args`.

## Suggested Direction

Populate the value: `return &Token{Index: p.argi, Type: TokenArg, Value: arg}`. If treating `-` as a flag-like special case was intentional, it should at minimum be documented; nothing in the docs mentions it.

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.