Skip to content

Commit 79f9b63

Browse files
committed
major refactoring; expanded diagnostics; add pre-commit hook
Signed-off-by: Oliver Eikemeier <[email protected]>
1 parent ddf0285 commit 79f9b63

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

97 files changed

+4249
-808
lines changed

.github/workflows/test.yml

+7-7
Original file line numberDiff line numberDiff line change
@@ -15,37 +15,37 @@ jobs:
1515
contents: read
1616
pull-requests: read
1717
statuses: write
18-
runs-on: ubuntu-24.04
18+
runs-on: ubuntu-24.04-arm
1919
strategy:
2020
matrix:
2121
go: ["1.24", "1.23"]
2222
include:
2323
- go: "1.24"
24-
go-version: "1.24.2"
24+
go-version: "1.24.3"
2525
update-coverage: true
2626
- go: "1.23"
27-
go-version: "1.23.8"
27+
go-version: "1.23.9"
2828
env:
2929
GOTOOLCHAIN: local
3030
steps:
3131
- name: ✔ Check out
3232
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
3333
- name: 🐹 Set up Go ${{ matrix.go-version }}
34-
uses: actions/setup-go@0aaccfd150d50ccaeb58ebd88d36e91967a5f35b # v5.4.0
34+
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
3535
with:
3636
go-version: ${{ matrix.go-version }}
3737
- name: 🧸 golangci-lint
38-
uses: golangci/golangci-lint-action@1481404843c368bc19ca9406f87d6e0fc97bdcfd # v7.0.0
38+
uses: golangci/golangci-lint-action@4afd733a84b1f43292c63897423277bb7f4313a9 # v8.0.0
3939
with:
40-
version: v2.0.2
40+
version: v2.1.6
4141
- name: 🔨 Test
4242
run: |
4343
(cd ./pkg/internal/visitor/testdata && go mod download)
4444
go test -coverprofile=cover.out ./...
4545
env:
4646
GOEXPERIMENT: aliastypeparams
4747
- name: 🧑🏻‍💻 codecov
48-
uses: codecov/codecov-action@0565863a31f2c772f9f0395002a31e3f06189574 # v5.4.0
48+
uses: codecov/codecov-action@ad3126e916f78f00edff4ed0317cf185271ccc2d # v5.4.2
4949
if: ${{ matrix.update-coverage }}
5050
with:
5151
files: ./cover.out

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
!/.golangci.yaml
1010
!/.markdownlint.yaml
1111
!/.mockery.yaml
12+
!/.pre-commit-config.yaml
1213
!/.prettierrc.yaml
1314
!/.yamlfmt
1415
!/.yamllint

.golangci.yaml

+14-13
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ linters:
2121
- nonamedreturns
2222
- varnamelen
2323
- wrapcheck
24-
- wsl
2524
settings:
2625
errcheck:
2726
exclude-functions:
@@ -55,27 +54,29 @@ linters:
5554
- name: dot-imports
5655
exclude: ["TEST"]
5756
- name: early-return
57+
arguments:
58+
- allow-jump: true
59+
- name: empty-block
60+
- name: error-naming
5861
- name: error-return
5962
- name: error-strings
60-
- name: error-naming
63+
- name: errorf
6164
- name: exported
65+
- name: file-header
66+
arguments:
67+
- "Copyright 2024 Oliver Eikemeier. All Rights Reserved."
6268
- name: increment-decrement
63-
- name: var-naming
64-
- name: var-declaration
69+
- name: indent-error-flow
6570
- name: range
6671
- name: receiver-naming
72+
- name: redefines-builtin-id
73+
- name: superfluous-else
6774
- name: time-naming
6875
- name: unexported-return
69-
- name: indent-error-flow
70-
- name: errorf
71-
- name: empty-block
72-
- name: superfluous-else
73-
- name: unused-parameter
7476
- name: unreachable-code
75-
- name: redefines-builtin-id
76-
- name: file-header
77-
arguments:
78-
- "Copyright 2024 Oliver Eikemeier. All Rights Reserved."
77+
- name: unused-parameter
78+
- name: var-declaration
79+
- name: var-naming
7980
exclusions:
8081
rules:
8182
- path: _test\.go$

.markdownlint.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,5 @@ no-hard-tabs:
44
- go
55
spaces_per_tab: 4
66
line-length:
7+
code_blocks: false
78
line_length: 120

