Fix govet lint findings; fix masked unset-on-array error path (#2129)

* plans/lintfixes.md

* plans/lintfixes.md

* Fix remaining govet lint findings

- Rename MarshalJSON -> FormatAsJSON on Mlrval and Mlrmap (govet
  stdmethods): the methods shadowed json.Marshaler with an
  incompatible signature.
- Remove unreachable return after exhaustive if-else in
  pkg/mlrval/mlrval_collections.go (govet unreachable).
- Update plans/lintfixes.md with current status: 84 findings remain
  (50 errcheck, 34 staticcheck).

Part of #2109.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Return nil on successful single-index array unset

removeIndexedOnArray removed the element on the in-bounds path but
then fell through to return an "array index out of bounds for unset"
error, so the success path never returned nil. Callers currently
ignore the error, which masked this; return nil on success so that
upcoming errcheck fixes can propagate the error meaningfully. This
matches removeIndexedOnMap, which returns nil on success.

Add unit tests for RemoveIndexed on arrays.

Part of #2109.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
John Kerl 2026-07-03 10:31:08 -04:00 committed by GitHub
parent 7fa7b1914f
commit 2bec10fc54
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 224 additions and 15 deletions

View file

@ -788,7 +788,7 @@ func BIF_json_parse(input1 *mlrval.Mlrval) *mlrval.Mlrval {
}
func BIF_json_stringify_unary(input1 *mlrval.Mlrval) *mlrval.Mlrval {
outputBytes, err := input1.MarshalJSON(mlrval.JSON_SINGLE_LINE, false)
outputBytes, err := input1.FormatAsJSON(mlrval.JSON_SINGLE_LINE, false)
if err != nil {
return mlrval.FromError(err)
}
@ -805,7 +805,7 @@ func BIF_json_stringify_binary(input1, input2 *mlrval.Mlrval) *mlrval.Mlrval {
jsonFormatting = mlrval.JSON_MULTILINE
}
outputBytes, err := input1.MarshalJSON(jsonFormatting, false)
outputBytes, err := input1.FormatAsJSON(jsonFormatting, false)
if err != nil {
return mlrval.FromError(err)
}

View file

@ -9,7 +9,7 @@ import (
"github.com/johnkerl/miller/v6/pkg/lib"
)
func (mlrmap *Mlrmap) MarshalJSON(
func (mlrmap *Mlrmap) FormatAsJSON(
jsonFormatting TJSONFormatting,
outputIsStdout bool,
) (string, error) {
@ -147,7 +147,7 @@ func (mlrmap *Mlrmap) marshalJSONAuxSingleLine(
func (entry *MlrmapEntry) JSONStringifyInPlace(
jsonFormatting TJSONFormatting,
) {
outputBytes, err := entry.Value.MarshalJSON(jsonFormatting, false)
outputBytes, err := entry.Value.FormatAsJSON(jsonFormatting, false)
if err != nil {
entry.Value = FromError(err)
} else {

View file

@ -29,7 +29,7 @@ func (mlrmap *Mlrmap) ToNIDXString() string {
// Must have non-pointer receiver in order to implement the fmt.Stringer
// interface to make mlrmap printable via fmt.Println et al.
func (mlrmap Mlrmap) String() string {
bytes, err := mlrmap.MarshalJSON(JSON_MULTILINE, false)
bytes, err := mlrmap.FormatAsJSON(JSON_MULTILINE, false)
if err != nil {
return "Mlrmap: could not not marshal self to JSON"
}

View file

@ -459,6 +459,7 @@ func removeIndexedOnArray(
leftSlice := (*baseArray)[0:zindex]
rightSlice := (*baseArray)[zindex+1 : len((*baseArray))]
*baseArray = append(leftSlice, rightSlice...)
return nil
} else if mindex.intf.(int64) == 0 {
return errors.New("zero indices are not supported. Indices are 1-up")
}
@ -474,8 +475,6 @@ func removeIndexedOnArray(
// TODO: improve wording
return errors.New("array index out of bounds for unset")
}
return nil
}
// Nominally for TopKeeper

View file

@ -0,0 +1,44 @@
package mlrval
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestRemoveIndexedOnArrayInBounds(t *testing.T) {
array := FromArray([]*Mlrval{FromInt(10), FromInt(20), FromInt(30)})
err := array.RemoveIndexed([]*Mlrval{FromInt(2)})
assert.Nil(t, err)
elements := array.GetArray()
assert.Equal(t, 2, len(elements))
assert.Equal(t, int64(10), elements[0].intf.(int64))
assert.Equal(t, int64(30), elements[1].intf.(int64))
}
func TestRemoveIndexedOnArrayNegativeIndex(t *testing.T) {
array := FromArray([]*Mlrval{FromInt(10), FromInt(20), FromInt(30)})
// Negative indices are aliased from the end, so -1 removes the last element.
err := array.RemoveIndexed([]*Mlrval{FromInt(-1)})
assert.Nil(t, err)
elements := array.GetArray()
assert.Equal(t, 2, len(elements))
assert.Equal(t, int64(10), elements[0].intf.(int64))
assert.Equal(t, int64(20), elements[1].intf.(int64))
}
func TestRemoveIndexedOnArrayOutOfBounds(t *testing.T) {
array := FromArray([]*Mlrval{FromInt(10), FromInt(20), FromInt(30)})
err := array.RemoveIndexed([]*Mlrval{FromInt(4)})
assert.NotNil(t, err)
assert.Equal(t, 3, len(array.GetArray()))
err = array.RemoveIndexed([]*Mlrval{FromInt(0)})
assert.NotNil(t, err)
assert.Equal(t, 3, len(array.GetArray()))
}

View file

@ -1,5 +1,5 @@
// Mlrval implements the Unmarshaler and Marshaler interfaces needed for
// marshaling/unmarshaling to/from JSON, via the UnmarshalJSON and MarshalJSON
// marshaling/unmarshaling to/from JSON, via the UnmarshalJSON and FormatAsJSON
// methods.
//
// Please see also https://golang.org/pkg/encoding/json/
@ -259,7 +259,7 @@ func MlrvalDecodeFromJSON(decoder *json.Decoder) (
}
}
func (mv *Mlrval) MarshalJSON(
func (mv *Mlrval) FormatAsJSON(
jsonFormatting TJSONFormatting,
outputIsStdout bool,
) (string, error) {

View file

@ -92,7 +92,7 @@ func (mv *Mlrval) setPrintRep() {
}
case MT_ARRAY:
bytes, err := mv.MarshalJSON(JSON_MULTILINE, false)
bytes, err := mv.FormatAsJSON(JSON_MULTILINE, false)
// maybe just InternalCodingErrorIf(err != nil)
if err != nil {
fmt.Fprintf(os.Stderr, "mlr: %v\n", err)
@ -101,7 +101,7 @@ func (mv *Mlrval) setPrintRep() {
mv.printrep = string(bytes)
case MT_MAP:
bytes, err := mv.MarshalJSON(JSON_MULTILINE, false)
bytes, err := mv.FormatAsJSON(JSON_MULTILINE, false)
// maybe just InternalCodingErrorIf(err != nil)
if err != nil {
fmt.Fprintf(os.Stderr, "mlr: %v\n", err)

View file

@ -74,9 +74,9 @@ func (writer *RecordWriterJSON) writeWithListWrap(
bufferedOutputStream.WriteString("[\n")
}
// The Mlrmap MarshalJSON doesn't include the final newline, so that we
// The Mlrmap FormatAsJSON doesn't include the final newline, so that we
// can place it neatly with commas here (if the user requested them).
s, err := outrec.MarshalJSON(writer.jsonFormatting, outputIsStdout)
s, err := outrec.FormatAsJSON(writer.jsonFormatting, outputIsStdout)
if err != nil {
fmt.Fprintf(os.Stderr, "mlr: %v\n", err)
os.Exit(1)
@ -115,9 +115,9 @@ func (writer *RecordWriterJSON) writeWithoutListWrap(
return
}
// The Mlrmap MarshalJSON doesn't include the final newline, so that we
// The Mlrmap FormatAsJSON doesn't include the final newline, so that we
// can place it neatly with commas here (if the user requested them).
s, err := outrec.MarshalJSON(writer.jsonFormatting, outputIsStdout)
s, err := outrec.FormatAsJSON(writer.jsonFormatting, outputIsStdout)
if err != nil {
fmt.Fprintf(os.Stderr, "mlr: %v\n", err)
os.Exit(1)