Skip to content

Commit

Permalink
feat: add ExcludeMethod options with default (#8)
Browse files Browse the repository at this point in the history
Co-authored-by: Fernandez Ludovic <[email protected]>
  • Loading branch information
raeperd and ldez authored Nov 18, 2024
1 parent 110c625 commit 02533ff
Show file tree
Hide file tree
Showing 18 changed files with 246 additions and 10 deletions.
4 changes: 3 additions & 1 deletion .github/workflows/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,6 @@ jobs:
go-version-file: go.mod
- uses: golangci/golangci-lint-action@v6
with:
version: v1.61
version: v1.62
- run: make build
- run: go vet -vettool=./recvcheck ./...
10 changes: 10 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
linters:
enable:
- recvcheck

output:
show-stats: true
sort-results: true
sort-order:
- linter
- file
61 changes: 55 additions & 6 deletions analyzer.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,50 @@ import (
"golang.org/x/tools/go/ast/inspector"
)

var Analyzer = &analysis.Analyzer{
Name: "recvcheck",
Doc: "checks for receiver type consistency",
Run: run,
Requires: []*analysis.Analyzer{inspect.Analyzer},
// NewAnalyzer returns a new analyzer to check for receiver type consistency.
func NewAnalyzer(s Settings) *analysis.Analyzer {
// Default excludes for Marshal/Encode methods https://github.com/raeperd/recvcheck/issues/7
excludedMethods := map[string]struct{}{
"MarshalText": {},
"MarshalJSON": {},
"MarshalYAML": {},
"MarshalXML": {},
"MarshalBinary": {},
"GobEncode": {},
}

if s.DisableBuiltin {
excludedMethods = map[string]struct{}{}
}

a := &analyzer{excludedMethods: excludedMethods}

return &analysis.Analyzer{
Name: "recvcheck",
Doc: "checks for receiver type consistency",
Run: a.run,
Requires: []*analysis.Analyzer{inspect.Analyzer},
}
}

// Settings is the configuration for the analyzer.
type Settings struct {
// DisableBuiltin if true, disables the built-in method excludes.
// Built-in excluded methods:
// - "MarshalText"
// - "MarshalJSON"
// - "MarshalYAML"
// - "MarshalXML"
// - "MarshalBinary"
// - "GobEncode"
DisableBuiltin bool
}

func run(pass *analysis.Pass) (any, error) {
type analyzer struct {
excludedMethods map[string]struct{}
}

func (r *analyzer) run(pass *analysis.Pass) (any, error) {
inspector := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)

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

if r.isExcluded(funcDecl) {
return
}

var recv *ast.Ident
var isStar bool
switch recvType := funcDecl.Recv.List[0].Type.(type) {
Expand Down Expand Up @@ -61,6 +101,15 @@ func run(pass *analysis.Pass) (any, error) {
return nil, nil
}

func (r *analyzer) isExcluded(f *ast.FuncDecl) bool {
if f.Name == nil || f.Name.Name == "" {
return true
}

_, found := r.excludedMethods[f.Name.Name]
return found
}

type structType struct {
starUsed bool
typeUsed bool
Expand Down
26 changes: 25 additions & 1 deletion analyzer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,29 @@ import (
)

func TestAnalyzer(t *testing.T) {
analysistest.Run(t, analysistest.TestData(), recvcheck.Analyzer, "test")
testCases := []struct {
desc string
settings recvcheck.Settings
}{
{
desc: "basic",
settings: recvcheck.Settings{},
},
{
desc: "excluded",
settings: recvcheck.Settings{},
},
{
desc: "disablebuiltin",
settings: recvcheck.Settings{DisableBuiltin: true},
},
}

for _, test := range testCases {
t.Run(test.desc, func(t *testing.T) {
a := recvcheck.NewAnalyzer(test.settings)

analysistest.Run(t, analysistest.TestData(), a, test.desc)
})
}
}
13 changes: 12 additions & 1 deletion cmd/recvcheck/main.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,21 @@
package main

import (
"flag"

"github.com/raeperd/recvcheck"
"golang.org/x/tools/go/analysis/singlechecker"
)

func main() {
singlechecker.Main(recvcheck.Analyzer)
var noBuiltinExcludeMethod bool

flag.BoolVar(&noBuiltinExcludeMethod, "no-builtin-exclude-method", false,
`disables the default exclude methods such as "MarshalJSON"`)

setting := recvcheck.Settings{
DisableBuiltin: noBuiltinExcludeMethod,
}

singlechecker.Main(recvcheck.NewAnalyzer(setting))
}
2 changes: 1 addition & 1 deletion testdata/src/test/rpc.go → testdata/src/basic/rpc.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package test
package basic

import (
"time"
Expand Down
11 changes: 11 additions & 0 deletions testdata/src/disablebuiltin/binary.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package disablebuiltin

type Binary struct{} // want `the methods of "Binary" use pointer receiver and non-pointer receiver.`

func (b Binary) MarshalBinary() ([]byte, error) {
panic("not implemented")
}

func (b *Binary) UnmarshalBinary(data []byte) error {
panic("not implemented")
}
11 changes: 11 additions & 0 deletions testdata/src/disablebuiltin/gob.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package disablebuiltin

type Gob struct{} // want `the methods of "Gob" use pointer receiver and non-pointer receiver.`

func (g Gob) GobEncode() ([]byte, error) {
panic("not implemented")
}

func (g *Gob) GobDecode(data []byte) error {
panic("not implemented")
}
11 changes: 11 additions & 0 deletions testdata/src/disablebuiltin/json.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package disablebuiltin

type JSON struct{} // want `the methods of "JSON" use pointer receiver and non-pointer receiver.`

func (j JSON) MarshalJSON() ([]byte, error) {
panic("not implemented")
}

func (j *JSON) UnmarshalJSON(b []byte) error {
panic("not implemented")
}
11 changes: 11 additions & 0 deletions testdata/src/disablebuiltin/text.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package disablebuiltin

type Text struct{} // want `the methods of "Text" use pointer receiver and non-pointer receiver.`

func (t Text) MarshalText() ([]byte, error) {
panic("not implemented")
}

func (t *Text) UnmarshalText(b []byte) error {
panic("not implemented")
}
13 changes: 13 additions & 0 deletions testdata/src/disablebuiltin/xml.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package disablebuiltin

import "encoding/xml"

type XML struct{} // want `the methods of "XML" use pointer receiver and non-pointer receiver.`

func (x XML) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
panic("not implemented")
}

func (x *XML) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
panic("not implemented")
}
13 changes: 13 additions & 0 deletions testdata/src/disablebuiltin/yaml.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package disablebuiltin

type Node struct{}

type YAML struct{} // want `the methods of "YAML" use pointer receiver and non-pointer receiver.`

func (j YAML) MarshalYAML() (any, error) {
panic("not implemented")
}

func (j *YAML) UnmarshalYAML(value *Node) error {
panic("not implemented")
}
11 changes: 11 additions & 0 deletions testdata/src/excluded/binary.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package excluded

type Binary struct{}

func (b Binary) MarshalBinary() ([]byte, error) {
panic("not implemented")
}

func (b *Binary) UnmarshalBinary(data []byte) error {
panic("not implemented")
}
11 changes: 11 additions & 0 deletions testdata/src/excluded/gob.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package excluded

type Gob struct{}

func (g Gob) GobEncode() ([]byte, error) {
panic("not implemented")
}

func (g *Gob) GobDecode(data []byte) error {
panic("not implemented")
}
11 changes: 11 additions & 0 deletions testdata/src/excluded/json.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package excluded

type JSON struct{}

func (j JSON) MarshalJSON() ([]byte, error) {
panic("not implemented")
}

func (j *JSON) UnmarshalJSON(b []byte) error {
panic("not implemented")
}
11 changes: 11 additions & 0 deletions testdata/src/excluded/text.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package excluded

type Text struct{}

func (t Text) MarshalText() ([]byte, error) {
panic("not implemented")
}

func (t *Text) UnmarshalText(b []byte) error {
panic("not implemented")
}
13 changes: 13 additions & 0 deletions testdata/src/excluded/xml.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package excluded

import "encoding/xml"

type XML struct{}

func (x XML) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
panic("not implemented")
}

func (x *XML) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
panic("not implemented")
}
13 changes: 13 additions & 0 deletions testdata/src/excluded/yaml.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package excluded

type Node struct{}

type YAML struct{}

func (j YAML) MarshalYAML() (any, error) {
panic("not implemented")
}

func (j *YAML) UnmarshalYAML(value *Node) error {
panic("not implemented")
}

0 comments on commit 02533ff

Please sign in to comment.