-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Description
Description
checklocks already provides annotations that let developers express which locks must be held when accessing a field. This works well when annotating fields directly. However, in the “mutex-hat” style (annotating the mutex with what it protects), a single mutex often guards several fields, and the current +checklocks:s.mu directive only supports naming one field at a time. This creates repetitive boilerplate.
This issue requests support for listing multiple protected fields in a single +checklocks:s.mu directive.
Motivation / Problem
In many codebases, the documentation pattern looks like:
- A mutex
muprotects several fields - The mutex (or its comment) is where that protection relationship is documented
This keeps “what the mutex protects” next to the mutex and avoids scattering lock info across many fields. It is similar to the go-critic’s mutexHat request:
- lint/mutexHat: add a comment for sync.Mutex about which fields are protected go-critic/go-critic#465
Today, with checklocks, expressing that s.mu protects multiple fields requires repeating the directive multiple times, or moving to field-level annotations everywhere. The repeated directive approach is noisy and scales poorly for structs with many guarded fields.
Is this feature related to a specific bug?
No
Do you have a specific solution in mind?
Allow // +checklocks:s.mu directives to name multiple fields in a single directive.
Proposed syntax options
Any of these would work; the main goal is “one directive → many fields”.
// Option A: comma-separated list
type s struct {
mu sync.Mutex
// +checklocks:s.mu=rateLimits,mostRecent,foo,bar
rateLimits Rate
mostRecent rateLimitCategory
foo int
bar string
}
// Option B: space-separated list
type s struct {
mu sync.Mutex
// +checklocks:s.mu rateLimits mostRecent foo bar
rateLimits Rate
mostRecent rateLimitCategory
foo int
bar string
}
// Option C: bracketed list
type s struct {
mu sync.Mutex
// +checklocks:s.mu=[rateLimits mostRecent foo bar]
rateLimits Rate
mostRecent rateLimitCategory
foo int
bar string
}(Whatever best matches existing directive parsing conventions.)
Expected behavior
• checklocks should treat each listed field as guarded by s.mu exactly as if each field had its own “field is guarded by mu” annotation.
• Multiple +checklocks:s.mu directives should merge (union) rather than override.
• Existing single-field usage must continue to work unchanged.
Example of desired semantics
Given:
type s struct {
mu sync.Mutex
// +checklocks:s.mu=rateLimits,mostRecent
rateLimits Rate
mostRecent rateLimitCategory
}Then accesses to rateLimits or mostRecent without holding mu should be flagged the same way as if the fields were individually annotated.
Why this helps
• Greatly reduces annotation noise when one mutex protects many fields.
• Encourages the “mutex-hat” style where the mutex declares what it guards.
• Improves readability for large structs by keeping protection relationships centralized.