diff --git a/.golangci.reference.yml b/.golangci.reference.yml index 408a3bad..ac5dedea 100644 --- a/.golangci.reference.yml +++ b/.golangci.reference.yml @@ -1074,6 +1074,11 @@ linters-settings: - pkg: knative.dev/serving/pkg/apis/(\w+)/(v[\w\d]+) alias: $1$2 + interfacebloat: + # The maximum number of methods allowed for an interface. + # Default: 10 + max: 5 + ireturn: # ireturn allows using `allow` and `reject` settings at the same time. # Both settings are lists of the keywords and regular expressions matched to interface or package names. @@ -1923,6 +1928,7 @@ linters: - ifshort - importas - ineffassign + - interfacebloat - interfacer - ireturn - lll @@ -2025,6 +2031,7 @@ linters: - ifshort - importas - ineffassign + - interfacebloat - interfacer - ireturn - lll diff --git a/go.mod b/go.mod index d94dfebb..dc598b75 100644 --- a/go.mod +++ b/go.mod @@ -74,6 +74,7 @@ require ( github.com/ryancurrah/gomodguard v1.2.4 github.com/ryanrolds/sqlclosecheck v0.3.0 github.com/sanposhiho/wastedassign/v2 v2.0.6 + github.com/sashamelentyev/interfacebloat v1.1.0 github.com/sashamelentyev/usestdlibvars v1.13.0 github.com/securego/gosec/v2 v2.13.1 github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c diff --git a/go.sum b/go.sum index 12699238..c348e99f 100644 --- a/go.sum +++ b/go.sum @@ -459,6 +459,8 @@ github.com/ryanrolds/sqlclosecheck v0.3.0 h1:AZx+Bixh8zdUBxUA1NxbxVAS78vTPq4rCb8 github.com/ryanrolds/sqlclosecheck v0.3.0/go.mod h1:1gREqxyTGR3lVtpngyFo3hZAgk0KCtEdgEkHwDbigdA= github.com/sanposhiho/wastedassign/v2 v2.0.6 h1:+6/hQIHKNJAUixEj6EmOngGIisyeI+T3335lYTyxRoA= github.com/sanposhiho/wastedassign/v2 v2.0.6/go.mod h1:KyZ0MWTwxxBmfwn33zh3k1dmsbF2ud9pAAGfoLfjhtI= +github.com/sashamelentyev/interfacebloat v1.1.0 h1:xdRdJp0irL086OyW1H/RTZTr1h/tMEOsumirXcOJqAw= +github.com/sashamelentyev/interfacebloat v1.1.0/go.mod h1:+Y9yU5YdTkrNvoX0xHc84dxiN1iBi9+G8zZIhPVoNjQ= github.com/sashamelentyev/usestdlibvars v1.13.0 h1:uObNudVEEHf6JbOJy5bgKJloA1bWjxR9fwgNFpPzKnI= github.com/sashamelentyev/usestdlibvars v1.13.0/go.mod h1:D2Wb7niIYmTB+gB8z7kh8tyP5ccof1dQ+SFk+WW5NtY= github.com/securego/gosec/v2 v2.13.1 h1:7mU32qn2dyC81MH9L2kefnQyRMUarfDER3iQyMHcjYM= diff --git a/pkg/config/linters_settings.go b/pkg/config/linters_settings.go index 1e923409..242a1de1 100644 --- a/pkg/config/linters_settings.go +++ b/pkg/config/linters_settings.go @@ -151,6 +151,7 @@ type LintersSettings struct { Grouper GrouperSettings Ifshort IfshortSettings ImportAs ImportAsSettings + InterfaceBloat InterfaceBloatSettings Ireturn IreturnSettings Lll LllSettings MaintIdx MaintIdxSettings @@ -454,6 +455,10 @@ type ImportAsAlias struct { Alias string } +type InterfaceBloatSettings struct { + Max int `mapstructure:"max"` +} + type IreturnSettings struct { Allow []string `mapstructure:"allow"` Reject []string `mapstructure:"reject"` diff --git a/pkg/golinters/interfacebloat.go b/pkg/golinters/interfacebloat.go new file mode 100644 index 00000000..f9cf81c8 --- /dev/null +++ b/pkg/golinters/interfacebloat.go @@ -0,0 +1,27 @@ +package golinters + +import ( + "github.com/sashamelentyev/interfacebloat/pkg/analyzer" + "golang.org/x/tools/go/analysis" + + "github.com/golangci/golangci-lint/pkg/config" + "github.com/golangci/golangci-lint/pkg/golinters/goanalysis" +) + +func NewInterfaceBloat(settings *config.InterfaceBloatSettings) *goanalysis.Linter { + a := analyzer.New() + + cfgMap := make(map[string]map[string]interface{}) + if settings != nil { + cfgMap[a.Name] = map[string]interface{}{ + analyzer.InterfaceMaxMethodsFlag: settings.Max, + } + } + + return goanalysis.NewLinter( + a.Name, + a.Doc, + []*analysis.Analyzer{a}, + nil, + ).WithLoadMode(goanalysis.LoadModeSyntax) +} diff --git a/pkg/lint/lintersdb/manager.go b/pkg/lint/lintersdb/manager.go index 4552153c..4d69aac6 100644 --- a/pkg/lint/lintersdb/manager.go +++ b/pkg/lint/lintersdb/manager.go @@ -137,6 +137,7 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config { grouperCfg *config.GrouperSettings ifshortCfg *config.IfshortSettings importAsCfg *config.ImportAsSettings + interfaceBloatCfg *config.InterfaceBloatSettings ireturnCfg *config.IreturnSettings lllCfg *config.LllSettings maintIdxCfg *config.MaintIdxSettings @@ -209,6 +210,7 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config { grouperCfg = &m.cfg.LintersSettings.Grouper ifshortCfg = &m.cfg.LintersSettings.Ifshort importAsCfg = &m.cfg.LintersSettings.ImportAs + interfaceBloatCfg = &m.cfg.LintersSettings.InterfaceBloat ireturnCfg = &m.cfg.LintersSettings.Ireturn lllCfg = &m.cfg.LintersSettings.Lll maintIdxCfg = &m.cfg.LintersSettings.MaintIdx @@ -557,6 +559,11 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config { WithPresets(linter.PresetUnused). WithURL("https://github.com/gordonklaus/ineffassign"), + linter.NewConfig(golinters.NewInterfaceBloat(interfaceBloatCfg)). + WithSince("v1.49.0"). + WithPresets(linter.PresetStyle). + WithURL("https://github.com/sashamelentyev/interfacebloat"), + linter.NewConfig(golinters.NewInterfacer()). WithSince("v1.0.0"). WithLoadForGoAnalysis(). diff --git a/test/testdata/interfacebloat.go b/test/testdata/interfacebloat.go new file mode 100644 index 00000000..e85ebd4a --- /dev/null +++ b/test/testdata/interfacebloat.go @@ -0,0 +1,126 @@ +//golangcitest:args -Einterfacebloat +package testdata + +import "time" + +type InterfaceBloatExample01 interface { // want "the interface has more than 10 methods: 11" + a01() time.Duration + a02() + a03() + a04() + a05() + a06() + a07() + a08() + a09() + a10() + a11() +} + +func InterfaceBloatExample02() { + var _ interface { // want "the interface has more than 10 methods: 11" + a01() time.Duration + a02() + a03() + a04() + a05() + a06() + a07() + a08() + a09() + a10() + a11() + } +} + +func InterfaceBloatExample03() interface { // want "the interface has more than 10 methods: 11" + a01() time.Duration + a02() + a03() + a04() + a05() + a06() + a07() + a08() + a09() + a10() + a11() +} { + return nil +} + +type InterfaceBloatExample04 struct { + Foo interface { // want "the interface has more than 10 methods: 11" + a01() time.Duration + a02() + a03() + a04() + a05() + a06() + a07() + a08() + a09() + a10() + a11() + } +} + +type InterfaceBloatSmall01 interface { + a01() time.Duration + a02() + a03() + a04() + a05() +} + +type InterfaceBloatSmall02 interface { + a06() + a07() + a08() + a09() + a10() + a11() +} + +type InterfaceBloatExample05 interface { + InterfaceBloatSmall01 + InterfaceBloatSmall02 +} + +type InterfaceBloatExample06 interface { + interface { // want "the interface has more than 10 methods: 11" + a01() time.Duration + a02() + a03() + a04() + a05() + a06() + a07() + a08() + a09() + a10() + a11() + } +} + +type InterfaceBloatTypeGeneric interface { + ~uint8 | ~uint16 | ~uint32 | ~uint64 | uint | + ~int8 | ~int16 | ~int32 | ~int64 | int | + ~float32 | ~float64 | + ~string +} + +func InterfaceBloatExampleNoProblem() interface { + a01() time.Duration + a02() + a03() + a04() + a05() + a06() + a07() + a08() + a09() + a10() +} { + return nil +}