Add makezero linter (#1520)

makezero ensures that objects recognized as slices are initialized with length 0.  By default, this is only required when we find a subsequent append to the object, but can also be enabled at all times as a way of discouraging the use of integer variables (i.e. "i") to index slices.
This commit is contained in:
Andrew Shannon Brown 2020-12-05 07:37:37 -08:00 committed by GitHub
parent f7a0c3c2e6
commit cf32a7b706
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 102 additions and 0 deletions

View File

@ -341,6 +341,9 @@ linters-settings:
errorlint:
# Report non-wrapping error creation using fmt.Errorf
errorf: true
makezero:
# Allow only slices initialized with a length of zero. Default is false.
always: false
# The custom section can be used to define linter plugins to be loaded at runtime. See README doc
# for more info.

1
go.mod
View File

@ -6,6 +6,7 @@ require (
4d63.com/gochecknoglobals v0.0.0-20201008074935-acfc0b28355a
github.com/Djarvur/go-err113 v0.0.0-20200511133814-5174e21577d5
github.com/OpenPeeDeeP/depguard v1.0.1
github.com/ashanbrown/makezero v0.0.0-20201205152432-7b7cdbb3025a
github.com/bombsimon/wsl/v3 v3.1.0
github.com/daixiang0/gci v0.2.4
github.com/denis-tingajkin/go-header v0.3.1

5
go.sum generated
View File

@ -31,6 +31,10 @@ github.com/andybalholm/brotli v1.0.0/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/ashanbrown/makezero v0.0.0-20201122223944-6aa12ecc2e71 h1:I5vCM3EzMpsjllkzez2fjY/FXJNbtulB33LudiHuMNo=
github.com/ashanbrown/makezero v0.0.0-20201122223944-6aa12ecc2e71/go.mod h1:oG9Dnez7/ESBqc4EdrdNlryeo7d0KcW1ftXHm7nU/UU=
github.com/ashanbrown/makezero v0.0.0-20201205152432-7b7cdbb3025a h1:/U9tbJzDRof4fOR51vwzWdIBsIH6R2yU0KG1MBRM2Js=
github.com/ashanbrown/makezero v0.0.0-20201205152432-7b7cdbb3025a/go.mod h1:oG9Dnez7/ESBqc4EdrdNlryeo7d0KcW1ftXHm7nU/UU=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
@ -527,6 +531,7 @@ golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgw
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190910044552-dd2b5c81c578/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190916130336-e45ffcd953cc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=

View File

@ -266,6 +266,7 @@ type LintersSettings struct {
Exhaustive ExhaustiveSettings
Gofumpt GofumptSettings
ErrorLint ErrorLintSettings
Makezero MakezeroSettings
Custom map[string]CustomLinterSettings
}
@ -386,6 +387,10 @@ type ErrorLintSettings struct {
Errorf bool `mapstructure:"errorf"`
}
type MakezeroSettings struct {
Always bool
}
var defaultLintersSettings = LintersSettings{
Lll: LllSettings{
LineLength: 120,

60
pkg/golinters/makezero.go Normal file
View File

@ -0,0 +1,60 @@
package golinters
import (
"sync"
"github.com/ashanbrown/makezero/makezero"
"github.com/pkg/errors"
"golang.org/x/tools/go/analysis"
"github.com/golangci/golangci-lint/pkg/golinters/goanalysis"
"github.com/golangci/golangci-lint/pkg/lint/linter"
"github.com/golangci/golangci-lint/pkg/result"
)
const makezeroName = "makezero"
func NewMakezero() *goanalysis.Linter {
var mu sync.Mutex
var resIssues []goanalysis.Issue
analyzer := &analysis.Analyzer{
Name: makezeroName,
Doc: goanalysis.TheOnlyanalyzerDoc,
}
return goanalysis.NewLinter(
makezeroName,
"Finds slice declarations with non-zero initial length",
[]*analysis.Analyzer{analyzer},
nil,
).WithContextSetter(func(lintCtx *linter.Context) {
s := &lintCtx.Settings().Makezero
analyzer.Run = func(pass *analysis.Pass) (interface{}, error) {
var res []goanalysis.Issue
linter := makezero.NewLinter(s.Always)
for _, file := range pass.Files {
hints, err := linter.Run(pass.Fset, pass.TypesInfo, file)
if err != nil {
return nil, errors.Wrapf(err, "makezero linter failed on file %q", file.Name.String())
}
for _, hint := range hints {
res = append(res, goanalysis.NewIssue(&result.Issue{
Pos: hint.Position(),
Text: hint.Details(),
FromLinter: makezeroName,
}, pass))
}
}
if len(res) == 0 {
return nil, nil
}
mu.Lock()
resIssues = append(resIssues, res...)
mu.Unlock()
return nil, nil
}
}).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue {
return resIssues
}).WithLoadMode(goanalysis.LoadModeSyntax | goanalysis.LoadModeTypesInfo)
}

View File

@ -328,6 +328,9 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config {
WithPresets(linter.PresetStyle).
WithLoadForGoAnalysis().
WithURL("https://github.com/kunwardeep/paralleltest"),
linter.NewConfig(golinters.NewMakezero()).
WithPresets(linter.PresetStyle, linter.PresetBugs).
WithURL("https://github.com/ashanbrown/makezero"),
// nolintlint must be last because it looks at the results of all the previous linters for unused nolint directives
linter.NewConfig(golinters.NewNoLintLint()).

12
test/testdata/makezero.go vendored Normal file
View File

@ -0,0 +1,12 @@
//args: -Emakezero
package testdata
func Makezero() []int {
x := make([]int, 5)
return append(x, 1) // ERROR "append to slice `x` with non-zero initialized length"
}
func MakezeroNolint() []int {
x := make([]int, 5)
return append(x, 1) //nolint:makezero // ok that we're appending to an uninitialized slice
}

13
test/testdata/makezero_always.go vendored Normal file
View File

@ -0,0 +1,13 @@
//args: -Emakezero
//config: linters-settings.makezero.always=true
package testdata
func MakezeroAlways() []int {
x := make([]int, 5) // ERROR "slice `x` does not have non-zero initial length"
return x
}
func MakezeroAlwaysNolint() []int {
x := make([]int, 5) //nolint:makezero // ok that this is not initialized
return x
}