Skip to content

Commit

Permalink
better diagnostics, add basic mode; add errors.Is
Browse files Browse the repository at this point in the history
Signed-off-by: Oliver Eikemeier <[email protected]>
  • Loading branch information
eikemeier committed Jul 15, 2024
1 parent d8515ad commit 5d0367b
Show file tree
Hide file tree
Showing 19 changed files with 585 additions and 119 deletions.
1 change: 0 additions & 1 deletion .envrc

This file was deleted.

2 changes: 1 addition & 1 deletion .github/codecov.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ coverage:
project: false
patch: false
ignore:
- atomic/nocopy.go
- main.go
76 changes: 38 additions & 38 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -1,42 +1,42 @@
---
name: Test
"on":
push:
branches:
- main
pull_request:
branches:
- main
push:
branches:
- main
pull_request:
branches:
- main
jobs:
test:
name: Test on Go ${{ matrix.go }}
permissions:
checks: write
contents: read
pull-requests: read
statuses: write
runs-on: ubuntu-24.04
strategy:
matrix:
go: ["1.22", "1.21"]
env:
GOTOOLCHAIN: local
steps:
- name: ✔ Check out
uses: actions/checkout@v4
- name: 🐹 Set up Go ${{ matrix.go }}
uses: actions/setup-go@v5
with:
go-version: ${{ matrix.go }}
check-latest: true
- name: 🧸 golangci-lint
uses: golangci/golangci-lint-action@v6
with:
version: v1.59.1
- name: 🔨 Test
run: go test -coverprofile=cover.out ./...
- name: 🧑🏻‍💻 codecov
uses: codecov/codecov-action@v4
with:
files: ./cover.out
token: ${{ secrets.CODECOV_TOKEN }}
test:
name: Test on Go ${{ matrix.go }}
permissions:
checks: write
contents: read
pull-requests: read
statuses: write
runs-on: ubuntu-24.04
strategy:
matrix:
go: ["1.22", "1.21"]
env:
GOTOOLCHAIN: local
steps:
- name: ✔ Check out
uses: actions/checkout@v4
- name: 🐹 Set up Go ${{ matrix.go }}
uses: actions/setup-go@v5
with:
go-version: ${{ matrix.go }}
check-latest: true
- name: 🧸 golangci-lint
uses: golangci/golangci-lint-action@v6
with:
version: v1.59.1
- name: 🔨 Test
run: go test -coverprofile=cover.out ./...
- name: 🧑🏻‍💻 codecov
uses: codecov/codecov-action@v4
with:
files: ./cover.out
token: ${{ secrets.CODECOV_TOKEN }}
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ Usage: `zerolint [-flag] [package]`
Flags:

- **-c** int display offending line with this many lines of context (default -1)
- **-excluded** `<filename>` read exluded types from this file
- **-basic** basic analysis only
- **-excluded** `<filename>` read excluded types from this file
- **-zerotrace** trace found zero-sized types
- **-fix** apply all suggested fixes
59 changes: 59 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,72 @@
//
// SPDX-License-Identifier: Apache-2.0

// This is the main program for the zerolint linter.
package main

import (
"flag"
"fmt"
"os"
"runtime/debug"

"fillmore-labs.com/zerolint/pkg/analyzer"
"golang.org/x/tools/go/analysis/singlechecker"
)

func main() {
a := analyzer.Analyzer
addVersionFlag(&a.Flags)
singlechecker.Main(analyzer.Analyzer)
}

func addVersionFlag(s *flag.FlagSet) {
if s.Lookup("V") == nil {
s.Var(versionFlag{}, "V", "print version and exit")
}
}

type versionFlag struct{}

