From a73a30e068506e7d9888b5e7c8c94715da86b6a1 Mon Sep 17 00:00:00 2001 From: AdminBenni Date: Sat, 2 Aug 2025 14:41:54 +0000 Subject: [PATCH 1/3] feat: add iotamixing linter --- .golangci.next.reference.yml | 7 +++ go.mod | 1 + go.sum | 2 + jsonschema/golangci.next.jsonschema.json | 15 +++++ pkg/config/linters_settings.go | 5 ++ pkg/golinters/iotamixing/iotamixing.go | 29 ++++++++++ .../iotamixing/iotamixing_integration_test.go | 11 ++++ .../testdata/iotamixing-report-individual.go | 50 +++++++++++++++++ .../testdata/iotamixing-report-individual.yml | 6 ++ .../iotamixing/testdata/iotamixing.go | 55 +++++++++++++++++++ pkg/lint/lintersdb/builder_linter.go | 5 ++ 11 files changed, 186 insertions(+) create mode 100644 pkg/golinters/iotamixing/iotamixing.go create mode 100644 pkg/golinters/iotamixing/iotamixing_integration_test.go create mode 100644 pkg/golinters/iotamixing/testdata/iotamixing-report-individual.go create mode 100644 pkg/golinters/iotamixing/testdata/iotamixing-report-individual.yml create mode 100644 pkg/golinters/iotamixing/testdata/iotamixing.go 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..067a3450d0cb 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 v0.0.5 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..364469e016b2 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 v0.0.5 h1:BUJ5+puRS0vksO4JFOqsSMDlpuQrU6W6cw4QI3doXAU= +github.com/AdminBenni/iota-mixing v0.0.5/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..052d8d5f8b4f --- /dev/null +++ b/pkg/golinters/iotamixing/iotamixing.go @@ -0,0 +1,29 @@ +package iotamixing + +import ( + "golang.org/x/tools/go/analysis" + + "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 { + a := analyzer.GetIotaMixingAnalyzer() + + flags.SetupFlags(&a.Flags) + + cfg := map[string]map[string]any{} + if settings != nil { + cfg[a.Name] = map[string]any{flags.ReportIndividualFlagName: settings.ReportIndividual} + } + + return goanalysis.NewLinter( + a.Name, + a.Doc, + []*analysis.Analyzer{a}, + 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-report-individual.go b/pkg/golinters/iotamixing/testdata/iotamixing-report-individual.go new file mode 100644 index 000000000000..f23b06675241 --- /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/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/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(). From 49241052c27fd462e40f6873c5499309a12ecc0c Mon Sep 17 00:00:00 2001 From: Fernandez Ludovic Date: Tue, 9 Sep 2025 15:21:10 +0200 Subject: [PATCH 2/3] review --- pkg/golinters/iotamixing/iotamixing.go | 25 ++++++++----------- ...ual.go => iotamixing_report-individual.go} | 2 +- ...l.yml => iotamixing_report-individual.yml} | 0 3 files changed, 12 insertions(+), 15 deletions(-) rename pkg/golinters/iotamixing/testdata/{iotamixing-report-individual.go => iotamixing_report-individual.go} (97%) rename pkg/golinters/iotamixing/testdata/{iotamixing-report-individual.yml => iotamixing_report-individual.yml} (100%) diff --git a/pkg/golinters/iotamixing/iotamixing.go b/pkg/golinters/iotamixing/iotamixing.go index 052d8d5f8b4f..dee0c3c77124 100644 --- a/pkg/golinters/iotamixing/iotamixing.go +++ b/pkg/golinters/iotamixing/iotamixing.go @@ -1,9 +1,7 @@ package iotamixing import ( - "golang.org/x/tools/go/analysis" - - "github.com/AdminBenni/iota-mixing/pkg/analyzer" + im "github.com/AdminBenni/iota-mixing/pkg/analyzer" "github.com/AdminBenni/iota-mixing/pkg/analyzer/flags" "github.com/golangci/golangci-lint/v2/pkg/config" @@ -11,19 +9,18 @@ import ( ) func New(settings *config.IotaMixingSettings) *goanalysis.Linter { - a := analyzer.GetIotaMixingAnalyzer() - - flags.SetupFlags(&a.Flags) + cfg := map[string]any{} - cfg := map[string]map[string]any{} if settings != nil { - cfg[a.Name] = map[string]any{flags.ReportIndividualFlagName: settings.ReportIndividual} + cfg[flags.ReportIndividualFlagName] = settings.ReportIndividual } - return goanalysis.NewLinter( - a.Name, - a.Doc, - []*analysis.Analyzer{a}, - cfg, - ).WithLoadMode(goanalysis.LoadModeSyntax) + analyzer := im.GetIotaMixingAnalyzer() + + flags.SetupFlags(&analyzer.Flags) + + return goanalysis. + NewLinterFromAnalyzer(analyzer). + WithConfig(cfg). + WithLoadMode(goanalysis.LoadModeSyntax) } diff --git a/pkg/golinters/iotamixing/testdata/iotamixing-report-individual.go b/pkg/golinters/iotamixing/testdata/iotamixing_report-individual.go similarity index 97% rename from pkg/golinters/iotamixing/testdata/iotamixing-report-individual.go rename to pkg/golinters/iotamixing/testdata/iotamixing_report-individual.go index f23b06675241..1549caeab9d8 100644 --- a/pkg/golinters/iotamixing/testdata/iotamixing-report-individual.go +++ b/pkg/golinters/iotamixing/testdata/iotamixing_report-individual.go @@ -1,5 +1,5 @@ //golangcitest:args -Eiotamixing -//golangcitest:config_path testdata/iotamixing-report-individual.yml +//golangcitest:config_path testdata/iotamixing_report-individual.yml package testdata import "fmt" diff --git a/pkg/golinters/iotamixing/testdata/iotamixing-report-individual.yml b/pkg/golinters/iotamixing/testdata/iotamixing_report-individual.yml similarity index 100% rename from pkg/golinters/iotamixing/testdata/iotamixing-report-individual.yml rename to pkg/golinters/iotamixing/testdata/iotamixing_report-individual.yml From f003a0af3f3ca371b554b1d2303dfafb8c123f67 Mon Sep 17 00:00:00 2001 From: Fernandez Ludovic Date: Wed, 10 Sep 2025 21:44:33 +0200 Subject: [PATCH 3/3] chore: update version --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 067a3450d0cb..c3e36f60e843 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +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 v0.0.5 + 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 364469e016b2..3d9f20efac66 100644 --- a/go.sum +++ b/go.sum @@ -45,8 +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 v0.0.5 h1:BUJ5+puRS0vksO4JFOqsSMDlpuQrU6W6cw4QI3doXAU= -github.com/AdminBenni/iota-mixing v0.0.5/go.mod h1:i4+tpAaB+qMVIV9OK3m4/DAynOd5bQFaOu+2AhtBCNY= +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=