Skip to content

Commit 03571df

Browse files
committed
.github: add golangci lint
1 parent 39414ab commit 03571df

19 files changed

+169
-44
lines changed

.github/workflows/lint.yaml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
name: Lint
2+
on: push
3+
jobs:
4+
golangci:
5+
runs-on: ubuntu-latest
6+
steps:
7+
- name: Checkout
8+
uses: actions/checkout@v4
9+
10+
- name: Setup Go
11+
uses: actions/setup-go@v5
12+
with:
13+
go-version-file: go.mod
14+
15+
- name: Run
16+
uses: golangci/golangci-lint-action@v7
17+
with:
18+
version: v2.1.5

.github/workflows/test.yaml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
name: Test
2+
on: push
3+
jobs:
4+
test:
5+
runs-on: ubuntu-latest
6+
steps:
7+
- name: Checkout
8+
uses: actions/checkout@v4
9+
10+
- name: Setup Go
11+
uses: actions/setup-go@v5
12+
with:
13+
go-version-file: go.mod
14+
15+
- run: go test -race ./...
16+
17+
fuzz:
18+
runs-on: ubuntu-latest
19+
steps:
20+
- name: Checkout
21+
uses: actions/checkout@v4
22+
23+
- name: Setup Go
24+
uses: actions/setup-go@v5
25+
with:
26+
go-version-file: go.mod
27+
28+
- run: go test -fuzztime 15s -fuzz=Fuzz ./trace

.golangci.yaml

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
#file: noinspection SpellCheckingInspection
2+
version: "2"
3+
linters:
4+
enable:
5+
- depguard
6+
- dupword
7+
- durationcheck
8+
- errchkjson
9+
- errname
10+
- errorlint
11+
- exhaustive
12+
- exptostd
13+
- forcetypeassert
14+
- gocheckcompilerdirectives
15+
- gochecknoglobals
16+
- gochecknoinits
17+
- gochecksumtype
18+
- gocritic
19+
- godot
20+
- godox
21+
- gosec
22+
- intrange
23+
- mirror
24+
- misspell
25+
- nilerr
26+
- nilnesserr
27+
- nilnil
28+
- nolintlint
29+
- predeclared
30+
- reassign
31+
- recvcheck
32+
- sloglint
33+
- unconvert
34+
- unparam
35+
- usestdlibvars
36+
- usetesting
37+
- wastedassign
38+
- wrapcheck
39+
exclusions:
40+
generated: lax
41+
presets:
42+
- comments
43+
- common-false-positives
44+
- legacy
45+
- std-error-handling
46+
settings:
47+
depguard:
48+
rules:
49+
Main:
50+
list-mode: strict
51+
allow: ["$gostd", "github.com/jschaf/observe"]
52+
wrapcheck:
53+
extra-ignore-sigs: ["json.Marshal("]
54+
formatters:
55+
enable:
56+
- gofumpt
57+
exclusions:
58+
generated: lax

internal/difftest/assert_same.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,18 @@ func AssertSame[T any](t *testing.T, msg string, want, got T) {
1515
}
1616
}
1717