.pre-commit-config.yaml

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
repos:
3+
- repo: https://github.com/google/yamlfmt
4+
rev: v0.16.0
5+
hooks:
6+
- id: yamlfmt
7+
- repo: https://github.com/google/keep-sorted
8+
rev: v0.6.1
9+
hooks:
10+
- id: keep-sorted

README.md

+84-6
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,32 @@
22

33
[![Go Reference](https://pkg.go.dev/badge/fillmore-labs.com/zerolint.svg)](https://pkg.go.dev/fillmore-labs.com/zerolint)
44
[![Test](https://github.com/fillmore-labs/zerolint/actions/workflows/test.yml/badge.svg?branch=main)](https://github.com/fillmore-labs/zerolint/actions/workflows/test.yml)
5+
[![CodeQL](https://github.com/fillmore-labs/zerolint/actions/workflows/github-code-scanning/codeql/badge.svg?branch=main)](https://github.com/fillmore-labs/zerolint/actions/workflows/github-code-scanning/codeql)
56
[![Coverage](https://codecov.io/gh/fillmore-labs/zerolint/branch/main/graph/badge.svg?token=TUE1BL1QZV)](https://codecov.io/gh/fillmore-labs/zerolint)
67
[![Maintainability](https://api.codeclimate.com/v1/badges/baf50ad423cc30ff7790/maintainability)](https://codeclimate.com/github/fillmore-labs/zerolint/maintainability)
78
[![Go Report Card](https://goreportcard.com/badge/fillmore-labs.com/zerolint)](https://goreportcard.com/report/fillmore-labs.com/zerolint)
89
[![License](https://img.shields.io/github/license/fillmore-labs/zerolint)](https://www.apache.org/licenses/LICENSE-2.0)
910

10-
The `zerolint` linter checks usage patterns of pointers to zero-size variables in Go.
11+
`zerolint` is a Go static analysis tool (linter) that detects potentially wrong or unnecessary usage of pointers to
12+
zero-sized types.
13+
14+
## Motivation
15+
16+
Go's zero-sized types, like `struct{}` or `[0]byte`, occupy no memory. While useful in certain contexts (e.g.,
17+
signaling on channels, map keys/values for set semantics), taking the address of a zero-sized variable (`&struct{}{}`)
18+
or allocating them (`new(struct{})`) is often redundant.
19+
20+
All values of a zero-sized type are indistinguishable, so pointers to them generally don't convey unique state or
21+
identity. Using pointers to zero-sized types can obscure intent, as readers might mistakenly assume the pointer implies
22+
state or identity management. Also, since pointers are not zero-sized, it introduces minor performance overhead and
23+
waste of memory.
24+
25+
`zerolint` helps identify these patterns, promoting cleaner and potentially more efficient code.
1126

1227
## Usage
1328

1429
```console
30+
# Install the linter
1531
go install fillmore-labs.com/zerolint@latest
1632
```
1733

@@ -20,8 +36,70 @@ Usage: `zerolint [-flag] [package]`
2036
Flags:
2137

2238
- **-c** int display offending line with this many lines of context (default -1)
23-
- **-full** full analysis
24-
- **-excluded** `<filename>` read excluded types from this file
25-
- **-zerotrace** trace found zero-sized types
26-
- **-fix** apply all suggested fixes
27-
- **-generated** analyze generated files and types defined in generated files
39+
- **-level** Perform a more comprehensive analysis (Default, Extended, Full)
40+
- **-match** Regex limiting zero-sized type detection only to matching types. Useful with `-fix`.
41+
- **-excluded** `<filename>` Read types to be excluded from analysis from the specified file. The file should contain
42+
fully qualified type names, one per line. See Excluding Types.
43+
- **-generated** Analyze files that contain code generation markers (e.g., `// Code generated ... DO NOT EDIT.`). By
44+
default, these files are skipped.
45+
- **-zerotrace** Enable verbose logging which types zerolint identifies as zero-sized. Useful for building a list of
46+
excluded types.
47+
- **-fix** Apply all suggested fixes automatically. Use with caution and always review the changes made by `-fix`.
48+
49+
## Examples
50+
51+
Consider the following Go code:
52+
53+
```go
54+
// main.go
55+
package main
56+
57+
type myError struct{}
58+
59+
func (*myError) Error() string {
60+
return "my error"
61+
}
62+
63+
func processing() error {
64+
return &myError{}
65+
}
66+
67+
func main() {
68+
if err := processing(); err != nil {
69+
panic(err)
70+
}
71+
}
72+
```
73+
74+
Running `zerolint % zerolint -level 2 ./...` would produce output similar to:
75+
76+
```text
77+
/path/to/your/project/main.go:6:7: error interface implemented on pointer to zero-sized type "example.com/project.myError" (zl:err)
78+
/path/to/your/project/main.go:11:9: address of zero-size variable of type "example.com/project.myError" (zl:add)
79+
```
80+
81+
## Excluding Types
82+
83+
If you have specific zero-sized types where pointer usage is intentional or required (e.g., due to external library
84+
constraints), you can exclude them using the `-excluded` flag with a file path.
85+
86+
Example `excludes.txt`:
87+
88+
```text
89+
# zerolint excludes for my project
90+
example.com/some/pkg.RequiredPointerType
91+
vendor.org/library.MarkerInterfaceImplementation
92+
```
93+
94+
Then run: `zerolint -excluded=excludes.txt ./...`
95+
96+
## Integration
97+
98+
See `zerolint-golangci-plugin`
99+
100+
## Known Bugs
101+
102+
We are aware of a number of minor bugs in the analyzer's fixes. For example, it may sometimes cause a type not
103+
implement an interface or check a non-pointer type for nil. The known bugs are low-risk and easy to fix, as they result
104+
in a broken build or are obvious during a code review; none cause latent behavior changes. Please report any additional
105+
problems you encounter.

go.mod

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

33
go 1.23.0
44

5-
toolchain go1.24.2
5+
toolchain go1.24.3
66

7-
require golang.org/x/tools v0.31.0
7+
require golang.org/x/tools v0.33.0
88

99
require (
1010
golang.org/x/mod v0.24.0 // indirect
11-
golang.org/x/sync v0.13.0 // indirect
11+
golang.org/x/sync v0.14.0 // indirect
1212
)
13+
14+
// add when go1.23 is dropped
15+
// tool golang.org/x/tools/cmd/stringer

go.sum

+4-4
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
22
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
33
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
44
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
5-
golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
6-
golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
7-
golang.org/x/tools v0.31.0 h1:0EedkvKDbh+qistFTd0Bcwe/YLh4vHwWEkiI0toFIBU=
8-
golang.org/x/tools v0.31.0/go.mod h1:naFTU+Cev749tSJRXJlna0T3WxKvb1kWEx15xA4SdmQ=
5+
golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
6+
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
7+
golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc=
8+
golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI=

main.go

+3-35
Original file line numberDiff line numberDiff line change
@@ -14,47 +14,15 @@
1414
//
1515
// SPDX-License-Identifier: Apache-2.0
1616

17-
// This is the main program for the zerolint linter.
1817
package main
1918

2019
import (
21-
"flag"
22-
"fmt"
23-
"os"
24-
"runtime/debug"
25-
26-
"fillmore-labs.com/zerolint/pkg/analyzer"
20+
"fillmore-labs.com/zerolint/pkg/zerolint"
2721
"golang.org/x/tools/go/analysis/singlechecker"
2822
)
2923

3024
func main() {
31-
a := analyzer.Analyzer
32-
addVersionFlag(&a.Flags)
33-
singlechecker.Main(analyzer.Analyzer)
34-
}
35-
36-
func addVersionFlag(s *flag.FlagSet) {
37-
if s.Lookup("V") == nil {
38-
s.Var(versionFlag{}, "V", "print version and exit")
39-
}
40-
}
41-
42-
type versionFlag struct{}
43-
44-
func (versionFlag) IsBoolFlag() bool { return true }
45-
func (versionFlag) Get() any { return nil }
46-
func (versionFlag) String() string { return "" }
47-
func (versionFlag) Set(_ string) error {
48-
const progname = "zerolint"
49-
50-
if bi, ok := debug.ReadBuildInfo(); ok {
51-
fmt.Printf("%s version %s build with %s\n",
52-
progname, bi.Main.Version, bi.GoVersion)
53-
} else {
54-
fmt.Printf("%s version (unknown)\n", progname)
55-
}
56-
57-
os.Exit(0)
25+
a := zerolint.New(zerolint.WithFlags(true))
5826

59-
return nil
27+
singlechecker.Main(a)
6028
}

pkg/analyzer/analyzer.go

-79
This file was deleted.

0 commit comments

Comments
 (0)