Skip to content

Commit

Permalink
refactor (rule/unused-receiver): replace AST walker by iteration over…
Browse files Browse the repository at this point in the history
… declarations
  • Loading branch information
chavacava committed Dec 4, 2024
1 parent 09fb350 commit 66affcc
Showing 1 changed file with 22 additions and 45 deletions.
67 changes: 22 additions & 45 deletions rule/unused_receiver.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,74 +51,51 @@ func (r *UnusedReceiverRule) Apply(file *lint.File, args lint.Arguments) []lint.
r.configureOnce.Do(func() { r.configure(args) })
var failures []lint.Failure

onFailure := func(failure lint.Failure) {
failures = append(failures, failure)
}

w := lintUnusedReceiverRule{
onFailure: onFailure,
allowRegex: r.allowRegex,
failureMsg: r.failureMsg,
}

ast.Walk(w, file.AST)

return failures
}

// Name returns the rule name.
func (*UnusedReceiverRule) Name() string {
return "unused-receiver"
}

type lintUnusedReceiverRule struct {
onFailure func(lint.Failure)
allowRegex *regexp.Regexp
failureMsg string
}

func (w lintUnusedReceiverRule) Visit(node ast.Node) ast.Visitor {
switch n := node.(type) {
case *ast.FuncDecl:
if n.Recv == nil {
return nil // skip this func decl, not a method
for _, decl := range file.AST.Decls {
funcDecl, ok := decl.(*ast.FuncDecl)
isMethod := ok && funcDecl.Recv != nil
if !isMethod {
continue
}

rec := n.Recv.List[0] // safe to access only the first (unique) element of the list
rec := funcDecl.Recv.List[0] // safe to access only the first (unique) element of the list
if len(rec.Names) < 1 {
return nil // the receiver is anonymous: func (aType) Foo(...) ...
continue // the receiver is anonymous: func (aType) Foo(...) ...
}

recID := rec.Names[0]
if recID.Name == "_" {
return nil // the receiver is already named _
continue // the receiver is already named _
}

if w.allowRegex != nil && w.allowRegex.FindStringIndex(recID.Name) != nil {
return nil
if r.allowRegex != nil && r.allowRegex.FindStringIndex(recID.Name) != nil {
continue
}

// inspect the func body looking for references to the receiver id
fselect := func(n ast.Node) bool {
selectReceiverUses := func(n ast.Node) bool {
ident, isAnID := n.(*ast.Ident)

return isAnID && ident.Obj == recID.Obj
}
refs2recID := pick(n.Body, fselect)
receiverUses := pick(funcDecl.Body, selectReceiverUses)

if len(refs2recID) > 0 {
return nil // the receiver is referenced in the func body
if len(receiverUses) > 0 {
continue // the receiver is referenced in the func body
}

w.onFailure(lint.Failure{
failures = append(failures, lint.Failure{
Confidence: 1,
Node: recID,
Category: "bad practice",
Failure: fmt.Sprintf(w.failureMsg, recID.Name),
Failure: fmt.Sprintf(r.failureMsg, recID.Name),
})

return nil // full method body already inspected
}

return w
return failures
}

// Name returns the rule name.
func (*UnusedReceiverRule) Name() string {
return "unused-receiver"
}

0 comments on commit 66affcc

Please sign in to comment.