Skip to content

Commit 4dc99bb

Browse files
rnbenrnben
authored andcommitted
Initial commit
0 parents  commit 4dc99bb

File tree

9 files changed

+153
-0
lines changed

9 files changed

+153
-0
lines changed

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2024 rnben
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# go-break-select-in-for
2+
3+
The Go linter go-break-select-in-for checks that break statement inside select statement inside for loop.
4+
5+
For example, in myFunc the break may want to exit the outer for loop, but it doesn't work as expected.
6+
7+
```go
8+
func myFunc() {
9+
for {
10+
select {
11+
case <-ch:
12+
break // should be careful
13+
}
14+
}
15+
}
16+
```

cmd/go-break-select-in-for/main.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package main
2+
3+
import (
4+
"github.com/rnben/go-break-select-in-for/pkg/analyzer"
5+
"golang.org/x/tools/go/analysis/singlechecker"
6+
)
7+
8+
func main() {
9+
singlechecker.Main(analyzer.Analyzer)
10+
}

example.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package main
2+
3+
func bad() {
4+
var ch chan string
5+
for {
6+
select {
7+
case <-ch:
8+
break
9+
}
10+
}
11+
}

go.mod

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
module github.com/rnben/go-break-select-in-for
2+
3+
go 1.18
4+
5+
require golang.org/x/tools v0.24.0
6+
7+
require (
8+
golang.org/x/mod v0.20.0 // indirect
9+
golang.org/x/sync v0.8.0 // indirect
10+
)

go.sum

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
2+
golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0=
3+
golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
4+
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
5+
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
6+
golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24=
7+
golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ=

pkg/analyzer/analyzer.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package analyzer
2+
3+
import (
4+
"go/ast"
5+
"go/token"
6+
7+
"golang.org/x/tools/go/analysis"
8+
)
9+
10+
var Analyzer = &analysis.Analyzer{
11+
Name: "gobreakselectinfor",
12+
Doc: "Checks that using break statement inside select inside for loop",
13+
Run: run,
14+
}
15+
16+
func run(pass *analysis.Pass) (interface{}, error) {
17+
inspect := func(node ast.Node) bool {
18+
funcDecl, ok := node.(*ast.FuncDecl)
19+
if !ok {
20+
return true
21+
}
22+
23+
ast.Inspect(funcDecl.Body, func(stmt ast.Node) bool {
24+
if forStmt, ok := stmt.(*ast.ForStmt); ok {
25+
ast.Inspect(forStmt.Body, func(stmt ast.Node) bool {
26+
if selStmt, ok := stmt.(*ast.SelectStmt); ok {
27+
ast.Inspect(selStmt.Body, func(stmt ast.Node) bool {
28+
if brkStmt, ok := stmt.(*ast.BranchStmt); ok && brkStmt.Tok == token.BREAK {
29+
pass.Reportf(stmt.Pos(), "break statement inside select statement inside for loop")
30+
return true
31+
}
32+
return true
33+
})
34+
}
35+
return true
36+
})
37+
}
38+
return true
39+
})
40+
41+
return true
42+
}
43+
44+
for _, f := range pass.Files {
45+
ast.Inspect(f, inspect)
46+
}
47+
return nil, nil
48+
}

pkg/analyzer/analyzer_test.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package analyzer
2+
3+
import (
4+
"os"
5+
"path/filepath"
6+
"testing"
7+
8+
"golang.org/x/tools/go/analysis/analysistest"
9+
)
10+
11+
func TestAll(t *testing.T) {
12+
wd, err := os.Getwd()
13+
if err != nil {
14+
t.Fatalf("Failed to get wd: %s", err)
15+
}
16+
17+
testdata := filepath.Join(filepath.Dir(filepath.Dir(wd)), "testdata")
18+
analysistest.Run(t, testdata, Analyzer, "p")
19+
}

testdata/src/p/p.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package p
2+
3+
func bad() {
4+
var ch chan string
5+
for {
6+
select {
7+
case <-ch:
8+
break // want "break statement inside select statement inside for loop"
9+
}
10+
}
11+
}

0 commit comments

Comments
 (0)