Skip to content

Commit 02533ff

Browse files
raeperdldez
andauthored
feat: add ExcludeMethod options with default (#8)
Co-authored-by: Fernandez Ludovic <[email protected]>
1 parent 110c625 commit 02533ff

File tree

18 files changed

+246
-10
lines changed

18 files changed

+246
-10
lines changed

.github/workflows/build.yaml

+3-1
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,6 @@ jobs:
3434
go-version-file: go.mod
3535
- uses: golangci/golangci-lint-action@v6
3636
with:
37-
version: v1.61
37+
version: v1.62
38+
- run: make build
39+
- run: go vet -vettool=./recvcheck ./...

.golangci.yml

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
linters:
2+
enable:
3+
- recvcheck
4+
5+
output:
6+
show-stats: true
7+
sort-results: true
8+
sort-order:
9+
- linter
10+
- file

analyzer.go

+55-6
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,50 @@ import (
88
"golang.org/x/tools/go/ast/inspector"
99
)
1010

11-
var Analyzer = &analysis.Analyzer{
12-
Name: "recvcheck",
13-
Doc: "checks for receiver type consistency",
14-
Run: run,
15-
Requires: []*analysis.Analyzer{inspect.Analyzer},
11+
// NewAnalyzer returns a new analyzer to check for receiver type consistency.
12+
func NewAnalyzer(s Settings) *analysis.Analyzer {
13+
// Default excludes for Marshal/Encode methods https://github.com/raeperd/recvcheck/issues/7
14+
excludedMethods := map[string]struct{}{
15+
"MarshalText": {},
16+
"MarshalJSON": {},
17+
"MarshalYAML": {},
18+
"MarshalXML": {},
19+
"MarshalBinary": {},
20+
"GobEncode": {},
21+
}
22+
23+
if s.DisableBuiltin {
24+
excludedMethods = map[string]struct{}{}
25+
}
26+
27+
a := &analyzer{excludedMethods: excludedMethods}
28+
29+
return &analysis.Analyzer{
30+
Name: "recvcheck",
31+
Doc: "checks for receiver type consistency",
32+
Run: a.run,
33+
Requires: []*analysis.Analyzer{inspect.Analyzer},
34+
}
35+
}
36+
37+
// Settings is the configuration for the analyzer.
38+
type Settings struct {
39+
// DisableBuiltin if true, disables the built-in method excludes.
40+
// Built-in excluded methods:
41+
// - "MarshalText"
42+
// - "MarshalJSON"
43+
// - "MarshalYAML"
44+
// - "MarshalXML"
45+
// - "MarshalBinary"
46+
// - "GobEncode"
47+
DisableBuiltin bool
1648
}
1749

18-
func run(pass *analysis.Pass) (any, error) {
50+
type analyzer struct {
51+
excludedMethods map[string]struct{}
52+
}
53+
54+
func (r *analyzer) run(pass *analysis.Pass) (any, error) {
1955
inspector := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
2056

2157
structs := map[string]*structType{}
@@ -25,6 +61,10 @@ func run(pass *analysis.Pass) (any, error) {
2561
return
2662
}
2763

64+
if r.isExcluded(funcDecl) {
65+
return
66+
}
67+
2868
var recv *ast.Ident
2969
var isStar bool
3070
switch recvType := funcDecl.Recv.List[0].Type.(type) {
@@ -61,6 +101,15 @@ func run(pass *analysis.Pass) (any, error) {
61101
return nil, nil
62102
}
63103

104+
func (r *analyzer) isExcluded(f *ast.FuncDecl) bool {
105+
if f.Name == nil || f.Name.Name == "" {
106+
return true
107+
}
108+
109+
_, found := r.excludedMethods[f.Name.Name]
110+
return found
111+
}
112+
64113
type structType struct {
65114
starUsed bool
66115
typeUsed bool

analyzer_test.go

+25-1
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,29 @@ import (
88
)
99

1010
func TestAnalyzer(t *testing.T) {
11-
analysistest.Run(t, analysistest.TestData(), recvcheck.Analyzer, "test")
11+
testCases := []struct {
12+
desc string
13+
settings recvcheck.Settings
14+
}{
15+
{
16+
desc: "basic",
17+
settings: recvcheck.Settings{},
18+
},
19+
{
20+
desc: "excluded",
21+
settings: recvcheck.Settings{},
22+
},
23+
{
24+
desc: "disablebuiltin",
25+
settings: recvcheck.Settings{DisableBuiltin: true},
26+
},
27+
}
28+
29+
for _, test := range testCases {
30+
t.Run(test.desc, func(t *testing.T) {
31+
a := recvcheck.NewAnalyzer(test.settings)
32+
33+
analysistest.Run(t, analysistest.TestData(), a, test.desc)
34+
})
35+
}
1236
}

cmd/recvcheck/main.go

+12-1
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,21 @@
11
package main
22

33
import (
4+
"flag"
5+
46
"github.com/raeperd/recvcheck"
57
"golang.org/x/tools/go/analysis/singlechecker"
68
)
79

810
func main() {
9-
singlechecker.Main(recvcheck.Analyzer)
11+
var noBuiltinExcludeMethod bool
12+
13+
flag.BoolVar(&noBuiltinExcludeMethod, "no-builtin-exclude-method", false,
14+
`disables the default exclude methods such as "MarshalJSON"`)
15+
16+
setting := recvcheck.Settings{
17+
DisableBuiltin: noBuiltinExcludeMethod,
18+
}
19+
20+
singlechecker.Main(recvcheck.NewAnalyzer(setting))
1021
}

testdata/src/test/rpc.go renamed to testdata/src/basic/rpc.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package test
1+
package basic
22

33
import (
44
"time"

testdata/src/disablebuiltin/binary.go

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package disablebuiltin
2+
3+
type Binary struct{} // want `the methods of "Binary" use pointer receiver and non-pointer receiver.`
4+
5+
func (b Binary) MarshalBinary() ([]byte, error) {
6+
panic("not implemented")
7+
}
8+
9+
func (b *Binary) UnmarshalBinary(data []byte) error {
10+
panic("not implemented")
11+
}

testdata/src/disablebuiltin/gob.go

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package disablebuiltin
2+
3+
type Gob struct{} // want `the methods of "Gob" use pointer receiver and non-pointer receiver.`
4+
5+
func (g Gob) GobEncode() ([]byte, error) {
6+
panic("not implemented")
7+
}
8+
9+
func (g *Gob) GobDecode(data []byte) error {
10+
panic("not implemented")
11+
}

testdata/src/disablebuiltin/json.go

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package disablebuiltin
2+
3+
type JSON struct{} // want `the methods of "JSON" use pointer receiver and non-pointer receiver.`
4+
5+
func (j JSON) MarshalJSON() ([]byte, error) {
6+
panic("not implemented")
7+
}
8+
9+
func (j *JSON) UnmarshalJSON(b []byte) error {
10+
panic("not implemented")
11+
}

testdata/src/disablebuiltin/text.go

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package disablebuiltin
2+
3+
type Text struct{} // want `the methods of "Text" use pointer receiver and non-pointer receiver.`
4+
5+
func (t Text) MarshalText() ([]byte, error) {
6+
panic("not implemented")
7+
}
8+
9+
func (t *Text) UnmarshalText(b []byte) error {
10+
panic("not implemented")
11+
}

testdata/src/disablebuiltin/xml.go

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package disablebuiltin
2+
3+
import "encoding/xml"
4+
5+
type XML struct{} // want `the methods of "XML" use pointer receiver and non-pointer receiver.`
6+
7+
func (x XML) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
8+
panic("not implemented")
9+
}
10+
11+
func (x *XML) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
12+
panic("not implemented")
13+
}

testdata/src/disablebuiltin/yaml.go

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package disablebuiltin
2+
3+
type Node struct{}
4+
5+
type YAML struct{} // want `the methods of "YAML" use pointer receiver and non-pointer receiver.`
6+
7+
func (j YAML) MarshalYAML() (any, error) {
8+
panic("not implemented")
9+
}
10+
11+
func (j *YAML) UnmarshalYAML(value *Node) error {
12+
panic("not implemented")
13+
}

testdata/src/excluded/binary.go

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package excluded
2+
3+
type Binary struct{}
4+
5+
func (b Binary) MarshalBinary() ([]byte, error) {
6+
panic("not implemented")
7+
}
8+
9+
func (b *Binary) UnmarshalBinary(data []byte) error {
10+
panic("not implemented")
11+
}

testdata/src/excluded/gob.go

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package excluded
2+
3+
type Gob struct{}
4+
5+
func (g Gob) GobEncode() ([]byte, error) {
6+
panic("not implemented")
7+
}
8+
9+
func (g *Gob) GobDecode(data []byte) error {
10+
panic("not implemented")
11+
}

testdata/src/excluded/json.go

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package excluded
2+
3+
type JSON struct{}
4+
5+
func (j JSON) MarshalJSON() ([]byte, error) {
6+
panic("not implemented")
7+
}
8+
9+
func (j *JSON) UnmarshalJSON(b []byte) error {
10+
panic("not implemented")
11+
}

testdata/src/excluded/text.go

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package excluded
2+
3+
type Text struct{}
4+
5+
func (t Text) MarshalText() ([]byte, error) {
6+
panic("not implemented")
7+
}
8+
9+
func (t *Text) UnmarshalText(b []byte) error {
10+
panic("not implemented")
11+
}

testdata/src/excluded/xml.go

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package excluded
2+
3+
import "encoding/xml"
4+
5+
type XML struct{}
6+
7+
func (x XML) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
8+
panic("not implemented")
9+
}
10+
11+
func (x *XML) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
12+
panic("not implemented")
13+
}

testdata/src/excluded/yaml.go

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package excluded
2+
3+
type Node struct{}
4+
5+
type YAML struct{}
6+
7+
func (j YAML) MarshalYAML() (any, error) {
8+
panic("not implemented")
9+
}
10+
11+
func (j *YAML) UnmarshalYAML(value *Node) error {
12+
panic("not implemented")
13+
}

0 commit comments

Comments
 (0)