diff --git a/.golangci.next.reference.yml b/.golangci.next.reference.yml index 44ce5c4a9db8..23f662e5a1ea 100644 --- a/.golangci.next.reference.yml +++ b/.golangci.next.reference.yml @@ -75,6 +75,7 @@ linters: - ineffassign - interfacebloat - intrange + - iotamixing - ireturn - lll - loggercheck @@ -186,6 +187,7 @@ linters: - ineffassign - interfacebloat - intrange + - iotamixing - ireturn - lll - loggercheck @@ -1883,6 +1885,11 @@ linters: # Default: 10 max: 5 + iotamixing: + # Whether to report individual consts rather than just the const block. + # Default: false + report-individual: true + ireturn: # List of interfaces to allow. # Lists of the keywords and regular expressions matched to interface or package names can be used. diff --git a/go.mod b/go.mod index 788c13b6d9f3..c3e36f60e843 100644 --- a/go.mod +++ b/go.mod @@ -8,6 +8,7 @@ require ( dev.gaijin.team/go/exhaustruct/v4 v4.0.0 github.com/4meepo/tagalign v1.4.3 github.com/Abirdcfly/dupword v0.1.6 + github.com/AdminBenni/iota-mixing v1.0.0 github.com/AlwxSin/noinlineerr v1.0.5 github.com/Antonboom/errname v1.1.0 github.com/Antonboom/nilnil v1.1.0 diff --git a/go.sum b/go.sum index afd7617733b4..3d9f20efac66 100644 --- a/go.sum +++ b/go.sum @@ -45,6 +45,8 @@ github.com/4meepo/tagalign v1.4.3 h1:Bnu7jGWwbfpAie2vyl63Zup5KuRv21olsPIha53BJr8 github.com/4meepo/tagalign v1.4.3/go.mod h1:00WwRjiuSbrRJnSVeGWPLp2epS5Q/l4UEy0apLLS37c= github.com/Abirdcfly/dupword v0.1.6 h1:qeL6u0442RPRe3mcaLcbaCi2/Y/hOcdtw6DE9odjz9c= github.com/Abirdcfly/dupword v0.1.6/go.mod h1:s+BFMuL/I4YSiFv29snqyjwzDp4b65W2Kvy+PKzZ6cw= +github.com/AdminBenni/iota-mixing v1.0.0 h1:Os6lpjG2dp/AE5fYBPAA1zfa2qMdCAWwPMCgpwKq7wo= +github.com/AdminBenni/iota-mixing v1.0.0/go.mod h1:i4+tpAaB+qMVIV9OK3m4/DAynOd5bQFaOu+2AhtBCNY= github.com/AlwxSin/noinlineerr v1.0.5 h1:RUjt63wk1AYWTXtVXbSqemlbVTb23JOSRiNsshj7TbY= github.com/AlwxSin/noinlineerr v1.0.5/go.mod h1:+QgkkoYrMH7RHvcdxdlI7vYYEdgeoFOVjU9sUhw/rQc= github.com/Antonboom/errname v1.1.0 h1:A+ucvdpMwlo/myWrkHEUEBWc/xuXdud23S8tmTb/oAE= diff --git a/jsonschema/golangci.next.jsonschema.json b/jsonschema/golangci.next.jsonschema.json index 9221df3feb96..1a93f7f92b0b 100644 --- a/jsonschema/golangci.next.jsonschema.json +++ b/jsonschema/golangci.next.jsonschema.json @@ -813,6 +813,7 @@ "ineffassign", "interfacebloat", "intrange", + "iotamixing", "ireturn", "lll", "loggercheck", @@ -2429,6 +2430,17 @@ } } }, + "iotamixingSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "report-individual": { + "description": "Whether to report individual consts rather than just the const block.", + "type": "boolean", + "default": false + } + } + }, "ireturnSettings": { "type": "object", "additionalProperties": false, @@ -4589,6 +4601,9 @@ "ineffassign": { "$ref": "#/definitions/settings/definitions/ineffassignSettings" }, + "iotamixing": { + "$ref": "#/definitions/settings/definitions/iotamixingSettings" + }, "ireturn": { "$ref": "#/definitions/settings/definitions/ireturnSettings" }, diff --git a/pkg/config/linters_settings.go b/pkg/config/linters_settings.go index 567416d977de..c1660d01cd99 100644 --- a/pkg/config/linters_settings.go +++ b/pkg/config/linters_settings.go @@ -256,6 +256,7 @@ type LintersSettings struct { Inamedparam INamedParamSettings `mapstructure:"inamedparam"` Ineffassign IneffassignSettings `mapstructure:"ineffassign"` InterfaceBloat InterfaceBloatSettings `mapstructure:"interfacebloat"` + IotaMixing IotaMixingSettings `mapstructure:"iotamixing"` Ireturn IreturnSettings `mapstructure:"ireturn"` Lll LllSettings `mapstructure:"lll"` LoggerCheck LoggerCheckSettings `mapstructure:"loggercheck"` @@ -653,6 +654,10 @@ type InterfaceBloatSettings struct { Max int `mapstructure:"max"` } +type IotaMixingSettings struct { + ReportIndividual bool `mapstructure:"report-individual"` +} + type IreturnSettings struct { Allow []string `mapstructure:"allow"` Reject []string `mapstructure:"reject"` diff --git a/pkg/golinters/iotamixing/iotamixing.go b/pkg/golinters/iotamixing/iotamixing.go new file mode 100644 index 000000000000..dee0c3c77124 --- /dev/null +++ b/pkg/golinters/iotamixing/iotamixing.go @@ -0,0 +1,26 @@ +package iotamixing + +import ( + im "github.com/AdminBenni/iota-mixing/pkg/analyzer" + "github.com/AdminBenni/iota-mixing/pkg/analyzer/flags" + + "github.com/golangci/golangci-lint/v2/pkg/config" + "github.com/golangci/golangci-lint/v2/pkg/goanalysis" +) + +func New(settings *config.IotaMixingSettings) *goanalysis.Linter { + cfg := map[string]any{} + + if settings != nil { + cfg[flags.ReportIndividualFlagName] = settings.ReportIndividual + } + + analyzer := im.GetIotaMixingAnalyzer() + + flags.SetupFlags(&analyzer.Flags) + + return goanalysis. + NewLinterFromAnalyzer(analyzer). + WithConfig(cfg). + WithLoadMode(goanalysis.LoadModeSyntax) +} diff --git a/pkg/golinters/iotamixing/iotamixing_integration_test.go b/pkg/golinters/iotamixing/iotamixing_integration_test.go new file mode 100644 index 000000000000..b1f1b49520df --- /dev/null +++ b/pkg/golinters/iotamixing/iotamixing_integration_test.go @@ -0,0 +1,11 @@ +package iotamixing + +import ( + "testing" + + "github.com/golangci/golangci-lint/v2/test/testshared/integration" +) + +func TestFromTestdata(t *testing.T) { + integration.RunTestdata(t) +} diff --git a/pkg/golinters/iotamixing/testdata/iotamixing.go b/pkg/golinters/iotamixing/testdata/iotamixing.go new file mode 100644 index 000000000000..ab537b632dc9 --- /dev/null +++ b/pkg/golinters/iotamixing/testdata/iotamixing.go @@ -0,0 +1,55 @@ +//golangcitest:args -Eiotamixing +package testdata + +import "fmt" + +// iota mixing in const block containing an iota and r-val declared above. +const ( // want "iota mixing. keep iotas in separate blocks to consts with r-val" + InvalidPerBlockIotaDeclAboveAnything = "anything" + InvalidPerBlockIotaDeclAboveNotZero = iota + InvalidPerBlockIotaDeclAboveNotOne + InvalidPerBlockIotaDeclAboveNotTwo +) + +// iota mixing in const block containing an iota and r-val declared below. +const ( // want "iota mixing. keep iotas in separate blocks to consts with r-val" + InvalidPerBlockIotaDeclBelowZero = iota + InvalidPerBlockIotaDeclBelowOne + InvalidPerBlockIotaDeclBelowTwo + InvalidPerBlockIotaDeclBelowAnything = "anything" +) + +// iota mixing in const block containing an iota and r-val declared between consts. +const ( // want "iota mixing. keep iotas in separate blocks to consts with r-val" + InvalidPerBlockIotaDeclBetweenZero = iota + InvalidPerBlockIotaDeclBetweenOne + InvalidPerBlockIotaDeclBetweenAnything = "anything" + InvalidPerBlockIotaDeclBetweenNotTwo +) + +// iota mixing in const block containing an iota and r-vals declared above, between, and below consts. +const ( // want "iota mixing. keep iotas in separate blocks to consts with r-val" + InvalidPerBlockIotaDeclMultipleAbove = "above" + InvalidPerBlockIotaDeclMultipleNotZero = iota + InvalidPerBlockIotaDeclMultipleNotOne + InvalidPerBlockIotaDeclMultipleBetween = "between" + InvalidPerBlockIotaDeclMultipleNotTwo + InvalidPerBlockIotaDeclMultipleBelow = "below" +) + +// no iota mixing in a const block containing an iota and no r-vals. +const ( + ValidPerBlockIotaZero = iota + ValidPerBlockIotaOne + ValidPerBlockIotaTwo +) + +// no iota mixing in a const block containing r-vals and no iota. +const ( + ValidPerBlockRegularSomething = "something" + ValidPerBlockRegularAnything = "anything" +) + +func _() { + fmt.Println("using the std import so goland doesn't nuke it") +} diff --git a/pkg/golinters/iotamixing/testdata/iotamixing_report-individual.go b/pkg/golinters/iotamixing/testdata/iotamixing_report-individual.go new file mode 100644 index 000000000000..1549caeab9d8 --- /dev/null +++ b/pkg/golinters/iotamixing/testdata/iotamixing_report-individual.go @@ -0,0 +1,50 @@ +//golangcitest:args -Eiotamixing +//golangcitest:config_path testdata/iotamixing_report-individual.yml +package testdata + +import "fmt" + +const ( + InvalidPerIndividualIotaDeclAboveAnything = "anything" // want "InvalidPerIndividualIotaDeclAboveAnything is a const with r-val in same const block as iota. keep iotas in separate const blocks" + InvalidPerIndividualIotaDeclAboveNotZero = iota + InvalidPerIndividualIotaDeclAboveNotOne + InvalidPerIndividualIotaDeclAboveNotTwo +) + +const ( + InvalidPerIndividualIotaDeclBelowZero = iota + InvalidPerIndividualIotaDeclBelowOne + InvalidPerIndividualIotaDeclBelowTwo + InvalidPerIndividualIotaDeclBelowAnything = "anything" // want "InvalidPerIndividualIotaDeclBelowAnything is a const with r-val in same const block as iota. keep iotas in separate const blocks" +) + +const ( + InvalidPerIndividualIotaDeclBetweenZero = iota + InvalidPerIndividualIotaDeclBetweenOne + InvalidPerIndividualIotaDeclBetweenAnything = "anything" // want "InvalidPerIndividualIotaDeclBetweenAnything is a const with r-val in same const block as iota. keep iotas in separate const blocks" + InvalidPerIndividualIotaDeclBetweenNotTwo +) + +const ( + InvalidPerIndividualIotaDeclMultipleAbove = "above" // want "InvalidPerIndividualIotaDeclMultipleAbove is a const with r-val in same const block as iota. keep iotas in separate const blocks" + InvalidPerIndividualIotaDeclMultipleNotZero = iota + InvalidPerIndividualIotaDeclMultipleNotOne + InvalidPerIndividualIotaDeclMultipleBetween = "between" // want "InvalidPerIndividualIotaDeclMultipleBetween is a const with r-val in same const block as iota. keep iotas in separate const blocks" + InvalidPerIndividualIotaDeclMultipleNotTwo + InvalidPerIndividualIotaDeclMultipleBelow = "below" // want "InvalidPerIndividualIotaDeclMultipleBelow is a const with r-val in same const block as iota. keep iotas in separate const blocks" +) + +const ( + ValidPerIndividualIotaZero = iota + ValidPerIndividualIotaOne + ValidPerIndividualIotaTwo +) + +const ( + ValidPerIndividualRegularSomething = "something" + ValidPerIndividualRegularAnything = "anything" +) + +func _() { + fmt.Println("using the std import so goland doesn't nuke it") +} diff --git a/pkg/golinters/iotamixing/testdata/iotamixing_report-individual.yml b/pkg/golinters/iotamixing/testdata/iotamixing_report-individual.yml new file mode 100644 index 000000000000..de35d627a2e6 --- /dev/null +++ b/pkg/golinters/iotamixing/testdata/iotamixing_report-individual.yml @@ -0,0 +1,6 @@ +version: "2" + +linters: + settings: + iotamixing: + report-individual: true diff --git a/pkg/lint/lintersdb/builder_linter.go b/pkg/lint/lintersdb/builder_linter.go index c64d498e4198..3c4254228eae 100644 --- a/pkg/lint/lintersdb/builder_linter.go +++ b/pkg/lint/lintersdb/builder_linter.go @@ -63,6 +63,7 @@ import ( "github.com/golangci/golangci-lint/v2/pkg/golinters/ineffassign" "github.com/golangci/golangci-lint/v2/pkg/golinters/interfacebloat" "github.com/golangci/golangci-lint/v2/pkg/golinters/intrange" + "github.com/golangci/golangci-lint/v2/pkg/golinters/iotamixing" "github.com/golangci/golangci-lint/v2/pkg/golinters/ireturn" "github.com/golangci/golangci-lint/v2/pkg/golinters/lll" "github.com/golangci/golangci-lint/v2/pkg/golinters/loggercheck" @@ -440,6 +441,10 @@ func (LinterBuilder) Build(cfg *config.Config) ([]*linter.Config, error) { WithURL("https://github.com/ckaznocha/intrange"). WithNoopFallback(cfg, linter.IsGoLowerThanGo122()), + linter.NewConfig(iotamixing.New(&cfg.Linters.Settings.IotaMixing)). + WithSince("v2.5.0"). + WithURL("github.com/AdminBenni/iota-mixing"), + linter.NewConfig(ireturn.New(&cfg.Linters.Settings.Ireturn)). WithSince("v1.43.0"). WithLoadForGoAnalysis().