Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 21 additions & 5 deletions analyzer.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package recvcheck

import (
"fmt"
"go/ast"

"golang.org/x/tools/go/analysis"
Expand Down Expand Up @@ -87,13 +88,28 @@ func (r *analyzer) run(pass *analysis.Pass) (any, error) {
if isStar {
st.starUsed = true
} else {
st.typeUsed = true
st.valueRecvTypes = append(st.valueRecvTypes, recv)
}
})

for recv, st := range structs {
if st.starUsed && st.typeUsed {
pass.Reportf(pass.Pkg.Scope().Lookup(recv).Pos(), "the methods of %q use pointer receiver and non-pointer receiver.", recv)
if st.starUsed && len(st.valueRecvTypes) > 0 {
edits := make([]analysis.TextEdit, len(st.valueRecvTypes))
for i, ident := range st.valueRecvTypes {
edits[i] = analysis.TextEdit{
Pos: ident.Pos(),
End: ident.Pos(),
NewText: []byte("*"),
}
}
pass.Report(analysis.Diagnostic{
Pos: pass.Pkg.Scope().Lookup(recv).Pos(),
Message: fmt.Sprintf("the methods of %q use pointer receiver and non-pointer receiver.", recv),
SuggestedFixes: []analysis.SuggestedFix{{
Message: fmt.Sprintf("use pointer receiver for %q", recv),
TextEdits: edits,
}},
})
Comment thread
raeperd marked this conversation as resolved.
}
}

Expand All @@ -116,8 +132,8 @@ func (r *analyzer) isExcluded(recv *ast.Ident, f *ast.FuncDecl) bool {
}

type structType struct {
starUsed bool
typeUsed bool
starUsed bool
valueRecvTypes []*ast.Ident
}

func recvTypeIdent(r ast.Expr) (*ast.Ident, bool) {
Expand Down
2 changes: 1 addition & 1 deletion analyzer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ func TestAnalyzer(t *testing.T) {
t.Run(test.desc, func(t *testing.T) {
a := recvcheck.NewAnalyzer(test.settings)

analysistest.Run(t, analysistest.TestData(), a, test.desc)
analysistest.RunWithSuggestedFixes(t, analysistest.TestData(), a, test.desc)
})
}
}
33 changes: 33 additions & 0 deletions testdata/src/basic/rpc.go.golden
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package basic

import (
"time"
)

type RPC struct { // want `the methods of "RPC" use pointer receiver and non-pointer receiver.`
result int
done chan struct{}
}

func (rpc *RPC) compute() {
time.Sleep(time.Second) // strenuous computation intensifies
rpc.result = 42
close(rpc.done)
}

func (*RPC) version() int {
return 1 // never going to need to change this
}

// Following main function cause data race error
// reference: https://dave.cheney.net/2015/11/18/wednesday-pop-quiz-spot-the-race
// func main() {
// rpc := &RPC{done: make(chan struct{})}
//
// go rpc.compute() // kick off computation in the background
// version := rpc.version() // grab some other information while we're waiting
// <-rpc.done // wait for computation to finish
// result := rpc.result
//
// fmt.Printf("RPC computation complete, result: %d, version: %d\n", result, version)
// }
11 changes: 11 additions & 0 deletions testdata/src/disablebuiltin/binary.go.golden
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.golden
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.golden
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.golden
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.golden
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.golden
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")
}
Loading