18+
//golint:ignore:asciicheck
1819
func diff(a, b any) string {
1920
switch x := a.(type) {
2021
case string:
21-
return diffString(x, b.(string))
22+
y, _ := b.(string)
23+
return diffString(x, y)
2224
case time.Time:
23-
return diffTime(x, b.(time.Time))
25+
y, _ := b.(time.Time)
26+
return diffTime(x, y)
2427
case http.Header:
25-
if equalMaps(x, b.(http.Header)) {
28+
y, _ := b.(http.Header)
29+
if equalMaps(x, y) {
2630
return ""
2731
}
2832
return fmt.Sprintf("- %v\n+ %v", a, b)

internal/epoch/nanos.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import (
66
)
77

88
// Nanos is the nanoseconds since the Unix epoch.
9+
//
10+
//nolint:recvcheck
911
type Nanos int64
1012

1113
// NewNanos converts a time.Time to Nanos. If the time is zero, it
@@ -35,9 +37,9 @@ func (u Nanos) ToTime() time.Time {
3537
// Returns true if the value was swapped.
3638
//
3739
//goland:noinspection GoMixedReceiverTypes
38-
func (u *Nanos) SwapIfZero(new Nanos) bool {
40+
func (u *Nanos) SwapIfZero(ns Nanos) bool {
3941
p := (*int64)(u)
40-
return atomic.CompareAndSwapInt64(p, 0, int64(new))
42+
return atomic.CompareAndSwapInt64(p, 0, int64(ns))
4143
}
4244

4345
//goland:noinspection GoMixedReceiverTypes

internal/hextbl/hex.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ func ParseUint64(s string) (uint64, bool) {
4242
var n uint64
4343
invalidMark := byte(0)
4444
for i := 0; i < len(s); i += 4 {
45-
shift := uint((15 - i) * 4)
45+
shift := uint((15 - i) * 4) //nolint:gosec
4646
n |= uint64(Reverse[s[i]]) << shift
4747
n |= uint64(Reverse[s[i+1]]) << (shift - 4)
4848
n |= uint64(Reverse[s[i+2]]) << (shift - 8)

internal/hextbl/hex_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ func TestReadByte(t *testing.T) {
3434
})
3535
}
3636
}
37+
3738
func TestParseUint64(t *testing.T) {
3839
tests := []struct {
3940
name string

internal/hextbl/uint128.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ func ParseUint128(s string) (Uint128, bool) {
2121
var hi, lo uint64
2222
invalidMark := byte(0)
2323
for i := 0; i < 16; i += 4 {
24-
shift := uint((15 - i) * 4)
24+
shift := uint((15 - i) * 4) //nolint:gosec
2525

2626
hi |= uint64(Reverse[s[i]]) << shift
2727
hi |= uint64(Reverse[s[i+1]]) << (shift - 4)

internal/tty/color.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"os"
55
)
66

7+
//nolint:gochecknoglobals
78
var noColor = os.Getenv("NO_COLOR") != "" || os.Getenv("TERM") == "dumb"
89

910
// Foreground colors.
@@ -21,6 +22,7 @@ const (
2122
White Color = 37
2223
)
2324

25+
//nolint:gochecknoglobals
2426
var codes = [8]string{
2527
"\x1b[30m",
2628
"\x1b[31m",

log/log_test.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package log
22

33
import (
44
"bytes"
5-
"context"
65
"log/slog"
76
"regexp"
87
"strings"
@@ -15,7 +14,7 @@ import (
1514
const textTimeRE = `\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}(Z|[+-]\d{2}:\d{2})`
1615

1716
func TestLogTextHandler(t *testing.T) {
18-
ctx := context.Background()
17+
ctx := t.Context()
1918

2019
var buf bytes.Buffer
2120
slog.SetDefault(slog.New(slog.NewTextHandler(&buf, nil)))

log/logdev/buffer.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import "sync"
99
type Buffer []byte
1010

1111
// Having an initial size gives a dramatic speedup.
12+
//
13+
//nolint:gochecknoglobals
1214
var bufPool = sync.Pool{
1315
New: func() any {
1416
b := make([]byte, 0, 1024)
@@ -17,7 +19,8 @@ var bufPool = sync.Pool{
1719
}
1820

1921
func NewBuffer() *Buffer {
20-
return bufPool.Get().(*Buffer)
22+
buf, _ := bufPool.Get().(*Buffer)
23+
return buf
2124
}
2225

2326
func (b *Buffer) Free() {

log/logdev/dev_handler.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,8 @@ func appendValue(buf *Buffer, v slog.Value) {
176176
default:
177177
_, _ = fmt.Fprint(buf, a)
178178
}
179+
case slog.KindGroup, slog.KindLogValuer:
180+
panic(fmt.Sprintf("unsupported kind: %s", v.Kind()))
179181
default:
180182
panic(fmt.Sprintf("bad kind: %s", v.Kind()))
181183
}

log/logdev/dev_handler_test.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package log
22

33
import (
44
"bytes"
5-
"context"
65
"fmt"
76
"log/slog"
87
"os"
@@ -22,11 +21,11 @@ func TestDevHandler_Sample(t *testing.T) {
2221
l.Debug("debug level message", slog.String("attribute3", "value3"))
2322
l.Warn("warning level with multiple attrs", slog.Float64("pi", 3.14), slog.Bool("isTest", true))
2423
l.Error("error level message", slog.Any("details", map[string]any{"key": "value"}))
25-
l.Info("info level message that is really, really, long", slog.String("attribute1", "value1"), slog.Int("attribute2", 42))
24+
l.Info("info level message that is really, long", slog.String("attribute1", "value1"), slog.Int("attribute2", 42))
2625
}
2726

2827
func TestDevHandler_Handle(t *testing.T) {
29-
ctx := context.Background()
28+
ctx := t.Context()
3029
buf := &bytes.Buffer{}
3130
h := &DevHandler{w: buf}
3231

@@ -44,7 +43,7 @@ func TestDevHandler_Handle(t *testing.T) {
4443
}
4544

4645
func BenchmarkDevHandler_Handle(b *testing.B) {
47-
ctx := context.Background()
46+
ctx := b.Context()
4847
buf := &bytes.Buffer{}
4948
h := &DevHandler{w: buf}
5049

trace/id.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,13 @@ func (s SpanID) String() string {
4545
return string(a[:])
4646
}
4747

48-
//goland:noinspection SpellCheckingInspection
48+
func genTraceID() TraceID {
49+
return TraceID{n: hextbl.Uint128{Hi: rand.Uint64(), Lo: rand.Uint64()}} //nolint:gosec
50+
}
4951

50-
func genTraceID() TraceID { return TraceID{n: hextbl.Uint128{Hi: rand.Uint64(), Lo: rand.Uint64()}} }
51-
func genSpanID() SpanID { return SpanID{n: rand.Uint64() & math.MaxInt64} }
52+
func genSpanID() SpanID {
53+
return SpanID{n: rand.Uint64() & math.MaxInt64} //nolint:gosec
54+
}
5255

5356
// ParseTraceID parses a 32-character hex string into a TraceID.
5457
func ParseTraceID(s string) (TraceID, error) {

trace/id_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ func TestTraceID_String(t *testing.T) {
3131
}
3232

3333
t.Run("random", func(t *testing.T) {
34-
for i := 0; i < 1000; i++ {
34+
for range 1000 {
3535
id := genTraceID()
3636
if !id.IsValid() {
3737
continue
@@ -156,7 +156,7 @@ func TestSpanID_String(t *testing.T) {
156156
}
157157

158158
t.Run("random", func(t *testing.T) {
159-
for i := 0; i < 1000; i++ {
159+
for range 1000 {
160160
id := genSpanID()
161161
if !id.IsValid() {
162162
continue

trace/span_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ func TestSpan_End_Race(t *testing.T) {
4242
for range readerCount {
4343
go func() {
4444
defer wg.Done()
45-
for j := 0; j < 5; j++ {
45+
for range 5 {
4646
_ = span.IsRecording()
4747
_ = span.EndTime()
4848
time.Sleep(1 * time.Millisecond) // delay to allow interleaving

trace/state.go

Lines changed: 28 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -68,31 +68,39 @@ func ParseState(ts string) (State, error) {
6868

6969
// Check for dupe keys. We only check duplicated lengths.
7070
if dupeLenBits.hasAny() {
71-
// Note: we're checking for duplicates in the hash of the key, not the key
72-
// itself. We may have false positives, but it should be vanishingly rare.
73-
var hashes = [32]keyHash{}
74-
idx := 0
75-
for pos := range splitMembers(ts) {
76-
if pos.isEmpty() {
77-
continue
78-
}
79-
if !dupeLenBits.hasBit(pos.keyLen()) {
80-
continue
81-
}
82-
hashes[idx] = pos.hashKey(ts)
83-
idx++
84-
}
85-
slices.Sort(hashes[:idx]) // sorting 32 entries is 2x faster than a map
86-
for i := 1; i < idx; i++ {
87-
if hashes[i] == hashes[i-1] {
88-
return State{}, fmt.Errorf("duplicate key")
89-
}
71+
err := checkDupes(ts, dupeLenBits)
72+
if err != nil {
73+
return State{}, err
9074
}
9175
}
9276

9377
return State{s: ts, isClean: isClean}, nil
9478
}
9579

80+
func checkDupes(ts string, dupeLenBits *bitset) error {
81+
// Note: we're checking for duplicates in the hash of the key, not the key
82+
// itself. We may have false positives, but it should be vanishingly rare.
83+
hashes := [32]keyHash{}
84+
idx := 0
85+
for pos := range splitMembers(ts) {
86+
if pos.isEmpty() {
87+
continue
88+
}
89+
if !dupeLenBits.hasBit(pos.keyLen()) {
90+
continue
91+
}
92+
hashes[idx] = pos.hashKey(ts)
93+
idx++
94+
}
95+
slices.Sort(hashes[:idx]) // sorting 32 entries is 2x faster than a map
96+
for i := 1; i < idx; i++ {
97+
if hashes[i] == hashes[i-1] {
98+
return fmt.Errorf("duplicate key")
99+
}
100+
}
101+
return nil
102+
}
103+
96104
// Members iterate over each key-value list-member in the tracestate string.
97105
func (st State) Members() iter.Seq2[string, string] {
98106
return func(yield func(string, string) bool) {
@@ -153,7 +161,7 @@ type memberPos struct {
153161
func (p memberPos) isEmpty() bool { return p == memberPos{} }
154162
func (p memberPos) isValid() bool { return p.keyLo < p.keyHi && p.valLo < p.valHi }
155163
func (p memberPos) startsAt(offs int) bool { return p.keyLo == offs }
156-
func (p memberPos) keyLen() uint64 { return uint64(p.keyHi - p.keyLo) }
164+
func (p memberPos) keyLen() uint64 { return uint64(p.keyHi - p.keyLo) } //nolint:gosec
157165
func (p memberPos) last() int { return p.valHi }
158166
func (p memberPos) memberString(ts string) string { return ts[p.keyLo:p.valHi] }
159167
func (p memberPos) keyString(ts string) string { return ts[p.keyLo:p.keyHi] }
@@ -269,7 +277,6 @@ func checkKeyRest(ts string, lo, hi int) bool {
269277
}
270278
}
271279
return true
272-
273280
}
274281

275282
func checkVal(ts string, pos memberPos) bool {

trace/state_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,7 @@ func TestParseState(t *testing.T) {
276276

277277
func genTracestateSequence(tmpl string, n int) string {
278278
seq := make([]string, n)
279-
for i := 0; i < n; i++ {
279+
for i := range n {
280280
seq[i] = strings.ReplaceAll(tmpl, "%d", strconv.Itoa(i))
281281
}
282282
return strings.Join(seq, ",")

0 commit comments

Comments
 (0)