Skip to content

Commit 3a9780e

Browse files
committed
Upgrade dependency to Go 1.22; add check for errors.As and basic check for ZST pointer receivers
Signed-off-by: Oliver Eikemeier <[email protected]>
1 parent 0ab2d38 commit 3a9780e

25 files changed

+491
-253
lines changed

.codeclimate.yml

+2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ checks:
55
enabled: false
66
identical-code:
77
enabled: false
8+
return-statements:
9+
enabled: false
810
exclude_patterns:
911
- "**/.*"
1012
- "**/*_test.go"

.github/workflows/test.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ jobs:
1818
runs-on: ubuntu-24.04
1919
strategy:
2020
matrix:
21-
go: ["1.23", "1.22", "1.21"]
21+
go: ["1.23", "1.22"]
2222
env:
2323
GOTOOLCHAIN: local
2424
steps:
@@ -32,7 +32,7 @@ jobs:
3232
- name: 🧸 golangci-lint
3333
uses: golangci/golangci-lint-action@v6
3434
with:
35-
version: v1.60.1
35+
version: v1.60.3
3636
- name: 🔨 Test
3737
run: |
3838
go get -C ./pkg/analyzer/testdata golang.org/x/exp/errors

.golangci.yaml

+1-3
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ linters:
66
disable:
77
# deprecated
88
- execinquery
9+
- exportloopref
910
- gomnd
1011
# disabled
1112
- depguard
@@ -16,9 +17,6 @@ linters:
1617
- varnamelen
1718
- wrapcheck
1819
- wsl
19-
# Go 1.22
20-
- copyloopvar
21-
- intrange
2220
linters-settings:
2321
govet:
2422
enable-all: true

.prettierrc.yaml

