From dc2869922a80aec73bda0f06b53b1d4aca01fcb8 Mon Sep 17 00:00:00 2001 From: rnben Date: Sat, 17 Aug 2024 09:08:48 +0800 Subject: [PATCH 1/4] feat(linters): add new linter gobreakselectinfor --- .golangci.next.reference.yml | 2 + jsonschema/golangci.next.jsonschema.json | 1 + .../gobreakselectinfor/gobreakselectinfor.go | 92 +++++++++++++++++++ .../gobreakselectinfor_test.go | 11 +++ .../testdata/gobreakselectinfor.go | 12 +++ pkg/lint/lintersdb/builder_linter.go | 7 ++ 6 files changed, 125 insertions(+) create mode 100644 pkg/golinters/gobreakselectinfor/gobreakselectinfor.go create mode 100644 pkg/golinters/gobreakselectinfor/gobreakselectinfor_test.go create mode 100644 pkg/golinters/gobreakselectinfor/testdata/gobreakselectinfor.go diff --git a/.golangci.next.reference.yml b/.golangci.next.reference.yml index 2d4c9e10e009..e93a772f67b5 100644 --- a/.golangci.next.reference.yml +++ b/.golangci.next.reference.yml @@ -2620,6 +2620,7 @@ linters: - funlen - gci - ginkgolinter + - gobreakselectinfor - gocheckcompilerdirectives - gochecknoglobals - gochecknoinits @@ -2735,6 +2736,7 @@ linters: - funlen - gci - ginkgolinter + - gobreakselectinfor - gocheckcompilerdirectives - gochecknoglobals - gochecknoinits diff --git a/jsonschema/golangci.next.jsonschema.json b/jsonschema/golangci.next.jsonschema.json index 510740d61660..c5cde1c32332 100644 --- a/jsonschema/golangci.next.jsonschema.json +++ b/jsonschema/golangci.next.jsonschema.json @@ -324,6 +324,7 @@ "funlen", "gci", "ginkgolinter", + "gobreakselectinfor", "gocheckcompilerdirectives", "gochecknoglobals", "gochecknoinits", diff --git a/pkg/golinters/gobreakselectinfor/gobreakselectinfor.go b/pkg/golinters/gobreakselectinfor/gobreakselectinfor.go new file mode 100644 index 000000000000..fb782e96a34e --- /dev/null +++ b/pkg/golinters/gobreakselectinfor/gobreakselectinfor.go @@ -0,0 +1,92 @@ +package gobreakselectinfor + +import ( + "go/ast" + "go/token" + "sync" + + "golang.org/x/tools/go/analysis" + + "github.com/golangci/golangci-lint/pkg/goanalysis" + "github.com/golangci/golangci-lint/pkg/lint/linter" + "github.com/golangci/golangci-lint/pkg/result" +) + +const linterName = "gobreakselectinfor" + +func New() *goanalysis.Linter { + var mu sync.Mutex + var resIssues []goanalysis.Issue + + analyzer := &analysis.Analyzer{ + Name: linterName, + Doc: goanalysis.TheOnlyanalyzerDoc, + Run: func(pass *analysis.Pass) (any, error) { + fileIssues := run(pass) + res := make([]goanalysis.Issue, 0, len(fileIssues)) + for i := range fileIssues { + res = append(res, goanalysis.NewIssue(&fileIssues[i], pass)) + } + if len(res) == 0 { + return nil, nil + } + + mu.Lock() + resIssues = append(resIssues, res...) + mu.Unlock() + + return nil, nil + }, + } + + return goanalysis.NewLinter( + linterName, + "Checks that break statement inside select statement inside for loop", + []*analysis.Analyzer{analyzer}, + nil, + ).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue { + return resIssues + }).WithLoadMode(goanalysis.LoadModeSyntax) +} + +func run(pass *analysis.Pass) []result.Issue { + var res []result.Issue + + inspect := func(node ast.Node) bool { + funcDecl, ok := node.(*ast.FuncDecl) + if !ok { + return true + } + + ast.Inspect(funcDecl.Body, func(stmt ast.Node) bool { + if forStmt, ok := stmt.(*ast.ForStmt); ok { + ast.Inspect(forStmt.Body, func(stmt ast.Node) bool { + if selStmt, ok := stmt.(*ast.SelectStmt); ok { + ast.Inspect(selStmt.Body, func(stmt ast.Node) bool { + if brkStmt, ok := stmt.(*ast.BranchStmt); ok && brkStmt.Tok == token.BREAK { + pass.Reportf(stmt.Pos(), "break statement inside select statement inside for loop") + res = append(res, result.Issue{ + Pos: pass.Fset.Position(stmt.Pos()), + Text: "break statement inside select statement inside for loop", + FromLinter: linterName, + }) + return true + } + return true + }) + } + return true + }) + } + return true + }) + + return true + } + + for _, f := range pass.Files { + ast.Inspect(f, inspect) + } + + return res +} diff --git a/pkg/golinters/gobreakselectinfor/gobreakselectinfor_test.go b/pkg/golinters/gobreakselectinfor/gobreakselectinfor_test.go new file mode 100644 index 000000000000..d1ae27b4c44d --- /dev/null +++ b/pkg/golinters/gobreakselectinfor/gobreakselectinfor_test.go @@ -0,0 +1,11 @@ +package gobreakselectinfor + +import ( + "testing" + + "github.com/golangci/golangci-lint/test/testshared/integration" +) + +func TestFromTestdata(t *testing.T) { + integration.RunTestdata(t) +} diff --git a/pkg/golinters/gobreakselectinfor/testdata/gobreakselectinfor.go b/pkg/golinters/gobreakselectinfor/testdata/gobreakselectinfor.go new file mode 100644 index 000000000000..6d8f4fa26488 --- /dev/null +++ b/pkg/golinters/gobreakselectinfor/testdata/gobreakselectinfor.go @@ -0,0 +1,12 @@ +//golangcitest:args -Egobreakselectinfor +package testdata + +func bad() { + var ch chan string + for { + select { + case <-ch: + break // want "break statement inside select statement inside for loop" + } + } +} diff --git a/pkg/lint/lintersdb/builder_linter.go b/pkg/lint/lintersdb/builder_linter.go index 3a46cbf88e5e..a3cf549b821f 100644 --- a/pkg/lint/lintersdb/builder_linter.go +++ b/pkg/lint/lintersdb/builder_linter.go @@ -33,6 +33,7 @@ import ( "github.com/golangci/golangci-lint/pkg/golinters/funlen" "github.com/golangci/golangci-lint/pkg/golinters/gci" "github.com/golangci/golangci-lint/pkg/golinters/ginkgolinter" + "github.com/golangci/golangci-lint/pkg/golinters/gobreakselectinfor" "github.com/golangci/golangci-lint/pkg/golinters/gocheckcompilerdirectives" "github.com/golangci/golangci-lint/pkg/golinters/gochecknoglobals" "github.com/golangci/golangci-lint/pkg/golinters/gochecknoinits" @@ -318,6 +319,12 @@ func (LinterBuilder) Build(cfg *config.Config) ([]*linter.Config, error) { WithPresets(linter.PresetStyle). WithURL("https://github.com/nunnatsa/ginkgolinter"), + linter.NewConfig(gobreakselectinfor.New()). + WithSince("1.61.0"). + WithPresets(linter.PresetStyle). + WithLoadForGoAnalysis(). + WithURL("https://github.com/rnben/go-break-select-in-for"), + linter.NewConfig(gocheckcompilerdirectives.New()). WithSince("v1.51.0"). WithPresets(linter.PresetBugs). From 0d670d67eaa57d7d02f9e60aad4b16d8ccf5d961 Mon Sep 17 00:00:00 2001 From: rnben Date: Sat, 17 Aug 2024 10:42:18 +0800 Subject: [PATCH 2/4] dev: reuse created linter --- go.mod | 1 + go.sum | 2 + .../gobreakselectinfor/gobreakselectinfor.go | 85 ++----------------- 3 files changed, 9 insertions(+), 79 deletions(-) diff --git a/go.mod b/go.mod index e5c83bc9d932..2bd8d6ace3ce 100644 --- a/go.mod +++ b/go.mod @@ -86,6 +86,7 @@ require ( github.com/pelletier/go-toml/v2 v2.2.2 github.com/polyfloyd/go-errorlint v1.6.0 github.com/quasilyte/go-ruleguard/dsl v0.3.22 + github.com/rnben/go-break-select-in-for v0.0.1 github.com/ryancurrah/gomodguard v1.3.3 github.com/ryanrolds/sqlclosecheck v0.5.1 github.com/sanposhiho/wastedassign/v2 v2.0.7 diff --git a/go.sum b/go.sum index 8dce73dbff62..a3715df2b524 100644 --- a/go.sum +++ b/go.sum @@ -456,6 +456,8 @@ github.com/quasilyte/regex/syntax v0.0.0-20210819130434-b3f0c404a727 h1:TCg2WBOl github.com/quasilyte/regex/syntax v0.0.0-20210819130434-b3f0c404a727/go.mod h1:rlzQ04UMyJXu/aOvhd8qT+hvDrFpiwqp8MRXDY9szc0= github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567 h1:M8mH9eK4OUR4lu7Gd+PU1fV2/qnDNfzT635KRSObncs= github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567/go.mod h1:DWNGW8A4Y+GyBgPuaQJuWiy0XYftx4Xm/y5Jqk9I6VQ= +github.com/rnben/go-break-select-in-for v0.0.1 h1:nsl0IXc94wigYVwbecdeBYrCLookdYvYTgwn/LWyHmg= +github.com/rnben/go-break-select-in-for v0.0.1/go.mod h1:7JoY3ApLuYoE4iHy1U9X/v5ix10c/9Z54hpFoYhwmow= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= diff --git a/pkg/golinters/gobreakselectinfor/gobreakselectinfor.go b/pkg/golinters/gobreakselectinfor/gobreakselectinfor.go index fb782e96a34e..3cc2706649ef 100644 --- a/pkg/golinters/gobreakselectinfor/gobreakselectinfor.go +++ b/pkg/golinters/gobreakselectinfor/gobreakselectinfor.go @@ -1,92 +1,19 @@ package gobreakselectinfor import ( - "go/ast" - "go/token" - "sync" - + "github.com/rnben/go-break-select-in-for/pkg/analyzer" "golang.org/x/tools/go/analysis" "github.com/golangci/golangci-lint/pkg/goanalysis" - "github.com/golangci/golangci-lint/pkg/lint/linter" - "github.com/golangci/golangci-lint/pkg/result" ) -const linterName = "gobreakselectinfor" - func New() *goanalysis.Linter { - var mu sync.Mutex - var resIssues []goanalysis.Issue - - analyzer := &analysis.Analyzer{ - Name: linterName, - Doc: goanalysis.TheOnlyanalyzerDoc, - Run: func(pass *analysis.Pass) (any, error) { - fileIssues := run(pass) - res := make([]goanalysis.Issue, 0, len(fileIssues)) - for i := range fileIssues { - res = append(res, goanalysis.NewIssue(&fileIssues[i], pass)) - } - if len(res) == 0 { - return nil, nil - } - - mu.Lock() - resIssues = append(resIssues, res...) - mu.Unlock() - - return nil, nil - }, - } + a := analyzer.Analyzer return goanalysis.NewLinter( - linterName, - "Checks that break statement inside select statement inside for loop", - []*analysis.Analyzer{analyzer}, + a.Name, + a.Doc, + []*analysis.Analyzer{a}, nil, - ).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue { - return resIssues - }).WithLoadMode(goanalysis.LoadModeSyntax) -} - -func run(pass *analysis.Pass) []result.Issue { - var res []result.Issue - - inspect := func(node ast.Node) bool { - funcDecl, ok := node.(*ast.FuncDecl) - if !ok { - return true - } - - ast.Inspect(funcDecl.Body, func(stmt ast.Node) bool { - if forStmt, ok := stmt.(*ast.ForStmt); ok { - ast.Inspect(forStmt.Body, func(stmt ast.Node) bool { - if selStmt, ok := stmt.(*ast.SelectStmt); ok { - ast.Inspect(selStmt.Body, func(stmt ast.Node) bool { - if brkStmt, ok := stmt.(*ast.BranchStmt); ok && brkStmt.Tok == token.BREAK { - pass.Reportf(stmt.Pos(), "break statement inside select statement inside for loop") - res = append(res, result.Issue{ - Pos: pass.Fset.Position(stmt.Pos()), - Text: "break statement inside select statement inside for loop", - FromLinter: linterName, - }) - return true - } - return true - }) - } - return true - }) - } - return true - }) - - return true - } - - for _, f := range pass.Files { - ast.Inspect(f, inspect) - } - - return res + ).WithLoadMode(goanalysis.LoadModeTypesInfo) } From e3ec2fec39ab170c5cc3b4c947740491b7f97038 Mon Sep 17 00:00:00 2001 From: rnben Date: Sat, 17 Aug 2024 22:56:13 +0800 Subject: [PATCH 3/4] fix: labelled break report error --- go.mod | 2 +- go.sum | 4 ++-- .../testdata/gobreakselectinfor.go | 13 +++++++++++-- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/go.mod b/go.mod index 2bd8d6ace3ce..a4cc7bc92a12 100644 --- a/go.mod +++ b/go.mod @@ -86,7 +86,7 @@ require ( github.com/pelletier/go-toml/v2 v2.2.2 github.com/polyfloyd/go-errorlint v1.6.0 github.com/quasilyte/go-ruleguard/dsl v0.3.22 - github.com/rnben/go-break-select-in-for v0.0.1 + github.com/rnben/go-break-select-in-for v0.0.2 github.com/ryancurrah/gomodguard v1.3.3 github.com/ryanrolds/sqlclosecheck v0.5.1 github.com/sanposhiho/wastedassign/v2 v2.0.7 diff --git a/go.sum b/go.sum index a3715df2b524..875c0cd2b30e 100644 --- a/go.sum +++ b/go.sum @@ -456,8 +456,8 @@ github.com/quasilyte/regex/syntax v0.0.0-20210819130434-b3f0c404a727 h1:TCg2WBOl github.com/quasilyte/regex/syntax v0.0.0-20210819130434-b3f0c404a727/go.mod h1:rlzQ04UMyJXu/aOvhd8qT+hvDrFpiwqp8MRXDY9szc0= github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567 h1:M8mH9eK4OUR4lu7Gd+PU1fV2/qnDNfzT635KRSObncs= github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567/go.mod h1:DWNGW8A4Y+GyBgPuaQJuWiy0XYftx4Xm/y5Jqk9I6VQ= -github.com/rnben/go-break-select-in-for v0.0.1 h1:nsl0IXc94wigYVwbecdeBYrCLookdYvYTgwn/LWyHmg= -github.com/rnben/go-break-select-in-for v0.0.1/go.mod h1:7JoY3ApLuYoE4iHy1U9X/v5ix10c/9Z54hpFoYhwmow= +github.com/rnben/go-break-select-in-for v0.0.2 h1:NqyhPhtf77PThT9tyFEnALafMx22lu9lVIWyujDD5xY= +github.com/rnben/go-break-select-in-for v0.0.2/go.mod h1:7JoY3ApLuYoE4iHy1U9X/v5ix10c/9Z54hpFoYhwmow= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= diff --git a/pkg/golinters/gobreakselectinfor/testdata/gobreakselectinfor.go b/pkg/golinters/gobreakselectinfor/testdata/gobreakselectinfor.go index 6d8f4fa26488..a84cef5bf313 100644 --- a/pkg/golinters/gobreakselectinfor/testdata/gobreakselectinfor.go +++ b/pkg/golinters/gobreakselectinfor/testdata/gobreakselectinfor.go @@ -1,8 +1,7 @@ //golangcitest:args -Egobreakselectinfor package testdata -func bad() { - var ch chan string +func bad(ch <-chan string) { for { select { case <-ch: @@ -10,3 +9,13 @@ func bad() { } } } + +func good(ch <-chan string) { +OUTER: + for { + select { + case <-ch: + break OUTER + } + } +} From 90ac8f0031d55db897e571af03bb5315c49b9d8f Mon Sep 17 00:00:00 2001 From: rnben Date: Sun, 18 Aug 2024 08:07:21 +0800 Subject: [PATCH 4/4] dev: update version --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index a4cc7bc92a12..e1a830de76ef 100644 --- a/go.mod +++ b/go.mod @@ -86,7 +86,7 @@ require ( github.com/pelletier/go-toml/v2 v2.2.2 github.com/polyfloyd/go-errorlint v1.6.0 github.com/quasilyte/go-ruleguard/dsl v0.3.22 - github.com/rnben/go-break-select-in-for v0.0.2 + github.com/rnben/go-break-select-in-for v0.1.0 github.com/ryancurrah/gomodguard v1.3.3 github.com/ryanrolds/sqlclosecheck v0.5.1 github.com/sanposhiho/wastedassign/v2 v2.0.7 diff --git a/go.sum b/go.sum index 875c0cd2b30e..5c2e90ae5682 100644 --- a/go.sum +++ b/go.sum @@ -456,8 +456,8 @@ github.com/quasilyte/regex/syntax v0.0.0-20210819130434-b3f0c404a727 h1:TCg2WBOl github.com/quasilyte/regex/syntax v0.0.0-20210819130434-b3f0c404a727/go.mod h1:rlzQ04UMyJXu/aOvhd8qT+hvDrFpiwqp8MRXDY9szc0= github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567 h1:M8mH9eK4OUR4lu7Gd+PU1fV2/qnDNfzT635KRSObncs= github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567/go.mod h1:DWNGW8A4Y+GyBgPuaQJuWiy0XYftx4Xm/y5Jqk9I6VQ= -github.com/rnben/go-break-select-in-for v0.0.2 h1:NqyhPhtf77PThT9tyFEnALafMx22lu9lVIWyujDD5xY= -github.com/rnben/go-break-select-in-for v0.0.2/go.mod h1:7JoY3ApLuYoE4iHy1U9X/v5ix10c/9Z54hpFoYhwmow= +github.com/rnben/go-break-select-in-for v0.1.0 h1:MSykRlo7dtIpS1fta0eRoDGdvF2/7Zz15+S+nSSV85c= +github.com/rnben/go-break-select-in-for v0.1.0/go.mod h1:7JoY3ApLuYoE4iHy1U9X/v5ix10c/9Z54hpFoYhwmow= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=