diff --git a/.golangci.next.reference.yml b/.golangci.next.reference.yml index 4d5d5c88aad3..31eff76c6f75 100644 --- a/.golangci.next.reference.yml +++ b/.golangci.next.reference.yml @@ -1540,6 +1540,14 @@ linters-settings: patterns: - ".*" + recv: + # Maximum length for a receiver name. + # Default: 3 + max-name-length: 6 + # Allow using pointer receiver and non-pointer receiver on the same struct. + # Default: true + type-consistency: false + revive: # Maximum number of open files at the same time. # See https://github.com/mgechev/revive#command-line-flags @@ -2689,6 +2697,7 @@ linters: - promlinter - protogetter - reassign + - recv - revive - rowserrcheck - sloglint @@ -2804,6 +2813,7 @@ linters: - promlinter - protogetter - reassign + - recv - revive - rowserrcheck - sloglint diff --git a/go.mod b/go.mod index 051df607ecc0..588138c20449 100644 --- a/go.mod +++ b/go.mod @@ -67,6 +67,7 @@ require ( github.com/kyoh86/exportloopref v0.1.11 github.com/lasiar/canonicalheader v1.1.1 github.com/ldez/gomoddirectives v0.2.4 + github.com/ldez/recv v0.1.2 github.com/ldez/tagliatelle v0.5.0 github.com/leonklingele/grouper v1.1.2 github.com/lufeee/execinquery v1.2.1 diff --git a/go.sum b/go.sum index a8b2a42c91f2..b02938797ec6 100644 --- a/go.sum +++ b/go.sum @@ -343,6 +343,8 @@ github.com/lasiar/canonicalheader v1.1.1 h1:wC+dY9ZfiqiPwAexUApFush/csSPXeIi4Qqy github.com/lasiar/canonicalheader v1.1.1/go.mod h1:cXkb3Dlk6XXy+8MVQnF23CYKWlyA7kfQhSw2CcZtZb0= github.com/ldez/gomoddirectives v0.2.4 h1:j3YjBIjEBbqZ0NKtBNzr8rtMHTOrLPeiwTkfUJZ3alg= github.com/ldez/gomoddirectives v0.2.4/go.mod h1:oWu9i62VcQDYp9EQ0ONTfqLNh+mDLWWDO+SO0qSQw5g= +github.com/ldez/recv v0.1.2 h1:j/1nFLi4wwItCBSYX+fQCIe06IlXP6OhSLdYKJig8Cc= +github.com/ldez/recv v0.1.2/go.mod h1:I/thYIKCTqjjfuVz8P/jkqeQUl1V5i88qYudsGok6FI= github.com/ldez/tagliatelle v0.5.0 h1:epgfuYt9v0CG3fms0pEgIMNPuFf/LpPIfjk4kyqSioo= github.com/ldez/tagliatelle v0.5.0/go.mod h1:rj1HmWiL1MiKQuOONhd09iySTEkUuE/8+5jtPYz9xa4= github.com/leonklingele/grouper v1.1.2 h1:o1ARBDLOmmasUaNDesWqWCIFH3u7hoFlM84YrjT3mIY= diff --git a/jsonschema/golangci.next.jsonschema.json b/jsonschema/golangci.next.jsonschema.json index e137b14c6d39..50d1bc39bbfb 100644 --- a/jsonschema/golangci.next.jsonschema.json +++ b/jsonschema/golangci.next.jsonschema.json @@ -388,6 +388,7 @@ "promlinter", "protogetter", "reassign", + "recv", "revive", "rowserrcheck", "scopelint", @@ -2283,6 +2284,22 @@ } } }, + "recv": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-name-length": { + "description": "Maximum length for a receiver name.", + "type": "integer", + "default": 3 + }, + "type-consistency": { + "description": "Allow using pointer receiver and non-pointer receiver on the same struct", + "type": "boolean", + "default": true + } + } + }, "nonamedreturns": { "type": "object", "additionalProperties": false, diff --git a/pkg/config/linters_settings.go b/pkg/config/linters_settings.go index 109de42431e1..f90166e5a1a4 100644 --- a/pkg/config/linters_settings.go +++ b/pkg/config/linters_settings.go @@ -137,6 +137,10 @@ var defaultLintersSettings = LintersSettings{ Ignore: "", Qualified: false, }, + Recv: RecvSettings{ + MaxNameLength: 3, + TypeConsistency: true, + }, SlogLint: SlogLintSettings{ NoMixedArgs: true, KVOnly: false, @@ -257,6 +261,7 @@ type LintersSettings struct { Promlinter PromlinterSettings ProtoGetter ProtoGetterSettings Reassign ReassignSettings + Recv RecvSettings Revive ReviveSettings RowsErrCheck RowsErrCheckSettings SlogLint SlogLintSettings @@ -791,6 +796,11 @@ type ReassignSettings struct { Patterns []string `mapstructure:"patterns"` } +type RecvSettings struct { + MaxNameLength int `mapstructure:"max-name-length"` + TypeConsistency bool `mapstructure:"type-consistency"` +} + type ReviveSettings struct { Go string `mapstructure:"-"` MaxOpenFiles int `mapstructure:"max-open-files"` diff --git a/pkg/golinters/recv/recv.go b/pkg/golinters/recv/recv.go new file mode 100644 index 000000000000..6778b2bc671c --- /dev/null +++ b/pkg/golinters/recv/recv.go @@ -0,0 +1,27 @@ +package recv + +import ( + "github.com/ldez/recv" + "golang.org/x/tools/go/analysis" + + "github.com/golangci/golangci-lint/pkg/config" + "github.com/golangci/golangci-lint/pkg/goanalysis" +) + +func New(settings *config.RecvSettings) *goanalysis.Linter { + cfg := recv.Config{} + + if settings != nil { + cfg.MaxNameLength = settings.MaxNameLength + cfg.TypeConsistency = settings.TypeConsistency + } + + a := recv.New(cfg) + + return goanalysis.NewLinter( + a.Name, + a.Doc, + []*analysis.Analyzer{a}, + nil, + ).WithLoadMode(goanalysis.LoadModeTypesInfo) +} diff --git a/pkg/golinters/recv/recv_integration_test.go b/pkg/golinters/recv/recv_integration_test.go new file mode 100644 index 000000000000..e4ecaa8752ea --- /dev/null +++ b/pkg/golinters/recv/recv_integration_test.go @@ -0,0 +1,11 @@ +package recv + +import ( + "testing" + + "github.com/golangci/golangci-lint/test/testshared/integration" +) + +func TestFromTestdata(t *testing.T) { + integration.RunTestdata(t) +} diff --git a/pkg/golinters/recv/testdata/recv.go b/pkg/golinters/recv/testdata/recv.go new file mode 100644 index 000000000000..cbb09808727a --- /dev/null +++ b/pkg/golinters/recv/testdata/recv.go @@ -0,0 +1,22 @@ +//golangcitest:args -Erecv +package testdata + +import "fmt" + +type Foo struct { // want `the methods of "Foo" use different receiver names: f, fo.` + Name string +} + +func (f Foo) A() {} +func (fo Foo) B() {} + +type Bar struct{} // want `the methods of "Bar" use pointer receiver and non-pointer receiver.` + +func (b Bar) A() {} +func (b *Bar) B() {} + +type Fuu struct{} + +func (faaa Fuu) A() { // want `the receiver name "faaa" is too long.` + fmt.Println("a") +} diff --git a/pkg/golinters/recv/testdata/recv_disable_type_consistency.go b/pkg/golinters/recv/testdata/recv_disable_type_consistency.go new file mode 100644 index 000000000000..f9300d82a859 --- /dev/null +++ b/pkg/golinters/recv/testdata/recv_disable_type_consistency.go @@ -0,0 +1,23 @@ +//golangcitest:args -Erecv +//golangcitest:config_path testdata/recv_disable_type_consistency.yml +package testdata + +import "fmt" + +type Foo struct { // want `the methods of "Foo" use different receiver names: f, fo.` + Name string +} + +func (f Foo) A() {} +func (fo Foo) B() {} + +type Bar struct{} + +func (b Bar) A() {} +func (b *Bar) B() {} + +type Fuu struct{} + +func (faaa Fuu) A() { // want `the receiver name "faaa" is too long.` + fmt.Println("a") +} diff --git a/pkg/golinters/recv/testdata/recv_disable_type_consistency.yml b/pkg/golinters/recv/testdata/recv_disable_type_consistency.yml new file mode 100644 index 000000000000..c1dff2e3b93d --- /dev/null +++ b/pkg/golinters/recv/testdata/recv_disable_type_consistency.yml @@ -0,0 +1,3 @@ +linters-settings: + recv: + type-consistency: false diff --git a/pkg/golinters/recv/testdata/recv_max_name_length.go b/pkg/golinters/recv/testdata/recv_max_name_length.go new file mode 100644 index 000000000000..114699574eb1 --- /dev/null +++ b/pkg/golinters/recv/testdata/recv_max_name_length.go @@ -0,0 +1,17 @@ +//golangcitest:args -Erecv +//golangcitest:config_path testdata/recv_max_name_length.yml +package testdata + +import "fmt" + +type Fuu struct{} + +func (faaa Fuu) A() { + fmt.Println("a") +} + +type Foo struct{} + +func (faaaaaa Foo) A() { // want `the receiver name "faaaaaa" is too long.` + fmt.Println("a") +} diff --git a/pkg/golinters/recv/testdata/recv_max_name_length.yml b/pkg/golinters/recv/testdata/recv_max_name_length.yml new file mode 100644 index 000000000000..1b112677b2b7 --- /dev/null +++ b/pkg/golinters/recv/testdata/recv_max_name_length.yml @@ -0,0 +1,3 @@ +linters-settings: + recv: + max-name-length: 6 diff --git a/pkg/lint/lintersdb/builder_linter.go b/pkg/lint/lintersdb/builder_linter.go index c06cd9a038cd..7c13f4048b25 100644 --- a/pkg/lint/lintersdb/builder_linter.go +++ b/pkg/lint/lintersdb/builder_linter.go @@ -85,6 +85,7 @@ import ( "github.com/golangci/golangci-lint/pkg/golinters/promlinter" "github.com/golangci/golangci-lint/pkg/golinters/protogetter" "github.com/golangci/golangci-lint/pkg/golinters/reassign" + "github.com/golangci/golangci-lint/pkg/golinters/recv" "github.com/golangci/golangci-lint/pkg/golinters/revive" "github.com/golangci/golangci-lint/pkg/golinters/rowserrcheck" "github.com/golangci/golangci-lint/pkg/golinters/sloglint" @@ -657,6 +658,12 @@ func (LinterBuilder) Build(cfg *config.Config) ([]*linter.Config, error) { WithLoadForGoAnalysis(). WithURL("https://github.com/curioswitch/go-reassign"), + linter.NewConfig(recv.New(&cfg.LintersSettings.Recv)). + WithSince("1.62.0"). + WithPresets(linter.PresetStyle). + WithLoadForGoAnalysis(). + WithURL("https://github.com/ldez/recv"), + linter.NewConfig(revive.New(&cfg.LintersSettings.Revive)). WithSince("v1.37.0"). WithPresets(linter.PresetStyle, linter.PresetMetaLinter).