diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..e50014ff --- /dev/null +++ b/.gitattributes @@ -0,0 +1,4 @@ +*.go text eol=lf +*.md text eol=lf +*.sh text eol=lf +*.yaml text eol=lf diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 7adfc939..f41cc3f6 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -1,26 +1,38 @@ name: CI -on: - push: - branches: [master] - pull_request: +on: [push, pull_request] + +# Default to 'contents: read', which grants actions to read commits. Any +# permission not included is implicitly set to "none". +# +# see https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#permissions +permissions: + contents: read jobs: test: name: Test - runs-on: ubuntu-latest + runs-on: ${{ matrix.platform }} + timeout-minutes: 20 # guardrails timeout strategy: fail-fast: false matrix: - go: ["1.12", "1.21", "1.22", "1.23"] - + # Oldest supported + currently supported Go versions + go: ["1.12.x", "oldstable", "stable"] + platform: [ubuntu-latest, windows-latest, macos-latest] + exclude: + # go1.12 is not available for windows and macos on GitHub actions. + - go: "1.12.x" + platform: windows-latest + - go: "1.12.x" + platform: macos-latest steps: - name: Checkout repository - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - name: Set up Go - uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0 + uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0 with: go-version: ${{ matrix.go }} @@ -29,20 +41,7 @@ jobs: # run: go test -race -v -shuffle=on ./... run: go test -race -v ./... - lint: - name: Lint - runs-on: ubuntu-latest - - steps: - - name: Checkout repository - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - - - name: Set up Go - uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0 - with: - go-version: "1.23" - - name: Lint - uses: golangci/golangci-lint-action@971e284b6050e8a5849b72094c50ab08da042db8 # v6.1.1 + uses: golangci/golangci-lint-action@1e7e51e771db61008b38414a730f564565cf7c20 # v9.2.0 with: - version: v1.63.4 + version: v2.7 diff --git a/.golangci.yaml b/.golangci.yaml index b274f248..94ce2e02 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -1,4 +1,21 @@ +version: "2" + linters: - disable-all: true enable: + - errorlint - nolintlint + - revive + - unconvert + - unparam + settings: + staticcheck: + # Enable all options, with some exceptions. + # For defaults, see https://golangci-lint.run/usage/linters/#staticcheck + checks: + - all + - -QF1008 # Omit embedded fields from selector expression; https://staticcheck.dev/docs/checks/#QF1008 + - -ST1003 # Poorly chosen identifier; https://staticcheck.dev/docs/checks/#ST1003 + +formatters: + enable: + - gofmt diff --git a/bool_func_test.go b/bool_func_test.go index 765c9c08..672d23a7 100644 --- a/bool_func_test.go +++ b/bool_func_test.go @@ -50,7 +50,7 @@ func TestBoolFuncUsage(t *testing.T) { // regular boolfunc flag: // expect to see '--flag1' followed by the usageMessage, and no mention of a default value fset := NewFlagSet("unittest", ContinueOnError) - fset.BoolFunc("flag1", "usage message", func(s string) error { return nil }) + fset.BoolFunc("flag1", "usage message", func(string) error { return nil }) usage := fset.FlagUsagesWrapped(80) usage = strings.TrimSpace(usage) @@ -64,7 +64,7 @@ func TestBoolFuncUsage(t *testing.T) { // func flag, with a placeholder name: // if usageMesage contains a placeholder, expect '--flag2 {placeholder}'; still expect no mention of a default value fset := NewFlagSet("unittest", ContinueOnError) - fset.BoolFunc("flag2", "usage message with `name` placeholder", func(s string) error { return nil }) + fset.BoolFunc("flag2", "usage message with `name` placeholder", func(string) error { return nil }) usage := fset.FlagUsagesWrapped(80) usage = strings.TrimSpace(usage) diff --git a/bool_slice.go b/bool_slice.go index 3731370d..58a788d2 100644 --- a/bool_slice.go +++ b/bool_slice.go @@ -22,13 +22,12 @@ func newBoolSliceValue(val []bool, p *[]bool) *boolSliceValue { // Set converts, and assigns, the comma-separated boolean argument string representation as the []bool value of this flag. // If Set is called on a flag that already has a []bool assigned, the newly converted values will be appended. func (s *boolSliceValue) Set(val string) error { - // remove all quote characters rmQuote := strings.NewReplacer(`"`, "", `'`, "", "`", "") // read flag arguments with CSV parser boolStrSlice, err := readAsCSV(rmQuote.Replace(val)) - if err != nil && err != io.EOF { + if err != nil && err != io.EOF { //nolint:errorlint // not using errors.Is for compatibility with go1.12 return err } @@ -60,7 +59,6 @@ func (s *boolSliceValue) Type() string { // String defines a "native" format for this boolean slice flag value. func (s *boolSliceValue) String() string { - boolStrSlice := make([]string, len(*s.value)) for i, b := range *s.value { boolStrSlice[i] = strconv.FormatBool(b) diff --git a/bool_slice_test.go b/bool_slice_test.go index 3c5a274f..e6873b75 100644 --- a/bool_slice_test.go +++ b/bool_slice_test.go @@ -184,7 +184,6 @@ func TestBSAsSliceValue(t *testing.T) { } func TestBSBadQuoting(t *testing.T) { - tests := []struct { Want []bool FlagArg []string diff --git a/bool_test.go b/bool_test.go index a4319e79..e3704fcd 100644 --- a/bool_test.go +++ b/bool_test.go @@ -26,7 +26,7 @@ func (v *triStateValue) IsBoolFlag() bool { } func (v *triStateValue) Get() interface{} { - return triStateValue(*v) + return *v } func (v *triStateValue) Set(s string) error { diff --git a/bytes.go b/bytes.go index 67d53045..1c100848 100644 --- a/bytes.go +++ b/bytes.go @@ -18,7 +18,6 @@ func (bytesHex bytesHexValue) String() string { // Set implements pflag.Value.Set. func (bytesHex *bytesHexValue) Set(value string) error { bin, err := hex.DecodeString(strings.TrimSpace(value)) - if err != nil { return err } @@ -39,20 +38,18 @@ func newBytesHexValue(val []byte, p *[]byte) *bytesHexValue { } func bytesHexConv(sval string) (interface{}, error) { - bin, err := hex.DecodeString(sval) if err == nil { return bin, nil } - return nil, fmt.Errorf("invalid string being converted to Bytes: %s %s", sval, err) + return nil, fmt.Errorf("invalid string being converted to Bytes: %s %s", sval, err.Error()) } // GetBytesHex return the []byte value of a flag with the given name func (f *FlagSet) GetBytesHex(name string) ([]byte, error) { val, err := f.getFlagType(name, "bytesHex", bytesHexConv) - if err != nil { return []byte{}, err } @@ -119,7 +116,6 @@ func (bytesBase64 bytesBase64Value) String() string { // Set implements pflag.Value.Set. func (bytesBase64 *bytesBase64Value) Set(value string) error { bin, err := base64.StdEncoding.DecodeString(strings.TrimSpace(value)) - if err != nil { return err } @@ -140,19 +136,17 @@ func newBytesBase64Value(val []byte, p *[]byte) *bytesBase64Value { } func bytesBase64ValueConv(sval string) (interface{}, error) { - bin, err := base64.StdEncoding.DecodeString(sval) if err == nil { return bin, nil } - return nil, fmt.Errorf("invalid string being converted to Bytes: %s %s", sval, err) + return nil, fmt.Errorf("invalid string being converted to Bytes: %s %s", sval, err.Error()) } // GetBytesBase64 return the []byte value of a flag with the given name func (f *FlagSet) GetBytesBase64(name string) ([]byte, error) { val, err := f.getFlagType(name, "bytesBase64", bytesBase64ValueConv) - if err != nil { return []byte{}, err } diff --git a/count.go b/count.go index d49c0143..521b5d21 100644 --- a/count.go +++ b/count.go @@ -13,7 +13,7 @@ func newCountValue(val int, p *int) *countValue { func (i *countValue) Set(s string) error { // "+1" means that no specific value was passed, so increment if s == "+1" { - *i = countValue(*i + 1) + *i = *i + 1 return nil } v, err := strconv.ParseInt(s, 0, 0) diff --git a/duration_slice.go b/duration_slice.go index badadda5..ee987274 100644 --- a/duration_slice.go +++ b/duration_slice.go @@ -1,7 +1,6 @@ package pflag import ( - "fmt" "strings" "time" ) @@ -46,7 +45,7 @@ func (s *durationSliceValue) Type() string { func (s *durationSliceValue) String() string { out := make([]string, len(*s.value)) for i, d := range *s.value { - out[i] = fmt.Sprintf("%s", d) + out[i] = d.String() } return "[" + strings.Join(out, ",") + "]" } @@ -56,7 +55,7 @@ func (s *durationSliceValue) fromString(val string) (time.Duration, error) { } func (s *durationSliceValue) toString(val time.Duration) string { - return fmt.Sprintf("%s", val) + return val.String() } func (s *durationSliceValue) Append(val string) error { diff --git a/errors.go b/errors.go index ff11b66b..9bfa433a 100644 --- a/errors.go +++ b/errors.go @@ -142,7 +142,7 @@ func (e *InvalidSyntaxError) Error() string { return fmt.Sprintf("bad flag syntax: %s", e.specifiedFlag) } -// GetSpecifiedName returns the exact flag (with dashes) as it +// GetSpecifiedFlag returns the exact flag (with dashes) as it // appeared in the parsed arguments. func (e *InvalidSyntaxError) GetSpecifiedFlag() string { return e.specifiedFlag diff --git a/errors_test.go b/errors_test.go index 7b4c7a4d..11012f8f 100644 --- a/errors_test.go +++ b/errors_test.go @@ -51,8 +51,8 @@ func TestInvalidValueError(t *testing.T) { if err.GetValue() != "foo" { t.Errorf("Expected GetValue to return %q, got %q", "foo", err.GetValue()) } - if err.Unwrap() != expectedCause { - t.Errorf("Expected Unwrwap to return %q, got %q", expectedCause, err.Unwrap()) + if actual := err.Unwrap(); actual != expectedCause { //nolint:errorlint // not using errors.Is for compatibility with go1.12 + t.Errorf("Expected Unwrwap to return %q, got %q", expectedCause, actual) } } diff --git a/flag.go b/flag.go index e9ca46e2..a041a15d 100644 --- a/flag.go +++ b/flag.go @@ -270,7 +270,7 @@ func (f *FlagSet) GetNormalizeFunc() func(f *FlagSet, name string) NormalizedNam if f.normalizeNameFunc != nil { return f.normalizeNameFunc } - return func(f *FlagSet, name string) NormalizedName { return NormalizedName(name) } + return func(_ *FlagSet, name string) NormalizedName { return NormalizedName(name) } } func (f *FlagSet) normalizeFlagName(name string) NormalizedName { @@ -388,7 +388,7 @@ func (f *FlagSet) ShorthandLookup(name string) *Flag { } if len(name) > 1 { msg := fmt.Sprintf("can not look up shorthand which is more than one ASCII character: %q", name) - fmt.Fprintf(f.Output(), msg) + _, _ = fmt.Fprint(f.Output(), msg) panic(msg) } c := name[0] @@ -510,7 +510,7 @@ func (f *FlagSet) Set(name, value string) error { } if flag.Deprecated != "" { - fmt.Fprintf(f.Output(), "Flag --%s has been deprecated, %s\n", flag.Name, flag.Deprecated) + _, _ = fmt.Fprintf(f.Output(), "Flag --%s has been deprecated, %s\n", flag.Name, flag.Deprecated) } return nil } @@ -551,7 +551,7 @@ func Set(name, value string) error { // otherwise, the default values of all defined flags in the set. func (f *FlagSet) PrintDefaults() { usages := f.FlagUsages() - fmt.Fprint(f.Output(), usages) + _, _ = fmt.Fprint(f.Output(), usages) } // defaultIsZeroValue returns true if the default value for this flag represents @@ -657,7 +657,7 @@ func wrapN(i, slop int, s string) (string, string) { // caller). Pass `w` == 0 to do no wrapping func wrap(i, w int, s string) string { if w == 0 { - return strings.Replace(s, "\n", "\n"+strings.Repeat(" ", i), -1) + return strings.ReplaceAll(s, "\n", "\n"+strings.Repeat(" ", i)) } // space between indent i and end of line width w into which @@ -675,7 +675,7 @@ func wrap(i, w int, s string) string { } // If still not enough space then don't even try to wrap. if wrap < 24 { - return strings.Replace(s, "\n", r, -1) + return strings.ReplaceAll(s, "\n", r) } // Try to avoid short orphan words on the final line, by @@ -687,18 +687,17 @@ func wrap(i, w int, s string) string { // Handle first line, which is indented by the caller (or the // special case above) l, s = wrapN(wrap, slop, s) - r = r + strings.Replace(l, "\n", "\n"+strings.Repeat(" ", i), -1) + r = r + strings.ReplaceAll(l, "\n", "\n"+strings.Repeat(" ", i)) // Now wrap the rest for s != "" { var t string t, s = wrapN(wrap, slop, s) - r = r + "\n" + strings.Repeat(" ", i) + strings.Replace(t, "\n", "\n"+strings.Repeat(" ", i), -1) + r = r + "\n" + strings.Repeat(" ", i) + strings.ReplaceAll(t, "\n", "\n"+strings.Repeat(" ", i)) } return r - } // FlagUsagesWrapped returns a string containing the usage information @@ -772,7 +771,7 @@ func (f *FlagSet) FlagUsagesWrapped(cols int) string { sidx := strings.Index(line, "\x00") spacing := strings.Repeat(" ", maxlen-sidx) // maxlen + 2 comes from + 1 for the \x00 and + 1 for the (deliberate) off-by-one in maxlen-sidx - fmt.Fprintln(buf, line[:sidx], spacing, wrap(maxlen+2, cols, line[sidx+1:])) + _, _ = fmt.Fprintln(buf, line[:sidx], spacing, wrap(maxlen+2, cols, line[sidx+1:])) } return buf.String() @@ -791,7 +790,7 @@ func PrintDefaults() { // defaultUsage is the default function to print a usage message. func defaultUsage(f *FlagSet) { - fmt.Fprintf(f.Output(), "Usage of %s:\n", f.name) + _, _ = fmt.Fprintf(f.Output(), "Usage of %s:\n", f.name) f.PrintDefaults() } @@ -804,7 +803,7 @@ func defaultUsage(f *FlagSet) { // By default it prints a simple header and calls PrintDefaults; for details about the // format of the output and how to control it, see the documentation for PrintDefaults. var Usage = func() { - fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0]) + _, _ = fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0]) PrintDefaults() } @@ -877,7 +876,7 @@ func (f *FlagSet) AddFlag(flag *Flag) { _, alreadyThere := f.formal[normalizedFlagName] if alreadyThere { msg := fmt.Sprintf("%s flag redefined: %s", f.name, flag.Name) - fmt.Fprintln(f.Output(), msg) + _, _ = fmt.Fprintln(f.Output(), msg) panic(msg) // Happens only if flags are declared with identical names } if f.formal == nil { @@ -893,7 +892,7 @@ func (f *FlagSet) AddFlag(flag *Flag) { } if len(flag.Shorthand) > 1 { msg := fmt.Sprintf("%q shorthand is more than one ASCII character", flag.Shorthand) - fmt.Fprintf(f.Output(), msg) + _, _ = fmt.Fprint(f.Output(), msg) panic(msg) } if f.shorthands == nil { @@ -903,7 +902,7 @@ func (f *FlagSet) AddFlag(flag *Flag) { used, alreadyThere := f.shorthands[c] if alreadyThere { msg := fmt.Sprintf("unable to redefine %q shorthand in %q flagset: it's already used for %q flag", c, f.name, used.Name) - fmt.Fprintf(f.Output(), msg) + _, _ = fmt.Fprint(f.Output(), msg) panic(msg) } f.shorthands[c] = flag @@ -963,17 +962,17 @@ func (f *FlagSet) usage() { // --unknown arg ... (args will be arg ...) func stripUnknownFlagValue(args []string) []string { if len(args) == 0 { - //--unknown + // --unknown return args } first := args[0] if len(first) > 0 && first[0] == '-' { - //--unknown --next-flag ... + // --unknown --next-flag ... return args } - //--unknown arg ... (args will be arg ...) + // --unknown arg ... (args will be arg ...) if len(args) > 1 { return args[1:] } @@ -1035,7 +1034,7 @@ func (f *FlagSet) parseLongArg(s string, args []string, fn parseFunc) (a []strin err = fn(flag, value) if err != nil { - f.fail(err) + err = f.fail(err) } return } @@ -1106,12 +1105,12 @@ func (f *FlagSet) parseSingleShortArg(shorthands string, args []string, fn parse } if flag.ShorthandDeprecated != "" { - fmt.Fprintf(f.Output(), "Flag shorthand -%s has been deprecated, %s\n", flag.Shorthand, flag.ShorthandDeprecated) + _, _ = fmt.Fprintf(f.Output(), "Flag shorthand -%s has been deprecated, %s\n", flag.Shorthand, flag.ShorthandDeprecated) } err = fn(flag, value) if err != nil { - f.fail(err) + err = f.fail(err) } return } @@ -1169,7 +1168,7 @@ func (f *FlagSet) parseArgs(args []string, fn parseFunc) (err error) { func (f *FlagSet) Parse(arguments []string) error { if f.addedGoFlagSets != nil { for _, goFlagSet := range f.addedGoFlagSets { - goFlagSet.Parse(nil) + _ = goFlagSet.Parse(nil) } } f.parsed = true @@ -1190,10 +1189,10 @@ func (f *FlagSet) Parse(arguments []string) error { case ContinueOnError: return err case ExitOnError: - if err == ErrHelp { + if err == ErrHelp { //nolint:errorlint // not using errors.Is for compatibility with go1.12 os.Exit(0) } - fmt.Fprintln(f.Output(), err) + _, _ = fmt.Fprintln(f.Output(), err) os.Exit(2) case PanicOnError: panic(err) @@ -1219,10 +1218,10 @@ func (f *FlagSet) ParseAll(arguments []string, fn func(flag *Flag, value string) case ContinueOnError: return err case ExitOnError: - if err == ErrHelp { + if err == ErrHelp { //nolint:errorlint // not using errors.Is for compatibility with go1.12 os.Exit(0) } - fmt.Fprintln(f.Output(), err) + _, _ = fmt.Fprintln(f.Output(), err) os.Exit(2) case PanicOnError: panic(err) @@ -1240,7 +1239,7 @@ func (f *FlagSet) Parsed() bool { // after all flags are defined and before flags are accessed by the program. func Parse() { // Ignore errors; CommandLine is set for ExitOnError. - CommandLine.Parse(os.Args[1:]) + _ = CommandLine.Parse(os.Args[1:]) } // ParseAll parses the command-line flags from os.Args[1:] and called fn for each. @@ -1248,7 +1247,7 @@ func Parse() { // defined and before flags are accessed by the program. func ParseAll(fn func(flag *Flag, value string) error) { // Ignore errors; CommandLine is set for ExitOnError. - CommandLine.ParseAll(os.Args[1:], fn) + _ = CommandLine.ParseAll(os.Args[1:], fn) } // SetInterspersed sets whether to support interspersed option/non-option arguments. diff --git a/flag_test.go b/flag_test.go index c60e344b..b367f7d2 100644 --- a/flag_test.go +++ b/flag_test.go @@ -20,15 +20,16 @@ import ( ) var ( - testBool = Bool("test_bool", false, "bool value") - testInt = Int("test_int", 0, "int value") - testInt64 = Int64("test_int64", 0, "int64 value") - testUint = Uint("test_uint", 0, "uint value") - testUint64 = Uint64("test_uint64", 0, "uint64 value") - testString = String("test_string", "0", "string value") - testFloat = Float64("test_float64", 0, "float64 value") - testDuration = Duration("test_duration", 0, "time.Duration value") - testOptionalInt = Int("test_optional_int", 0, "optional int value") + _ = Bool("test_bool", false, "bool value") + _ = Int("test_int", 0, "int value") + _ = Int64("test_int64", 0, "int64 value") + _ = Uint("test_uint", 0, "uint value") + _ = Uint64("test_uint64", 0, "uint64 value") + _ = String("test_string", "0", "string value") + _ = Float64("test_float64", 0, "float64 value") + _ = Duration("test_duration", 0, "time.Duration value") + _ = Int("test_optional_int", 0, "optional int value") + normalizeFlagNameInvocations = 0 ) @@ -75,15 +76,33 @@ func TestEverything(t *testing.T) { } } // Now set all flags - Set("test_bool", "true") - Set("test_int", "1") - Set("test_int64", "1") - Set("test_uint", "1") - Set("test_uint64", "1") - Set("test_string", "1") - Set("test_float64", "1") - Set("test_duration", "1s") - Set("test_optional_int", "1") + if err := Set("test_bool", "true"); err != nil { + t.Error(err) + } + if err := Set("test_int", "1"); err != nil { + t.Error(err) + } + if err := Set("test_int64", "1"); err != nil { + t.Error(err) + } + if err := Set("test_uint", "1"); err != nil { + t.Error(err) + } + if err := Set("test_uint64", "1"); err != nil { + t.Error(err) + } + if err := Set("test_string", "1"); err != nil { + t.Error(err) + } + if err := Set("test_float64", "1"); err != nil { + t.Error(err) + } + if err := Set("test_duration", "1s"); err != nil { + t.Error(err) + } + if err := Set("test_optional_int", "1"); err != nil { + t.Error(err) + } desired = "1" Visit(visitor) if len(m) != 9 { @@ -620,7 +639,7 @@ func TestShorthandLookup(t *testing.T) { } flag := f.ShorthandLookup("a") if flag == nil { - t.Errorf("f.ShorthandLookup(\"a\") returned nil") + t.Fatal("f.ShorthandLookup(\"a\") returned nil") } if flag.Name != "boola" { t.Errorf("f.ShorthandLookup(\"a\") found %q instead of \"boola\"", flag.Name) @@ -630,9 +649,9 @@ func TestShorthandLookup(t *testing.T) { t.Errorf("f.ShorthandLookup(\"\") did not return nil") } defer func() { - recover() + _ = recover() }() - flag = f.ShorthandLookup("ab") + _ = f.ShorthandLookup("ab") // should NEVER get here. lookup should panic. defer'd func should recover it. t.Errorf("f.ShorthandLookup(\"ab\") did not panic") } @@ -732,18 +751,18 @@ func TestChangedHelper(t *testing.T) { } } -func replaceSeparators(name string, from []string, to string) string { +func toDotSeparated(name string, from []string) string { result := name for _, sep := range from { - result = strings.Replace(result, sep, to, -1) + result = strings.ReplaceAll(result, sep, ".") } // Type convert to indicate normalization has been done. return result } -func wordSepNormalizeFunc(f *FlagSet, name string) NormalizedName { +func wordSepNormalizeFunc(_ *FlagSet, name string) NormalizedName { seps := []string{"-", "_"} - name = replaceSeparators(name, seps, ".") + name = toDotSeparated(name, seps) normalizeFlagNameInvocations++ return NormalizedName(name) @@ -799,13 +818,13 @@ func TestWordSepNormalizedNames(t *testing.T) { testWordSepNormalizedNames(args, t) } -func aliasAndWordSepFlagNames(f *FlagSet, name string) NormalizedName { +func aliasAndWordSepFlagNames(_ *FlagSet, name string) NormalizedName { seps := []string{"-", "_"} - oldName := replaceSeparators("old-valid_flag", seps, ".") - newName := replaceSeparators("valid-flag", seps, ".") + oldName := toDotSeparated("old-valid_flag", seps) + newName := toDotSeparated("valid-flag", seps) - name = replaceSeparators(name, seps, ".") + name = toDotSeparated(name, seps) switch name { case oldName: name = newName @@ -906,7 +925,9 @@ func TestNormalizationSetFlags(t *testing.T) { } f.Bool(testName, false, "bool value") - f.Set(testName, "true") + if err := f.Set(testName, "true"); err != nil { + t.Error(err) + } f.SetNormalizeFunc(nfunc) if len(f.formal) != 1 { @@ -964,7 +985,12 @@ func TestSetOutput(t *testing.T) { var buf bytes.Buffer flags.SetOutput(&buf) flags.Init("test", ContinueOnError) - flags.Parse([]string{"--unknown"}) + + expectedErr := "unknown flag: --unknown" + err := flags.Parse([]string{"--unknown"}) + if err == nil || err.Error() != expectedErr { + t.Errorf("expected error %q got %v", expectedErr, err) + } if out := buf.String(); !strings.Contains(out, "--unknown") { t.Logf("expected output mentioning unknown; got %q", out) } @@ -975,7 +1001,7 @@ func TestOutput(t *testing.T) { var buf bytes.Buffer expect := "an example string" flags.SetOutput(&buf) - fmt.Fprint(flags.Output(), expect) + _, _ = fmt.Fprint(flags.Output(), expect) if out := buf.String(); !strings.Contains(out, expect) { t.Errorf("expected output %q; got %q", expect, out) } @@ -1027,7 +1053,7 @@ func TestHelp(t *testing.T) { if err == nil { t.Fatal("error expected") } - if err != ErrHelp { + if err != ErrHelp { //nolint:errorlint // not using errors.Is for compatibility with go1.12 t.Fatal("expected ErrHelp; got ", err) } if !helpCalled { @@ -1101,7 +1127,7 @@ func TestTermination(t *testing.T) { func getDeprecatedFlagSet() *FlagSet { f := NewFlagSet("bob", ContinueOnError) f.Bool("badflag", true, "always true") - f.MarkDeprecated("badflag", "use --good-flag instead") + _ = f.MarkDeprecated("badflag", "use --good-flag instead") return f } @@ -1142,7 +1168,7 @@ func TestDeprecatedFlagShorthandInDocs(t *testing.T) { f := NewFlagSet("bob", ContinueOnError) name := "noshorthandflag" f.BoolP(name, "n", true, "always true") - f.MarkShorthandDeprecated("noshorthandflag", fmt.Sprintf("use --%s instead", name)) + _ = f.MarkShorthandDeprecated("noshorthandflag", fmt.Sprintf("use --%s instead", name)) out := new(bytes.Buffer) f.SetOutput(out) @@ -1154,6 +1180,7 @@ func TestDeprecatedFlagShorthandInDocs(t *testing.T) { } func parseReturnStderr(t *testing.T, f *FlagSet, args []string) (string, error) { + t.Helper() oldStderr := os.Stderr r, w, _ := os.Pipe() os.Stderr = w @@ -1164,11 +1191,11 @@ func parseReturnStderr(t *testing.T, f *FlagSet, args []string) (string, error) // copy the output in a separate goroutine so printing can't block indefinitely go func() { var buf bytes.Buffer - io.Copy(&buf, r) + _, _ = io.Copy(&buf, r) outC <- buf.String() }() - w.Close() + _ = w.Close() os.Stderr = oldStderr out := <-outC @@ -1179,7 +1206,7 @@ func TestDeprecatedFlagUsage(t *testing.T) { f := NewFlagSet("bob", ContinueOnError) f.Bool("badflag", true, "always true") usageMsg := "use --good-flag instead" - f.MarkDeprecated("badflag", usageMsg) + _ = f.MarkDeprecated("badflag", usageMsg) args := []string{"--badflag"} out, err := parseReturnStderr(t, f, args) @@ -1197,7 +1224,7 @@ func TestDeprecatedFlagShorthandUsage(t *testing.T) { name := "noshorthandflag" f.BoolP(name, "n", true, "always true") usageMsg := fmt.Sprintf("use --%s instead", name) - f.MarkShorthandDeprecated(name, usageMsg) + _ = f.MarkShorthandDeprecated(name, usageMsg) args := []string{"-n"} out, err := parseReturnStderr(t, f, args) @@ -1215,7 +1242,7 @@ func TestDeprecatedFlagUsageNormalized(t *testing.T) { f.Bool("bad-double_flag", true, "always true") f.SetNormalizeFunc(wordSepNormalizeFunc) usageMsg := "use --good-flag instead" - f.MarkDeprecated("bad_double-flag", usageMsg) + _ = f.MarkDeprecated("bad_double-flag", usageMsg) args := []string{"--bad_double_flag"} out, err := parseReturnStderr(t, f, args) @@ -1244,7 +1271,7 @@ func TestMultipleNormalizeFlagNameInvocations(t *testing.T) { func TestHiddenFlagInUsage(t *testing.T) { f := NewFlagSet("bob", ContinueOnError) f.Bool("secretFlag", true, "shhh") - f.MarkHidden("secretFlag") + _ = f.MarkHidden("secretFlag") out := new(bytes.Buffer) f.SetOutput(out) @@ -1258,7 +1285,7 @@ func TestHiddenFlagInUsage(t *testing.T) { func TestHiddenFlagUsage(t *testing.T) { f := NewFlagSet("bob", ContinueOnError) f.Bool("secretFlag", true, "shhh") - f.MarkHidden("secretFlag") + _ = f.MarkHidden("secretFlag") args := []string{"--secretFlag"} out, err := parseReturnStderr(t, f, args) @@ -1360,7 +1387,7 @@ func TestVisitAllFlagOrder(t *testing.T) { fs := NewFlagSet("TestVisitAllFlagOrder", ContinueOnError) fs.SortFlags = false // https://github.com/spf13/pflag/issues/120 - fs.SetNormalizeFunc(func(f *FlagSet, name string) NormalizedName { + fs.SetNormalizeFunc(func(_ *FlagSet, name string) NormalizedName { return NormalizedName(name) }) @@ -1384,7 +1411,9 @@ func TestVisitFlagOrder(t *testing.T) { names := []string{"C", "B", "A", "D"} for _, name := range names { fs.Bool(name, false, "") - fs.Set(name, "true") + if err := fs.Set(name, "true"); err != nil { + t.Error(err) + } } i := 0 diff --git a/func_test.go b/func_test.go index 4badf936..212b1375 100644 --- a/func_test.go +++ b/func_test.go @@ -64,7 +64,7 @@ func TestFuncUsage(t *testing.T) { // regular func flag: // expect to see '--flag1 value' followed by the usageMessage, and no mention of a default value fset := NewFlagSet("unittest", ContinueOnError) - fset.Func("flag1", "usage message", func(s string) error { return nil }) + fset.Func("flag1", "usage message", func(string) error { return nil }) usage := fset.FlagUsagesWrapped(80) usage = strings.TrimSpace(usage) @@ -78,7 +78,7 @@ func TestFuncUsage(t *testing.T) { // func flag, with a placeholder name: // if usageMesage contains a placeholder, expect that name; still expect no mention of a default value fset := NewFlagSet("unittest", ContinueOnError) - fset.Func("flag2", "usage message with `name` placeholder", func(s string) error { return nil }) + fset.Func("flag2", "usage message with `name` placeholder", func(string) error { return nil }) usage := fset.FlagUsagesWrapped(80) usage = strings.TrimSpace(usage) diff --git a/golangflag.go b/golangflag.go index e62eab53..88a5c499 100644 --- a/golangflag.go +++ b/golangflag.go @@ -71,14 +71,14 @@ func (v *flagValueWrapper) Type() string { // If the *flag.Flag.Name was a single character (ex: `v`) it will be accessiblei // with both `-v` and `--v` in flags. If the golang flag was more than a single // character (ex: `verbose`) it will only be accessible via `--verbose` -func PFlagFromGoFlag(goflag *goflag.Flag) *Flag { +func PFlagFromGoFlag(goflag *goflag.Flag) *Flag { //nolint:revive // ignore "func name will be used as pflag.PFlagFromGoFlag by other packages, and that stutters" // Remember the default value as a string; it won't change. flag := &Flag{ Name: goflag.Name, Usage: goflag.Usage, Value: wrapFlagValue(goflag.Value), // Looks like golang flags don't set DefValue correctly :-( - //DefValue: goflag.DefValue, + // DefValue: goflag.DefValue, DefValue: goflag.Value.String(), } // Ex: if the golang flag was -v, allow both -v and --v to work @@ -158,4 +158,3 @@ func ParseSkippedFlags(osArgs []string, goFlagSet *goflag.FlagSet) error { } return goFlagSet.Parse(skippedFlags) } - diff --git a/golangflag_test.go b/golangflag_test.go index 7309808d..9ef6494a 100644 --- a/golangflag_test.go +++ b/golangflag_test.go @@ -74,7 +74,7 @@ func TestToGoflags(t *testing.T) { pfs.Duration("DurationFlag", time.Second, "Duration flag usage") pfs.Bool("BoolFlag", true, "Bool flag usage") pfs.String("deprecated", "Deprecated value", "Deprecated flag usage") - pfs.MarkDeprecated("deprecated", "obsolete") + _ = pfs.MarkDeprecated("deprecated", "obsolete") pfs.CopyToGoFlagSet(&gfs) diff --git a/ip_slice.go b/ip_slice.go index 775faae4..84fa4189 100644 --- a/ip_slice.go +++ b/ip_slice.go @@ -23,13 +23,12 @@ func newIPSliceValue(val []net.IP, p *[]net.IP) *ipSliceValue { // Set converts, and assigns, the comma-separated IP argument string representation as the []net.IP value of this flag. // If Set is called on a flag that already has a []net.IP assigned, the newly converted values will be appended. func (s *ipSliceValue) Set(val string) error { - // remove all quote characters rmQuote := strings.NewReplacer(`"`, "", `'`, "", "`", "") // read flag arguments with CSV parser ipStrSlice, err := readAsCSV(rmQuote.Replace(val)) - if err != nil && err != io.EOF { + if err != nil && err != io.EOF { //nolint:errorlint // not using errors.Is for compatibility with go1.12 return err } @@ -61,7 +60,6 @@ func (s *ipSliceValue) Type() string { // String defines a "native" format for this net.IP slice flag value. func (s *ipSliceValue) String() string { - ipStrSlice := make([]string, len(*s.value)) for i, ip := range *s.value { ipStrSlice[i] = ip.String() @@ -72,8 +70,8 @@ func (s *ipSliceValue) String() string { return "[" + out + "]" } -func (s *ipSliceValue) fromString(val string) (net.IP, error) { - return net.ParseIP(strings.TrimSpace(val)), nil +func (s *ipSliceValue) fromString(val string) net.IP { + return net.ParseIP(strings.TrimSpace(val)) } func (s *ipSliceValue) toString(val net.IP) string { @@ -81,22 +79,14 @@ func (s *ipSliceValue) toString(val net.IP) string { } func (s *ipSliceValue) Append(val string) error { - i, err := s.fromString(val) - if err != nil { - return err - } - *s.value = append(*s.value, i) + *s.value = append(*s.value, s.fromString(val)) return nil } func (s *ipSliceValue) Replace(val []string) error { out := make([]net.IP, len(val)) for i, d := range val { - var err error - out[i], err = s.fromString(d) - if err != nil { - return err - } + out[i] = s.fromString(d) } *s.value = out return nil diff --git a/ip_slice_test.go b/ip_slice_test.go index d1892768..366dc195 100644 --- a/ip_slice_test.go +++ b/ip_slice_test.go @@ -165,7 +165,6 @@ func TestIPSAsSliceValue(t *testing.T) { } func TestIPSBadQuoting(t *testing.T) { - tests := []struct { Want []net.IP FlagArg []string @@ -222,7 +221,8 @@ func TestIPSBadQuoting(t *testing.T) { }, FlagArg: []string{ `"2e5e:66b2:6441:848:5b74:76ea:574c:3a7b, 2e5e:66b2:6441:848:5b74:76ea:574c:3a7b,2e5e:66b2:6441:848:5b74:76ea:574c:3a7b "`, - " 2e5e:66b2:6441:848:5b74:76ea:574c:3a7b"}, + " 2e5e:66b2:6441:848:5b74:76ea:574c:3a7b", + }, }, } diff --git a/ipnet_slice.go b/ipnet_slice.go index c6e89da1..c051fbd2 100644 --- a/ipnet_slice.go +++ b/ipnet_slice.go @@ -23,13 +23,12 @@ func newIPNetSliceValue(val []net.IPNet, p *[]net.IPNet) *ipNetSliceValue { // Set converts, and assigns, the comma-separated IPNet argument string representation as the []net.IPNet value of this flag. // If Set is called on a flag that already has a []net.IPNet assigned, the newly converted values will be appended. func (s *ipNetSliceValue) Set(val string) error { - // remove all quote characters rmQuote := strings.NewReplacer(`"`, "", `'`, "", "`", "") // read flag arguments with CSV parser ipNetStrSlice, err := readAsCSV(rmQuote.Replace(val)) - if err != nil && err != io.EOF { + if err != nil && err != io.EOF { //nolint:errorlint // not using errors.Is for compatibility with go1.12 return err } @@ -61,7 +60,6 @@ func (s *ipNetSliceValue) Type() string { // String defines a "native" format for this net.IPNet slice flag value. func (s *ipNetSliceValue) String() string { - ipNetStrSlice := make([]string, len(*s.value)) for i, n := range *s.value { ipNetStrSlice[i] = n.String() diff --git a/ipnet_slice_test.go b/ipnet_slice_test.go index 11644c58..5ac387da 100644 --- a/ipnet_slice_test.go +++ b/ipnet_slice_test.go @@ -8,15 +8,12 @@ import ( ) // Helper function to set static slices -func getCIDR(ip net.IP, cidr *net.IPNet, err error) net.IPNet { +func getCIDR(_ net.IP, cidr *net.IPNet, _ error) net.IPNet { return *cidr } func equalCIDR(c1 net.IPNet, c2 net.IPNet) bool { - if c1.String() == c2.String() { - return true - } - return false + return c1.String() == c2.String() } func setUpIPNetFlagSet(ipsp *[]net.IPNet) *FlagSet { @@ -159,7 +156,6 @@ func TestIPNetCalledTwice(t *testing.T) { } func TestIPNetBadQuoting(t *testing.T) { - tests := []struct { Want []net.IPNet FlagArg []string @@ -216,7 +212,8 @@ func TestIPNetBadQuoting(t *testing.T) { }, FlagArg: []string{ `"2e5e:66b2:6441:848:5b74:76ea:574c:3a7b/128, 2e5e:66b2:6441:848:5b74:76ea:574c:3a7b/128,2e5e:66b2:6441:848:5b74:76ea:574c:3a7b/128 "`, - " 2e5e:66b2:6441:848:5b74:76ea:574c:3a7b/128"}, + " 2e5e:66b2:6441:848:5b74:76ea:574c:3a7b/128", + }, }, } diff --git a/string.go b/string.go index 04e0a26f..c96b8020 100644 --- a/string.go +++ b/string.go @@ -12,6 +12,7 @@ func (s *stringValue) Set(val string) error { *s = stringValue(val) return nil } + func (s *stringValue) Type() string { return "string" } diff --git a/string_array.go b/string_array.go index d1ff0a96..2404407f 100644 --- a/string_array.go +++ b/string_array.go @@ -30,18 +30,14 @@ func (s *stringArrayValue) Append(val string) error { func (s *stringArrayValue) Replace(val []string) error { out := make([]string, len(val)) - for i, d := range val { - out[i] = d - } + copy(out, val) *s.value = out return nil } func (s *stringArrayValue) GetSlice() []string { out := make([]string, len(*s.value)) - for i, d := range *s.value { - out[i] = d - } + copy(out, *s.value) return out } diff --git a/string_slice.go b/string_slice.go index 3cb2e69d..d421887e 100644 --- a/string_slice.go +++ b/string_slice.go @@ -98,9 +98,12 @@ func (f *FlagSet) GetStringSlice(name string) ([]string, error) { // The argument p points to a []string variable in which to store the value of the flag. // Compared to StringArray flags, StringSlice flags take comma-separated value as arguments and split them accordingly. // For example: -// --ss="v1,v2" --ss="v3" +// +// --ss="v1,v2" --ss="v3" +// // will result in -// []string{"v1", "v2", "v3"} +// +// []string{"v1", "v2", "v3"} func (f *FlagSet) StringSliceVar(p *[]string, name string, value []string, usage string) { f.VarP(newStringSliceValue(value, p), name, "", usage) } @@ -114,9 +117,12 @@ func (f *FlagSet) StringSliceVarP(p *[]string, name, shorthand string, value []s // The argument p points to a []string variable in which to store the value of the flag. // Compared to StringArray flags, StringSlice flags take comma-separated value as arguments and split them accordingly. // For example: -// --ss="v1,v2" --ss="v3" +// +// --ss="v1,v2" --ss="v3" +// // will result in -// []string{"v1", "v2", "v3"} +// +// []string{"v1", "v2", "v3"} func StringSliceVar(p *[]string, name string, value []string, usage string) { CommandLine.VarP(newStringSliceValue(value, p), name, "", usage) } @@ -130,9 +136,12 @@ func StringSliceVarP(p *[]string, name, shorthand string, value []string, usage // The return value is the address of a []string variable that stores the value of the flag. // Compared to StringArray flags, StringSlice flags take comma-separated value as arguments and split them accordingly. // For example: -// --ss="v1,v2" --ss="v3" +// +// --ss="v1,v2" --ss="v3" +// // will result in -// []string{"v1", "v2", "v3"} +// +// []string{"v1", "v2", "v3"} func (f *FlagSet) StringSlice(name string, value []string, usage string) *[]string { p := []string{} f.StringSliceVarP(&p, name, "", value, usage) @@ -150,9 +159,12 @@ func (f *FlagSet) StringSliceP(name, shorthand string, value []string, usage str // The return value is the address of a []string variable that stores the value of the flag. // Compared to StringArray flags, StringSlice flags take comma-separated value as arguments and split them accordingly. // For example: -// --ss="v1,v2" --ss="v3" +// +// --ss="v1,v2" --ss="v3" +// // will result in -// []string{"v1", "v2", "v3"} +// +// []string{"v1", "v2", "v3"} func StringSlice(name string, value []string, usage string) *[]string { return CommandLine.StringSliceP(name, "", value, usage) } diff --git a/text.go b/text.go index 886d5a3d..bb057ac3 100644 --- a/text.go +++ b/text.go @@ -42,8 +42,7 @@ func (v textValue) String() string { return "" } -//end of copy - +// end of copy func (v textValue) Type() string { return reflect.ValueOf(v.p).Type().Name() } diff --git a/text_test.go b/text_test.go index e60c136a..2889576a 100644 --- a/text_test.go +++ b/text_test.go @@ -20,7 +20,7 @@ func TestText(t *testing.T) { expected time.Time }{ {"2003-01-02T15:04:05Z", true, time.Date(2003, 1, 2, 15, 04, 05, 0, time.UTC)}, - {"2003-01-02 15:05:01", false, time.Time{}}, //negative case, invalid layout + {"2003-01-02 15:05:01", false, time.Time{}}, // negative case, invalid layout {"2024-11-22T03:01:02Z", true, time.Date(2024, 11, 22, 3, 1, 02, 0, time.UTC)}, {"2006-01-02T15:04:05+07:00", true, time.Date(2006, 1, 2, 15, 4, 5, 0, time.FixedZone("UTC+7", 7*60*60))}, } diff --git a/time.go b/time.go index 3dee4247..fe457dea 100644 --- a/time.go +++ b/time.go @@ -51,9 +51,8 @@ func (d *timeValue) Type() string { func (d *timeValue) String() string { if d.Time.IsZero() { return "" - } else { - return d.Time.Format(time.RFC3339Nano) } + return d.Time.Format(time.RFC3339Nano) } // GetTime return the time value of a flag with the given name diff --git a/uint64.go b/uint64.go index f62240f2..86d8c7e6 100644 --- a/uint64.go +++ b/uint64.go @@ -27,7 +27,7 @@ func uint64Conv(sval string) (interface{}, error) { if err != nil { return 0, err } - return uint64(v), nil + return v, nil } // GetUint64 return the uint64 value of a flag with the given name