func (versionFlag) IsBoolFlag() bool { return true }
func (versionFlag) Get() any { return nil }
func (versionFlag) String() string { return "" }
func (versionFlag) Set(_ string) error {
progname, err := os.Executable()
if err != nil {
return err
}

var goVersion, version, revision, time string
if bi, ok := debug.ReadBuildInfo(); ok {
goVersion = bi.GoVersion
version = bi.Main.Version
var modified string
for _, s := range bi.Settings {
switch s.Key {
case "vcs.revision":
revision = s.Value

case "vcs.time":
time = s.Value

case "vcs.modified":
modified = s.Value
}
}

if len(revision) > 6 { //nolint:mnd
revision = revision[:7]
if len(modified) > 0 {
revision += " (dirty)"
}
}
}

fmt.Printf("%s version %s build with %s from %s on %s\n",
progname, version, goVersion, revision, time)

os.Exit(0)

return nil
}
13 changes: 11 additions & 2 deletions pkg/analyzer/analyzer.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,17 +40,26 @@ var Analyzer = &analysis.Analyzer{ //nolint:gochecknoglobals
func init() { //nolint:gochecknoinits
Analyzer.Flags.StringVar(&Excludes, "excluded", "", "read excluded types from this file")
Analyzer.Flags.BoolVar(&ZeroTrace, "zerotrace", false, "trace found zero-sized types")
Analyzer.Flags.BoolVar(&Basic, "basic", false, "basic analysis only")
}

var ZeroTrace bool //nolint:gochecknoglobals
var (
ZeroTrace bool //nolint:gochecknoglobals
Basic bool //nolint:gochecknoglobals
)

func run(pass *analysis.Pass) (any, error) {
excludes, err := ReadExcludes()
if err != nil {
return nil, err
}

v := visitor.Visitor{Pass: pass, Excludes: excludes, ZeroTrace: ZeroTrace}
v := visitor.Visitor{
Pass: pass,
Excludes: excludes,
ZeroTrace: ZeroTrace,
Basic: Basic,
}
v.Run()

return any(nil), nil
Expand Down
5 changes: 5 additions & 0 deletions pkg/analyzer/analyzer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,10 @@ func TestAnalyzer(t *testing.T) { //nolint:paralleltest
dir := analysistest.TestData()
a := analyzer.Analyzer

analyzer.Basic = true
analysistest.Run(t, dir, a, "basic")

analyzer.Basic = false
analyzer.Excludes = dir + "/excluded.txt"
analysistest.RunWithSuggestedFixes(t, dir, a, "a")
}
2 changes: 2 additions & 0 deletions pkg/analyzer/testdata/excluded.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# zerolint exclusions for a
a.C
80 changes: 79 additions & 1 deletion pkg/analyzer/testdata/src/a/testdata.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,59 @@
package a

import (
"errors"
"fmt"
)

type empty struct{}

type typedError[T any] struct {
_ [0]T
}

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

var (
_ error = &typedError[any]{} // want "address of zero-size variable"
ErrOne = &(typedError[int]{}) // want "address of zero-size variable"
ErrTwo = (new)(typedError[float64]) // want "new called on zero-size type"
)

type myErrors struct{}

func (myErrors) Is(err, target error) bool {
return false
}

var myErrs = myErrors{}

func Exported() {
var x [0]string
var y [0]string

if errors.Is(nil, ErrOne) {
fmt.Println("nil")
}

if myErrs.Is(ErrOne, ErrTwo) {
fmt.Println("nil")
}

if errors.Is(func() error { // want "comparison of pointer to zero-size variable"
return ErrOne
}(), ErrTwo) {
fmt.Println("equal")
}

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

_ = (new)(struct{}) // want "new called on zero-size type"

_ = new(empty) // want "new called on zero-size type"

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

_ = *xp // want "pointer to zero-size variable"
Expand All @@ -36,6 +82,10 @@ func Exported() {
fmt.Println("not equal")
}

if xp == nil {
fmt.Println("nil")
}

_, _ = any(xp).((*[0]string)) // want "pointer to zero-size type"

switch any(xp).(type) {
Expand All @@ -44,6 +94,16 @@ func Exported() {
}
}

func Undiagnosed() {
i := 1 + 1
i = *&i

f := func(int) {}
f(i)

_ = !false
}

type A [0]string

type B = A
Expand All @@ -62,7 +122,9 @@ func (g *greeter) String() string { // want "pointer to zero-size type"

var _ fmt.Stringer = &greeter{} // want "address of zero-size variable"

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

var _ fmt.Stringer = new(greeter) // want "new called on zero-size type"

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

Expand All @@ -71,3 +133,19 @@ func (g *greeter2[T]) String() string { // want "pointer to zero-size type"
}

var _ fmt.Stringer = &greeter2[int]{} // want "address of zero-size variable"

type C struct{}

func (*C) String() string {
return "hello, world"
}

var _ fmt.Stringer = (*C)(nil)

type D struct{ _ int }

func (*D) String() string {
return "hello, world"
}

var _ fmt.Stringer = (*D)(nil)
Loading

0 comments on commit 5d0367b

Please sign in to comment.