Skip to content

raeperd/recvcheck

Repository files navigation

recvcheck

Build Status Go Report Card Coverage Status Go Reference

Go linter that detects mixing pointer and value method receivers.

Why

Mixing pointer and value receivers on the same type creates subtle, hard-to-detect bugs that can cause:

🐛 Data Races

When you copy a struct with a mutex, you copy the mutex state, leading to race conditions:

type RPC struct {
    mu     sync.Mutex  // This is the problem!
    result int
    done   chan struct{}
}

func (rpc *RPC) compute() {
    rpc.mu.Lock()
    defer rpc.mu.Unlock()
    rpc.result = 42
}

func (RPC) version() int {  // Value receiver copies the mutex!
    return 1
}

func main() {
    rpc := &RPC{done: make(chan struct{})}
    go rpc.compute()         // Locks original mutex
    version := rpc.version() // Uses copied mutex - RACE!
    // ...
}

🚨 Silent Bugs

Value receivers create copies, so modifications are lost:

type Counter struct {
    value int
}

func (c *Counter) Increment() { c.value++ }  // pointer receiver
func (c Counter) Reset() { c.value = 0 }     // value receiver - NO EFFECT!

🤔 Developer Confusion

Mixed receivers make code behavior unpredictable and harder to reason about.

✅ The Solution

Consistency is key: Go's official guidance says Don't mix receiver types. Choose either pointers or values for all methods on a type.

recvcheck automatically detects these issues before they reach production.

Installation

# Standalone
go install github.com/raeperd/recvcheck/cmd/recvcheck@latest

# With golangci-lint (recommended)
# Add to .golangci.yml:
linters:
  enable:
    - recvcheck

Usage

recvcheck ./...
# or
golangci-lint run

Output:

main.go:8:1: the methods of "RPC" use pointer receiver and non-pointer receiver

Configuration

# .golangci.yml
linters-settings:
  recvcheck:
    # Disable default exclusions (MarshalJSON, etc.)
    disable-builtin: false
    
    # Custom exclusions
    exclusions:
      - "Server.Shutdown"   # Specific method
      - "*.String"          # All String methods

Default Exclusions

Marshal methods are excluded by default as they commonly use value receivers:

  • *.MarshalText, *.MarshalJSON, *.MarshalYAML
  • *.MarshalXML, *.MarshalBinary, *.GobEncode

Examples

Bad - Mixed receivers:

func (u *User) SetName(name string) { }  // pointer
func (u User) GetName() string { }       // value - inconsistent!

Good - Consistent receivers:

func (u *User) SetName(name string) { }  // pointer
func (u *User) GetName() string { }      // pointer - consistent!

Contributing

make test  # Run tests
make lint  # Run linter
make build # Build binary

License

MIT

About

Golang linter checks for receiver type consistency

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors 3

  •  
  •  
  •