+6
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,9 @@ overrides:
77
- files: "*.md"
88
options:
99
tabWidth: 2
10+
- files: "*.yaml"
11+
options:
12+
tabWidth: 2
13+
- files: "*.yml"
14+
options:
15+
tabWidth: 2

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
[![Go Report Card](https://goreportcard.com/badge/fillmore-labs.com/zerolint)](https://goreportcard.com/report/fillmore-labs.com/zerolint)
88
[![License](https://img.shields.io/github/license/fillmore-labs/zerolint)](https://www.apache.org/licenses/LICENSE-2.0)
99

10-
The `zerolint` linter checks usage patterns of pointers to zero-sized variables in Go.
10+
The `zerolint` linter checks usage patterns of pointers to zero-size variables in Go.
1111

1212
## Usage
1313

go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module fillmore-labs.com/zerolint
22

3-
go 1.21
3+
go 1.22
44

55
toolchain go1.23.0
66

pkg/analyzer/analyzer.go

+16-6
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ const (
2727
Doc = `checks for usage of pointers to zero-length variables
2828
2929
Pointer to zero-length variables carry very little information and
30-
can be avoided in most cases.`
30+
can often be avoided.`
3131
)
3232

3333
var Analyzer = &analysis.Analyzer{ //nolint:gochecknoglobals
@@ -44,19 +44,29 @@ func init() { //nolint:gochecknoinits
4444
}
4545

4646
var (
47+
// Excludes is a list of types to exclude from the analysis.
48+
Excludes string //nolint:gochecknoglobals
49+
50+
// ZeroTrace enables tracing of found zero-sized types.
4751
ZeroTrace bool //nolint:gochecknoglobals
48-
Basic bool //nolint:gochecknoglobals
52+
53+
// Basic enables basic analysis only.
54+
Basic bool //nolint:gochecknoglobals
4955
)
5056

57+
// Run applies the analyzer to a package.
5158
func run(pass *analysis.Pass) (any, error) {
52-
excludes, err := ReadExcludes()
59+
// Read the list of excluded types from the file specified by the "excluded" flag.
60+
excludes, err := ReadExcludes(Excludes)
5361
if err != nil {
5462
return nil, err
5563
}
5664

57-
v := visitor.Visitor{
58-
Pass: pass,
59-
Excludes: excludes,
65+
v := visitor.Run{
66+
Visitor: visitor.Visitor{
67+
Pass: pass,
68+
Excludes: excludes,
69+
},
6070
ZeroTrace: ZeroTrace,
6171
Basic: Basic,
6272
}

pkg/analyzer/excludes.go

+7-6
Original file line numberDiff line numberDiff line change
@@ -19,18 +19,19 @@ package analyzer
1919
import (
2020
"bufio"
2121
"os"
22-
)
2322

24-
var Excludes string //nolint:gochecknoglobals
23+
"fillmore-labs.com/zerolint/pkg/set"
24+
)
2525

26-
func ReadExcludes() (map[string]struct{}, error) {
27-
excludes := make(map[string]struct{})
26+
// ReadExcludes reads zero-sized types excluded from analysis from a file and returns them as a set.
27+
func ReadExcludes(name string) (set.Set[string], error) {
28+
excludes := set.New[string]()
2829

2930
if Excludes == "" {
3031
return excludes, nil
3132
}
3233

33-
file, err := os.Open(Excludes)
34+
file, err := os.Open(name)
3435
if err != nil {
3536
return nil, err
3637
}
@@ -42,7 +43,7 @@ func ReadExcludes() (map[string]struct{}, error) {
4243
if len(expr) == 0 || expr[0] == '#' {
4344
continue
4445
}
45-
excludes[string(expr)] = struct{}{}
46+
excludes.Insert(string(expr))
4647
}
4748
if err2 := scanner.Err(); err2 != nil {
4849
return nil, err2

pkg/analyzer/testdata/a/testdata.go

+16-15
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,14 @@ type typedError[T any] struct {
2929
_ [0]T
3030
}
3131

32-
func (*typedError[_]) Error() string { // want "pointer to zero-size type"
32+
func (*typedError[_]) Error() string { // want "pointer to zero-sized type"
3333
return "an error"
3434
}
3535

3636
var (
3737
_ error = &typedError[any]{} // want "address of zero-size variable"
3838
ErrOne = &(typedError[int]{}) // want "address of zero-size variable"
39-
ErrTwo = (new)(typedError[float64]) // want "new called on zero-size type"
39+
ErrTwo = (new)(typedError[float64]) // want "new called on zero-sized type"
4040
)
4141

4242
type myErrors struct{}
@@ -55,6 +55,11 @@ func Exported() {
5555
fmt.Println("nil")
5656
}
5757

58+
var oneErr *typedError[int] // want "pointer to zero-sized type"
59+
if errors.As(ErrOne, &oneErr) {
60+
fmt.Println("ErrOne is typedError[int]")
61+
}
62+
5863
func() {
5964
errors := myErrs
6065
if errors.Is(ErrOne, ErrTwo) {
@@ -68,12 +73,12 @@ func Exported() {
6873
fmt.Println("equal")
6974
}
7075

71-
var err *typedError[int] // want "pointer to zero-size type"
76+
var err *typedError[int] // want "pointer to zero-sized type"
7277
_ = errors.As(ErrOne, &err)
7378

74-
_ = (new)(struct{}) // want "new called on zero-size type"
79+
_ = (new)(struct{}) // want "new called on zero-sized type"
7580

76-
_ = new(empty) // want "new called on zero-size type"
81+
_ = new(empty) // want "new called on zero-sized type"
7782

7883
xp, yp := &x, &y // want "address of zero-size variable" "address of zero-size variable"
7984

@@ -87,14 +92,10 @@ func Exported() {
8792
fmt.Println("not equal")
8893
}
8994

90-
if xp == nil {
91-
fmt.Println("nil")
92-
}
93-
94-
_, _ = any(xp).((*[0]string)) // want "pointer to zero-size type"
95+
_, _ = any(xp).((*[0]string)) // want "pointer to zero-sized type"
9596

9697
switch any(xp).(type) {
97-
case (*[0]string): // want "pointer to zero-size type"
98+
case (*[0]string): // want "pointer to zero-sized type"
9899
case string:
99100
}
100101
}
@@ -113,15 +114,15 @@ type A [0]string
113114

114115
type B = A
115116

116-
func (*B) Combine(_ *B) *B { // want "pointer to zero-size type" "pointer to zero-size type" "pointer to zero-size type"
117+
func (*B) Combine(_ *B) *B { // want "pointer to zero-sized type" "pointer to zero-sized type" "pointer to zero-sized type"
117118
return &B{} // want "address of zero-size variable"
118119
}
119120

120121
func Ptr[T any](v T) *T { return &v }
121122

122123
type greeter [5][5]struct{}
123124

124-
type greeterAlias = *greeter // want "pointer to zero-size type"
125+
type greeterAlias = *greeter // want "pointer to zero-sized type"
125126

126127
func (g greeterAlias) String() string {
127128
return "hello, world"
@@ -131,11 +132,11 @@ var _ fmt.Stringer = &greeter{} // want "address of zero-size variable"
131132

132133
var _ fmt.Stringer = (*greeter)(nil) // want "cast of nil to pointer to zero-size variable"
133134

134-
var _ fmt.Stringer = new(greeter) // want "new called on zero-size type"
135+
var _ fmt.Stringer = new(greeter) // want "new called on zero-sized type"
135136

136137
type greeter2[T any] [5][5][0]T
137138

138-
func (g *greeter2[T]) String() string { // want "pointer to zero-size type"
139+
func (g *greeter2[T]) String() string { // want "pointer to zero-sized type"
139140
return "hello, world"
140141
}
141142

pkg/analyzer/testdata/a/testdata.go.golden

+16-15
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,14 @@ type typedError[T any] struct {
2929
_ [0]T
3030
}
3131

32-
func (typedError[_]) Error() string { // want "pointer to zero-size type"
32+
func (typedError[_]) Error() string { // want "pointer to zero-sized type"
3333
return "an error"
3434
}
3535

3636
var (
3737
_ error = typedError[any]{} // want "address of zero-size variable"
3838
ErrOne = (typedError[int]{}) // want "address of zero-size variable"
39-
ErrTwo = typedError[float64]{} // want "new called on zero-size type"
39+
ErrTwo = typedError[float64]{} // want "new called on zero-sized type"
4040
)
4141

4242
type myErrors struct{}
@@ -55,6 +55,11 @@ func Exported() {
5555
fmt.Println("nil")
5656
}
5757

58+
var oneErr typedError[int] // want "pointer to zero-sized type"
59+
if errors.As(ErrOne, &oneErr) {
60+
fmt.Println("ErrOne is typedError[int]")
61+
}
62+
5863
func() {
5964
errors := myErrs
6065
if errors.Is(ErrOne, ErrTwo) {
@@ -68,12 +73,12 @@ func Exported() {
6873
fmt.Println("equal")
6974
}
7075

71-
var err typedError[int] // want "pointer to zero-size type"
76+
var err typedError[int] // want "pointer to zero-sized type"
7277
_ = errors.As(ErrOne, &err)
7378

74-
_ = struct{}{} // want "new called on zero-size type"
79+
_ = struct{}{} // want "new called on zero-sized type"
7580

76-
_ = empty{} // want "new called on zero-size type"
81+
_ = empty{} // want "new called on zero-sized type"
7782

7883
xp, yp := x, y // want "address of zero-size variable" "address of zero-size variable"
7984

@@ -87,14 +92,10 @@ func Exported() {
8792
fmt.Println("not equal")
8893
}
8994

90-
if xp == nil {
91-
fmt.Println("nil")
92-
}
93-
94-
_, _ = any(xp).(([0]string)) // want "pointer to zero-size type"
95+
_, _ = any(xp).(([0]string)) // want "pointer to zero-sized type"
9596

9697
switch any(xp).(type) {
97-
case ([0]string): // want "pointer to zero-size type"
98+
case ([0]string): // want "pointer to zero-sized type"
9899
case string:
99100
}
100101
}
@@ -113,15 +114,15 @@ type A [0]string
113114

114115
type B = A
115116

116-
func (B) Combine(_ B) B { // want "pointer to zero-size type" "pointer to zero-size type" "pointer to zero-size type"
117+
func (B) Combine(_ B) B { // want "pointer to zero-sized type" "pointer to zero-sized type" "pointer to zero-sized type"
117118
return B{} // want "address of zero-size variable"
118119
}
119120

120121
func Ptr[T any](v T) *T { return &v }
121122

122123
type greeter [5][5]struct{}
123124

124-
type greeterAlias = greeter // want "pointer to zero-size type"
125+
type greeterAlias = greeter // want "pointer to zero-sized type"
125126

126127
func (g greeterAlias) String() string {
127128
return "hello, world"
@@ -131,11 +132,11 @@ var _ fmt.Stringer = greeter{} // want "address of zero-size variable"
131132

132133
var _ fmt.Stringer = greeter{} // want "cast of nil to pointer to zero-size variable"
133134

134-
var _ fmt.Stringer = greeter{} // want "new called on zero-size type"
135+
var _ fmt.Stringer = greeter{} // want "new called on zero-sized type"
135136

136137
type greeter2[T any] [5][5][0]T
137138

138-
func (g greeter2[T]) String() string { // want "pointer to zero-size type"
139+
func (g greeter2[T]) String() string { // want "pointer to zero-sized type"
139140
return "hello, world"
140141
}
141142

pkg/analyzer/testdata/basic/testdata.go

+13-5
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,15 @@ import (
2121
"fmt"
2222
)
2323

24-
type nyError struct{}
24+
type myError struct{}
2525

26-
func (*nyError) Error() string {
26+
func (*myError) Error() string { // want "method receiver is pointer to zero-size variable"
2727
return "my error"
2828
}
2929

3030
var (
31-
ErrOne = &nyError{}
32-
ErrTwo = new(nyError)
31+
ErrOne = &myError{}
32+
ErrTwo = new(myError)
3333
)
3434

3535
func Exported() {
@@ -43,7 +43,7 @@ func Exported() {
4343
fmt.Println("equal")
4444
}
4545

46-
var err *nyError
46+
var err *myError
4747
_ = errors.As(ErrOne, &err)
4848

4949
if ErrOne == ErrTwo { // want "comparison of pointers to zero-size variables"
@@ -54,3 +54,11 @@ func Exported() {
5454
fmt.Println("not equal")
5555
}
5656
}
57+
58+
type D struct{ _ int }
59+
60+
func (*D) String() string {
61+
return "hello, world"
62+
}
63+
64+
var _ fmt.Stringer = (*D)(nil)

pkg/analyzer/testdata/go.mod

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
module go.test
22

3-
go 1.21
3+
go 1.22
44

5-
require golang.org/x/exp/errors v0.0.0-20240719175910-8a7402abbf56
5+
require golang.org/x/exp/errors v0.0.0-20240823005443-9b4947da3948

pkg/analyzer/testdata/go.sum

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
golang.org/x/exp/errors v0.0.0-20240719175910-8a7402abbf56 h1:wWtjcEP22Momq1fphl8WkZCnFX615H/yPDX494o7PtU=
2-
golang.org/x/exp/errors v0.0.0-20240719175910-8a7402abbf56/go.mod h1:YgqsNsAu4fTvlab/7uiYK9LJrCIzKg/NiZUIH1/ayqo=
1+
golang.org/x/exp/errors v0.0.0-20240823005443-9b4947da3948 h1:IE9ZGXGK0A3EhE/vajvqwjYT5JsrZfxtVwBflW/O2vU=
2+
golang.org/x/exp/errors v0.0.0-20240823005443-9b4947da3948/go.mod h1:YgqsNsAu4fTvlab/7uiYK9LJrCIzKg/NiZUIH1/ayqo=

0 commit comments

Comments
 (0)