Go linter that detects mixing pointer and value method receivers.
Mixing pointer and value receivers on the same type creates subtle, hard-to-detect bugs that can cause:
When you copy a struct with a mutex, you copy the mutex state, leading to race conditions:
typeRPCstruct {
mu sync.Mutex// This is the problem!resultintdonechanstruct{}
}
func (rpc*RPC) compute() {
rpc.mu.Lock()
deferrpc.mu.Unlock()
rpc.result=42
}
func (RPC) version() int { // Value receiver copies the mutex!return1
}
funcmain() {
rpc:=&RPC{done: make(chanstruct{})}
gorpc.compute() // Locks original mutexversion:=rpc.version() // Uses copied mutex - RACE!// ...
}Value receivers create copies, so modifications are lost:
typeCounterstruct {
valueint
}
func (c*Counter) Increment() { c.value++ } // pointer receiverfunc (cCounter) Reset() { c.value=0 } // value receiver - NO EFFECT!Mixed receivers make code behavior unpredictable and harder to reason about.
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.
# Standalone
go install github.com/raeperd/recvcheck/cmd/recvcheck@latest
# With golangci-lint (recommended)# Add to .golangci.yml:
linters:
enable:
- recvcheckrecvcheck ./...
# or
golangci-lint runOutput:
main.go:8:1: the methods of "RPC" use pointer receiver and non-pointer receiver
# .golangci.ymllinters-settings:
recvcheck:
# Disable default exclusions (MarshalJSON, etc.)disable-builtin: false# Custom exclusionsexclusions:
- "Server.Shutdown"# Specific method
- "*.String"# All String methodsUnmarshal methods are excluded by default as they must use pointer receivers:
*.UnmarshalText,*.UnmarshalJSON,*.UnmarshalYAML*.UnmarshalXML,*.UnmarshalBinary,*.GobDecode
❌ Bad - Mixed receivers:
func (u*User) SetName(namestring) { } // pointerfunc (uUser) GetName() string { } // value - inconsistent!✅ Good - Consistent receivers:
func (u*User) SetName(namestring) { } // pointerfunc (u*User) GetName() string { } // pointer - consistent!make test# Run tests
make lint # Run linter
make build # Build binaryMIT