diff --git a/.golangci.yml b/.golangci.yml index 22c89a6beb..6dbbcc433d 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -31,6 +31,7 @@ linters: - govet - loggercheck - misspell + - modernize - nilnesserr # TODO(bwplotka): Enable once https://github.com/golangci/golangci-lint/issues/3228 is fixed. # - nolintlint @@ -117,6 +118,12 @@ linters: - shadow - fieldalignment enable-all: true + modernize: + disable: + # Suggest replacing omitempty with omitzero for struct fields. + # Disable this check for now since it introduces too many changes in our existing codebase. + # See https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#hdr-Analyzer_omitzero for more details. + - omitzero perfsprint: # Optimizes even if it requires an int or uint type cast. int-conversion: true diff --git a/Makefile.common b/Makefile.common index 3ed717b460..840bc0ea71 100644 --- a/Makefile.common +++ b/Makefile.common @@ -61,7 +61,7 @@ PROMU_URL := https://github.com/prometheus/promu/releases/download/v$(PROMU_ SKIP_GOLANGCI_LINT := GOLANGCI_LINT := GOLANGCI_LINT_OPTS ?= -GOLANGCI_LINT_VERSION ?= v2.6.0 +GOLANGCI_LINT_VERSION ?= v2.6.2 GOLANGCI_FMT_OPTS ?= # golangci-lint only supports linux, darwin and windows platforms on i386/amd64/arm64. # windows isn't included here because of the path separator being different. diff --git a/cmd/promtool/unittest.go b/cmd/promtool/unittest.go index 14557793c5..944ffc9d7c 100644 --- a/cmd/promtool/unittest.go +++ b/cmd/promtool/unittest.go @@ -196,7 +196,7 @@ type testStartTimestamp struct { // UnmarshalYAML implements custom YAML unmarshaling for testStartTimestamp. // It accepts both RFC3339 formatted strings and numeric Unix timestamps. -func (t *testStartTimestamp) UnmarshalYAML(unmarshal func(interface{}) error) error { +func (t *testStartTimestamp) UnmarshalYAML(unmarshal func(any) error) error { var s string if err := unmarshal(&s); err != nil { return err @@ -660,13 +660,14 @@ func (la labelsAndAnnotations) String() string { if len(la) == 0 { return "[]" } - s := "[\n0:" + indentLines("\n"+la[0].String(), " ") + var s strings.Builder + s.WriteString("[\n0:" + indentLines("\n"+la[0].String(), " ")) for i, l := range la[1:] { - s += ",\n" + strconv.Itoa(i+1) + ":" + indentLines("\n"+l.String(), " ") + s.WriteString(",\n" + strconv.Itoa(i+1) + ":" + indentLines("\n"+l.String(), " ")) } - s += "\n]" + s.WriteString("\n]") - return s + return s.String() } type labelAndAnnotation struct { @@ -717,11 +718,12 @@ func parsedSamplesString(pss []parsedSample) string { if len(pss) == 0 { return "nil" } - s := pss[0].String() + var s strings.Builder + s.WriteString(pss[0].String()) for _, ps := range pss[1:] { - s += ", " + ps.String() + s.WriteString(", " + ps.String()) } - return s + return s.String() } func (ps *parsedSample) String() string { diff --git a/discovery/aws/ecs.go b/discovery/aws/ecs.go index 3794ad178d..d6b36a7980 100644 --- a/discovery/aws/ecs.go +++ b/discovery/aws/ecs.go @@ -122,7 +122,7 @@ func (c *ECSSDConfig) NewDiscoverer(opts discovery.DiscovererOptions) (discovery } // UnmarshalYAML implements the yaml.Unmarshaler interface for the ECS Config. -func (c *ECSSDConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { +func (c *ECSSDConfig) UnmarshalYAML(unmarshal func(any) error) error { *c = DefaultECSSDConfig type plain ECSSDConfig err := unmarshal((*plain)(c)) @@ -461,10 +461,7 @@ func (d *ECSDiscovery) describeTasks(ctx context.Context, clusterARN string, tas func batchSlice[T any](a []T, size int) [][]T { batches := make([][]T, 0, len(a)/size+1) for i := 0; i < len(a); i += size { - end := i + size - if end > len(a) { - end = len(a) - } + end := min(i+size, len(a)) batches = append(batches, a[i:end]) } return batches diff --git a/documentation/examples/remote_storage/remote_storage_adapter/graphite/escape.go b/documentation/examples/remote_storage/remote_storage_adapter/graphite/escape.go index 1386f46761..3793973b7b 100644 --- a/documentation/examples/remote_storage/remote_storage_adapter/graphite/escape.go +++ b/documentation/examples/remote_storage/remote_storage_adapter/graphite/escape.go @@ -82,7 +82,7 @@ const ( func escape(tv model.LabelValue) string { length := len(tv) result := bytes.NewBuffer(make([]byte, 0, length)) - for i := 0; i < length; i++ { + for i := range length { b := tv[i] switch { // . is reserved by graphite, % is used to escape other bytes. diff --git a/documentation/examples/remote_storage/remote_storage_adapter/influxdb/client.go b/documentation/examples/remote_storage/remote_storage_adapter/influxdb/client.go index 005f8d534d..ffd81802c1 100644 --- a/documentation/examples/remote_storage/remote_storage_adapter/influxdb/client.go +++ b/documentation/examples/remote_storage/remote_storage_adapter/influxdb/client.go @@ -96,7 +96,7 @@ func (c *Client) Write(samples model.Samples) error { p := influx.NewPoint( string(s.Metric[model.MetricNameLabel]), tagsFromMetric(s.Metric), - map[string]interface{}{"value": v}, + map[string]any{"value": v}, s.Timestamp.Time(), ) points = append(points, p) @@ -158,16 +158,17 @@ func (c *Client) buildCommand(q *prompb.Query) (string, error) { // If we don't find a metric name matcher, query all metrics // (InfluxDB measurements) by default. - measurement := `r._measurement` + var measurement strings.Builder + measurement.WriteString(`r._measurement`) matchers := make([]string, 0, len(q.Matchers)) var joinedMatchers string for _, m := range q.Matchers { if m.Name == model.MetricNameLabel { switch m.Type { case prompb.LabelMatcher_EQ: - measurement += fmt.Sprintf(" == \"%s\"", m.Value) + measurement.WriteString(fmt.Sprintf(" == \"%s\"", m.Value)) case prompb.LabelMatcher_RE: - measurement += fmt.Sprintf(" =~ /%s/", escapeSlashes(m.Value)) + measurement.WriteString(fmt.Sprintf(" =~ /%s/", escapeSlashes(m.Value))) default: // TODO: Figure out how to support these efficiently. return "", errors.New("non-equal or regex-non-equal matchers are not supported on the metric name yet") @@ -195,7 +196,7 @@ func (c *Client) buildCommand(q *prompb.Query) (string, error) { // _measurement must be retained, otherwise "invalid metric name" shall be thrown command := fmt.Sprintf( "from(bucket: \"%s\") |> range(%s) |> filter(fn: (r) => %s%s)", - c.bucket, rangeInNs, measurement, joinedMatchers, + c.bucket, rangeInNs, measurement.String(), joinedMatchers, ) return command, nil @@ -237,7 +238,7 @@ func mergeResult(labelsToSeries map[string]*prompb.TimeSeries, record *query.Flu return nil } -func filterOutBuiltInLabels(labels map[string]interface{}) { +func filterOutBuiltInLabels(labels map[string]any) { delete(labels, "table") delete(labels, "_start") delete(labels, "_stop") @@ -248,7 +249,7 @@ func filterOutBuiltInLabels(labels map[string]interface{}) { delete(labels, "_measurement") } -func concatLabels(labels map[string]interface{}) string { +func concatLabels(labels map[string]any) string { // 0xff cannot occur in valid UTF-8 sequences, so use it // as a separator here. separator := "\xff" @@ -259,7 +260,7 @@ func concatLabels(labels map[string]interface{}) string { return strings.Join(pairs, separator) } -func tagsToLabelPairs(name string, tags map[string]interface{}) []prompb.Label { +func tagsToLabelPairs(name string, tags map[string]any) []prompb.Label { pairs := make([]prompb.Label, 0, len(tags)) for k, v := range tags { if v == nil { @@ -283,7 +284,7 @@ func tagsToLabelPairs(name string, tags map[string]interface{}) []prompb.Label { return pairs } -func valuesToSamples(timestamp time.Time, value interface{}) (prompb.Sample, error) { +func valuesToSamples(timestamp time.Time, value any) (prompb.Sample, error) { var valueFloat64 float64 var valueInt64 int64 var ok bool diff --git a/documentation/examples/remote_storage/remote_storage_adapter/opentsdb/tagvalue.go b/documentation/examples/remote_storage/remote_storage_adapter/opentsdb/tagvalue.go index 6a691778af..c40f829a56 100644 --- a/documentation/examples/remote_storage/remote_storage_adapter/opentsdb/tagvalue.go +++ b/documentation/examples/remote_storage/remote_storage_adapter/opentsdb/tagvalue.go @@ -66,7 +66,7 @@ func (tv TagValue) MarshalJSON() ([]byte, error) { // Need at least two more bytes than in tv. result := bytes.NewBuffer(make([]byte, 0, length+2)) result.WriteByte('"') - for i := 0; i < length; i++ { + for i := range length { b := tv[i] switch { case (b >= '-' && b <= '9') || // '-', '.', '/', 0-9 diff --git a/model/labels/labels_slicelabels.go b/model/labels/labels_slicelabels.go index 21ad145c1c..e999432bf4 100644 --- a/model/labels/labels_slicelabels.go +++ b/model/labels/labels_slicelabels.go @@ -297,12 +297,9 @@ func FromStrings(ss ...string) Labels { // Compare compares the two label sets. // The result will be 0 if a==b, <0 if a < b, and >0 if a > b. func Compare(a, b Labels) int { - l := len(a) - if len(b) < l { - l = len(b) - } + l := min(len(b), len(a)) - for i := 0; i < l; i++ { + for i := range l { if a[i].Name != b[i].Name { if a[i].Name < b[i].Name { return -1 @@ -419,10 +416,7 @@ func (b *Builder) Labels() Labels { return b.base } - expectedSize := len(b.base) + len(b.add) - len(b.del) - if expectedSize < 1 { - expectedSize = 1 - } + expectedSize := max(len(b.base)+len(b.add)-len(b.del), 1) res := make(Labels, 0, expectedSize) for _, l := range b.base { if slices.Contains(b.del, l.Name) || contains(b.add, l.Name) { diff --git a/model/labels/labels_slicelabels_test.go b/model/labels/labels_slicelabels_test.go index 7961828378..0e55730082 100644 --- a/model/labels/labels_slicelabels_test.go +++ b/model/labels/labels_slicelabels_test.go @@ -77,8 +77,8 @@ func BenchmarkScratchBuilderUnsafeAdd(b *testing.B) { l.SetUnsafeAdd(true) b.ReportAllocs() - b.ResetTimer() - for i := 0; i < b.N; i++ { + + for b.Loop() { l.Add("__name__", "metric1") l.add = l.add[:0] // Reset slice so add can be repeated without side effects. } diff --git a/promql/parser/printer.go b/promql/parser/printer.go index c315bface7..961167428b 100644 --- a/promql/parser/printer.go +++ b/promql/parser/printer.go @@ -37,15 +37,16 @@ func tree(node Node, level string) string { } typs := strings.Split(fmt.Sprintf("%T", node), ".")[1] - t := fmt.Sprintf("%s |---- %s :: %s\n", level, typs, node) + var t strings.Builder + t.WriteString(fmt.Sprintf("%s |---- %s :: %s\n", level, typs, node)) level += " · · ·" for e := range ChildrenIter(node) { - t += tree(e, level) + t.WriteString(tree(e, level)) } - return t + return t.String() } func (node *EvalStmt) String() string { diff --git a/tsdb/head_test.go b/tsdb/head_test.go index 552db13d07..d3e6ca9bcc 100644 --- a/tsdb/head_test.go +++ b/tsdb/head_test.go @@ -6525,19 +6525,19 @@ func TestWALSampleAndExemplarOrder(t *testing.T) { appendF: func(app storage.Appender, ts int64) (storage.SeriesRef, error) { return app.Append(0, lbls, ts, 1.0) }, - expectedType: reflect.TypeOf([]record.RefSample{}), + expectedType: reflect.TypeFor[[]record.RefSample](), }, "histogram sample": { appendF: func(app storage.Appender, ts int64) (storage.SeriesRef, error) { return app.AppendHistogram(0, lbls, ts, tsdbutil.GenerateTestHistogram(1), nil) }, - expectedType: reflect.TypeOf([]record.RefHistogramSample{}), + expectedType: reflect.TypeFor[[]record.RefHistogramSample](), }, "float histogram sample": { appendF: func(app storage.Appender, ts int64) (storage.SeriesRef, error) { return app.AppendHistogram(0, lbls, ts, nil, tsdbutil.GenerateTestFloatHistogram(1)) }, - expectedType: reflect.TypeOf([]record.RefFloatHistogramSample{}), + expectedType: reflect.TypeFor[[]record.RefFloatHistogramSample](), }, } diff --git a/tsdb/querier_test.go b/tsdb/querier_test.go index 5f5af441ca..6c3e37792f 100644 --- a/tsdb/querier_test.go +++ b/tsdb/querier_test.go @@ -23,6 +23,7 @@ import ( "slices" "sort" "strconv" + "strings" "sync" "testing" "time" @@ -3037,14 +3038,14 @@ func TestPostingsForMatchers(t *testing.T) { require.NoError(t, err) for _, c := range cases { - name := "" + var name strings.Builder for i, matcher := range c.matchers { if i > 0 { - name += "," + name.WriteString(",") } - name += matcher.String() + name.WriteString(matcher.String()) } - t.Run(name, func(t *testing.T) { + t.Run(name.String(), func(t *testing.T) { exp := map[string]struct{}{} for _, l := range c.exp { exp[l.String()] = struct{}{}