[teamai] Push 87 resource(s) from XingfenD
This commit is contained in:
@@ -0,0 +1 @@
|
||||
XingfenD
|
||||
@@ -0,0 +1,157 @@
|
||||
---
|
||||
name: golang-lint
|
||||
description: "Linting best practices and golangci-lint configuration for Golang projects — running linters, configuring .golangci.yml, suppressing warnings with nolint directives, interpreting lint output, and selecting linters. Use when configuring golangci-lint, asking about lint warnings or nolint suppressions, setting up code quality tooling, or choosing linters. Also use when the user mentions golangci-lint, go vet, staticcheck, or revive."
|
||||
user-invocable: true
|
||||
license: MIT
|
||||
compatibility: Designed for Claude Code or similar AI coding agents, and for projects using Golang.
|
||||
metadata:
|
||||
author: samber
|
||||
version: "1.3.0"
|
||||
openclaw:
|
||||
emoji: "🧹"
|
||||
homepage: https://github.com/samber/cc-skills-golang
|
||||
requires:
|
||||
bins:
|
||||
- go
|
||||
- golangci-lint
|
||||
install:
|
||||
- kind: brew
|
||||
formula: golangci-lint
|
||||
bins: [golangci-lint]
|
||||
allowed-tools: Read Edit Write Glob Grep Bash(go:*) Bash(golangci-lint:*) Bash(git:*) Agent
|
||||
---
|
||||
|
||||
**Persona:** You are a Go code quality engineer. You treat linting as a first-class part of the development workflow — not a post-hoc cleanup step.
|
||||
|
||||
**Orchestration mode:** Use `ultracode` when adopting linting on a legacy codebase — orchestrate the five sub-agents described in the "Parallelizing Legacy Codebase Cleanup" section (auto-fix, security linters, error handling, style/formatting, code quality) so independent linter categories are fixed concurrently.
|
||||
|
||||
**Modes:**
|
||||
|
||||
- **Setup mode** — configuring `.golangci.yml`, choosing linters, enabling CI: follow the configuration and workflow sections sequentially.
|
||||
- **Coding mode** — writing new Go code: launch a background agent running `golangci-lint run --fix` on the modified files only while the main agent continues implementing the feature; surface results when it completes.
|
||||
- **Interpret/fix mode** — reading lint output, suppressing warnings, fixing issues on existing code: start from "Interpreting Output" and "Suppressing Lint Warnings"; use parallel sub-agents for large-scale legacy cleanup.
|
||||
|
||||
**Dependencies:**
|
||||
|
||||
- golangci-lint: `go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest`
|
||||
|
||||
# Go Linting
|
||||
|
||||
## Overview
|
||||
|
||||
`golangci-lint` is the standard Go linting tool. It aggregates 100+ linters into a single binary, runs them in parallel, and provides a unified configuration format. Run it frequently during development and always in CI.
|
||||
|
||||
Every Go project MUST have a `.golangci.yml` — it is the **source of truth** for which linters are enabled and how they are configured. See the [recommended configuration](./assets/.golangci.yml) for a production-ready setup with 48 linters enabled.
|
||||
|
||||
## Quick Reference
|
||||
|
||||
```bash
|
||||
# Run all configured linters
|
||||
golangci-lint run ./...
|
||||
|
||||
# Auto-fix issues where possible
|
||||
golangci-lint run --fix ./...
|
||||
|
||||
# Format code (golangci-lint v2+)
|
||||
golangci-lint fmt ./...
|
||||
|
||||
# Run a single linter only
|
||||
golangci-lint run --enable-only govet ./...
|
||||
|
||||
# List all available linters
|
||||
golangci-lint linters
|
||||
|
||||
# Verbose output with timing info
|
||||
golangci-lint run --verbose ./...
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
The [recommended .golangci.yml](./assets/.golangci.yml) provides a production-ready setup with 33 linters. For configuration details, linter categories, and per-linter descriptions, see the **[linter reference](./references/linter-reference.md)** — which linters check for what (correctness, style, complexity, performance, security), descriptions of all 33+ linters, and when each one is useful.
|
||||
|
||||
## Suppressing Lint Warnings
|
||||
|
||||
Use `//nolint` directives sparingly — fix the root cause first.
|
||||
|
||||
```go
|
||||
// Good: specific linter + justification
|
||||
//nolint:errcheck // fire-and-forget logging, error is not actionable
|
||||
_ = logger.Sync()
|
||||
|
||||
// Bad: blanket suppression without reason
|
||||
//nolint
|
||||
_ = logger.Sync()
|
||||
```
|
||||
|
||||
Rules:
|
||||
|
||||
1. **//nolint directives MUST specify the linter name**: `//nolint:errcheck` not `//nolint`
|
||||
2. **//nolint directives MUST include a justification comment**: `//nolint:errcheck // reason`
|
||||
3. **The `nolintlint` linter enforces both rules above** — it flags bare `//nolint` and missing reasons
|
||||
4. **NEVER suppress security linters** (gosec, bodyclose, sqlclosecheck) without a very strong reason
|
||||
|
||||
For comprehensive patterns and examples, see **[nolint directives](./references/nolint-directives.md)** — when to suppress, how to write justifications, patterns for per-line vs per-function suppression, and anti-patterns.
|
||||
|
||||
## Development Workflow
|
||||
|
||||
1. **Linters SHOULD be run after every significant change**: `golangci-lint run ./...`
|
||||
2. **Auto-fix what you can**: `golangci-lint run --fix ./...`
|
||||
3. **Format before committing**: `golangci-lint fmt ./...`
|
||||
4. **Incremental adoption on legacy code**: set `issues.new-from-rev` in `.golangci.yml` to only lint new/changed code, then gradually clean up old code
|
||||
|
||||
Makefile targets (recommended):
|
||||
|
||||
```makefile
|
||||
lint:
|
||||
golangci-lint run ./...
|
||||
|
||||
lint-fix:
|
||||
golangci-lint run --fix ./...
|
||||
|
||||
fmt:
|
||||
golangci-lint fmt ./...
|
||||
```
|
||||
|
||||
For CI pipeline setup (GitHub Actions with `golangci-lint-action`), see the `samber/cc-skills-golang@golang-continuous-integration` skill.
|
||||
|
||||
## Interpreting Output
|
||||
|
||||
Each issue follows this format:
|
||||
|
||||
```
|
||||
path/to/file.go:42:10: message describing the issue (linter-name)
|
||||
```
|
||||
|
||||
The linter name in parentheses tells you which linter flagged it. Use this to:
|
||||
|
||||
- Look up the linter in the [reference](./references/linter-reference.md) to understand what it checks
|
||||
- Suppress with `//nolint:linter-name // reason` if it's a false positive
|
||||
- Use `golangci-lint run --verbose` for additional context and timing
|
||||
|
||||
## Common Issues
|
||||
|
||||
| Problem | Solution |
|
||||
| --- | --- |
|
||||
| "deadline exceeded" | Set or increase `run.timeout` in `.golangci.yml`; golangci-lint v2 defaults to no timeout (`0`) |
|
||||
| Too many issues on legacy code | Set `issues.new-from-rev: HEAD~1` to lint only new code |
|
||||
| Linter not found | Check `golangci-lint linters` — linter may need a newer version |
|
||||
| Conflicts between linters | Disable the less useful one with a comment explaining why |
|
||||
| v1 config errors after upgrade | Run `golangci-lint migrate` to convert config format |
|
||||
| Slow on large repos | Reduce `run.concurrency` or exclude paths with `linters.exclusions.paths` / `formatters.exclusions.paths` |
|
||||
|
||||
## Parallelizing Legacy Codebase Cleanup
|
||||
|
||||
When adopting linting on a legacy codebase, use up to 5 parallel sub-agents (via the Agent tool) to fix independent linter categories simultaneously:
|
||||
|
||||
- Sub-agent 1: Run `golangci-lint run --fix ./...` for auto-fixable issues
|
||||
- Sub-agent 2: Fix security linter findings (bodyclose, sqlclosecheck, gosec)
|
||||
- Sub-agent 3: Fix error handling issues (errcheck, nilerr, wrapcheck)
|
||||
- Sub-agent 4: Fix style and formatting (gofumpt, goimports, revive)
|
||||
- Sub-agent 5: Fix code quality (gocritic, unused, ineffassign)
|
||||
|
||||
## Cross-References
|
||||
|
||||
- → See `samber/cc-skills-golang@golang-continuous-integration` skill for CI pipeline with golangci-lint-action
|
||||
- → See `samber/cc-skills-golang@golang-code-style` skill for style rules that linters enforce
|
||||
- → See `samber/cc-skills-golang@golang-security` skill for SAST tools beyond linting (gosec, govulncheck)
|
||||
- → See `samber/cc-skills-golang@golang-continuous-integration` skill for automated AI-driven code review in CI using these guidelines
|
||||
@@ -0,0 +1,151 @@
|
||||
version: "2"
|
||||
run:
|
||||
concurrency: 4
|
||||
# Timeout for analysis
|
||||
timeout: 5m
|
||||
# Include test files
|
||||
tests: true
|
||||
|
||||
issues:
|
||||
max-issues-per-linter: 0 # 0 = unlimited (we want ALL issues)
|
||||
max-same-issues: 50
|
||||
|
||||
linters:
|
||||
enable:
|
||||
# correctness
|
||||
- govet # built-in checker: copylocks, printf formats, struct tags, unreachable code
|
||||
- staticcheck # extensive static analysis: deprecated APIs, common mistakes, simplifications
|
||||
- unused # unused variables, functions, types
|
||||
- errcheck # unchecked error returns and type assertions
|
||||
- errorlint # correct use of errors.Is/As and %w wrapping (Go 1.13+)
|
||||
- nilerr # returning nil error when err is non-nil
|
||||
- forcetypeassert # type assertions without comma-ok check
|
||||
- copyloopvar # loop variable copy issues (Go 1.22+)
|
||||
- durationcheck # detect time.Duration * time.Duration bugs
|
||||
- reassign # package-level variable reassignment
|
||||
# style
|
||||
- gocritic # opinionated style: unnecessary conversions, range copies, redundant code
|
||||
- revive # naming conventions, exported types, stuttered package names
|
||||
- wsl_v5 # whitespace and blank line rules for readability
|
||||
- whitespace # trailing whitespace, unnecessary blank lines
|
||||
- godot # exported-symbol comments must end with a period
|
||||
- misspell # common English misspellings in identifiers and comments
|
||||
- dupword # duplicate words in comments and strings (the the, is is)
|
||||
- predeclared # shadowing Go built-ins (len, cap, error)
|
||||
- errname # error type/var naming conventions (ErrFoo, FooError)
|
||||
- asciicheck # non-ASCII identifiers (prevents homoglyph/trojan source attacks)
|
||||
# complexity
|
||||
- gocyclo # cyclomatic complexity threshold
|
||||
- nestif # deeply nested if/else chains
|
||||
- funlen # function length limits (lines and statements)
|
||||
- dupl # code duplication detection
|
||||
# performance
|
||||
- perfsprint # faster alternatives to fmt.Sprintf
|
||||
- unconvert # unnecessary type conversions
|
||||
- ineffassign # assignments to variables never read
|
||||
- goconst # repeated literals that should be constants
|
||||
# security & resources
|
||||
- gosec # security scanner: SQL injection, hardcoded credentials, weak crypto, path traversal
|
||||
- bidichk # dangerous bidirectional Unicode sequences (trojan source CVE-2021-42574)
|
||||
- bodyclose # unclosed HTTP response bodies (connection leaks)
|
||||
- noctx # HTTP requests missing context.Context
|
||||
- containedctx # context.Context stored in struct fields instead of passed as parameter
|
||||
- fatcontext # context.WithValue/WithCancel in loops (unbounded context chain, memory leak)
|
||||
- sqlclosecheck # unclosed sql.Rows and sql.Stmt
|
||||
- rowserrcheck # unchecked sql.Rows.Err() after iteration
|
||||
# logging
|
||||
- sloglint # consistent log/slog code style
|
||||
- loggercheck # key-value pair validation for structured loggers (zap, slog, logr)
|
||||
# testing
|
||||
- testifylint # testify best practices
|
||||
- thelper # test helpers missing t.Helper()
|
||||
- usetesting # use t.Setenv/t.TempDir instead of os equivalents in tests
|
||||
- paralleltest # tests and subtests missing t.Parallel()
|
||||
# modernization & meta
|
||||
- modernize # old patterns replaceable with newer Go features
|
||||
- exptostd # replace golang.org/x/exp/ functions with stdlib equivalents
|
||||
- intrange # range over integer instead of C-style loop (Go 1.22+)
|
||||
- usestdlibvars # use stdlib constants instead of hardcoded values
|
||||
- exhaustive # switch statements not covering all enum values
|
||||
- nolintlint # enforces proper //nolint directive usage
|
||||
|
||||
disable:
|
||||
- lll # line length — handled by gofmt/gofumpt
|
||||
- prealloc # high false-positive rate; enable only after performance profiling
|
||||
- wrapcheck # forces wrapping all external errors — too noisy as a default
|
||||
- err113 # forces package-level sentinel errors — too opinionated, breaks common patterns
|
||||
- mnd # magic number detector — extremely noisy, flags obvious constants like HTTP 200
|
||||
- iface # interface pollution detector — too opinionated, not mature enough
|
||||
- nakedret # naked returns — overlaps with funlen (short functions make naked returns fine)
|
||||
- noinlineerr # bans `if err := ...; err != nil {}` — this is idiomatic Go
|
||||
- gocognit # cognitive complexity — redundant with gocyclo + nestif
|
||||
- cyclop # cyclomatic complexity — redundant with gocyclo
|
||||
- depguard # import allow/deny lists — requires per-project configuration
|
||||
- goheader # file header enforcement — project-specific policy
|
||||
- importas # import alias enforcement — requires per-project configuration
|
||||
- funcorder # function ordering — too opinionated for a default
|
||||
- godoclint # godoc validation — overlaps with godot and revive
|
||||
- varnamelen # variable name length — too opinionated, Go favors short names
|
||||
- exhaustruct # all struct fields must be set — extremely noisy, breaks zero-value idiom
|
||||
- gochecknoglobals # no global variables — too strict, many valid uses
|
||||
- gochecknoinits # no init() functions — too strict, many valid uses
|
||||
- unparam # unused function parameters — medium false-positive rate with interfaces
|
||||
- makezero # flags make([]T, n) — noisy, often wrong about intent
|
||||
- testpackage # forces _test package — valid but too opinionated as a default
|
||||
- embeddedstructfieldcheck # embedded type placement — minor style, not worth enforcing
|
||||
- iotamixing # iota in mixed const blocks — very rare issue
|
||||
- unqueryvet # SELECT * detection — too niche for a default config
|
||||
- recvcheck # receiver type consistency — overlaps with gocritic
|
||||
- mirror # bytes/strings mirror patterns — very few real hits
|
||||
- protogetter # proto field access via getters — only for protobuf users
|
||||
- spancheck # OpenTelemetry span checks — only for OTel users
|
||||
- zerologlint # zerolog usage — only for zerolog users
|
||||
|
||||
exclusions:
|
||||
paths:
|
||||
- vendor$
|
||||
- third_party$
|
||||
- testutils$
|
||||
- examples$
|
||||
|
||||
settings:
|
||||
dupl:
|
||||
threshold: 100 # lower => stricter (tokens)
|
||||
errcheck:
|
||||
check-type-assertions: true
|
||||
funlen:
|
||||
lines: 120
|
||||
statements: 80
|
||||
goconst:
|
||||
min-len: 3
|
||||
min-occurrences: 4
|
||||
gocyclo:
|
||||
min-complexity: 13 # strict; lower => stricter
|
||||
nolintlint:
|
||||
require-explanation: true
|
||||
require-specific: true
|
||||
wsl_v5:
|
||||
allow-first-in-block: true
|
||||
allow-whole-block: false
|
||||
branch-max-lines: 2
|
||||
|
||||
formatters:
|
||||
# formatters are opt-in: only those listed here run. Others are left commented
|
||||
# with the reason they stay off (a `disable` block is not valid here and makes
|
||||
# `golangci-lint config verify` fail).
|
||||
enable:
|
||||
- gofumpt # superset of gofmt: applies gofmt's rules first, then its own
|
||||
- goimports # import management: adds imports that --fix introduces, drops unused ones
|
||||
# - gofmt # redundant: gofumpt already applies gofmt's rules
|
||||
# - gci # import grouping/ordering — gofumpt already handles standard grouping
|
||||
# - golines # line wrapping — too opinionated, can break readability
|
||||
# - swaggo # swaggo comment formatting — only for swaggo users
|
||||
settings:
|
||||
gofumpt:
|
||||
extra-rules: true
|
||||
exclusions:
|
||||
generated: lax
|
||||
paths:
|
||||
- third_party$
|
||||
- builtin$
|
||||
- examples$
|
||||
@@ -0,0 +1,305 @@
|
||||
[
|
||||
{
|
||||
"id": 1,
|
||||
"name": "nolint-directive-specificity",
|
||||
"description": "Tests that nolint directives specify the linter name and include a justification — never bare //nolint",
|
||||
"prompt": "I have a Go function that triggers several lint warnings. I want to suppress them. Write the nolint directives for these cases:\n\n1. A logger.Sync() call where the error is intentionally ignored\n2. A type assertion that is guaranteed safe by a preceding type switch\n3. A function with cyclomatic complexity of 15 that orchestrates 6 subsystems\n4. A table-driven test function that is 200 lines long\n5. A deprecated API call that we can't migrate yet\n\nShow the code with proper suppression directives.",
|
||||
"trap": "Model uses bare //nolint without specifying the linter name, or omits the justification comment. May also use //nolint at the file level instead of per-line.",
|
||||
"assertions": [
|
||||
{
|
||||
"id": "1.1",
|
||||
"text": "Every //nolint directive specifies the linter name (e.g., //nolint:errcheck, //nolint:gocyclo) — NO bare //nolint without a linter name"
|
||||
},
|
||||
{
|
||||
"id": "1.2",
|
||||
"text": "Every //nolint directive includes a justification comment after // (e.g., //nolint:errcheck // fire-and-forget logging)"
|
||||
},
|
||||
{
|
||||
"id": "1.3",
|
||||
"text": "The type assertion uses //nolint:forcetypeassert with an explanation referencing why the assertion is safe"
|
||||
},
|
||||
{
|
||||
"id": "1.4",
|
||||
"text": "The long test function uses //nolint:funlen with a justification like 'table-driven test, length proportional to case count'"
|
||||
},
|
||||
{
|
||||
"id": "1.5",
|
||||
"text": "The cyclomatic complexity suppression uses //nolint:gocyclo with a justification about orchestration"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"name": "nolint-fix-vs-suppress-judgment",
|
||||
"description": "Tests judgment about when to fix vs when to suppress — security and correctness linters should almost never be suppressed",
|
||||
"prompt": "My Go codebase has these lint warnings. For each one, should I fix the code or suppress the warning? Explain.\n\n1. `bodyclose: response body not closed` on an HTTP client call\n2. `funlen: function too long (150 lines)` on a table-driven test\n3. `errcheck: error return not checked` on a database query in a request handler\n4. `dupl: duplicate code block` on two similar but intentionally parallel handler functions\n5. `sqlclosecheck: rows not closed` on a database query\n6. `goconst: string 'application/json' repeated 4 times` in test assertions",
|
||||
"trap": "Model suppresses bodyclose, errcheck on production DB code, or sqlclosecheck — these are real bugs, not style issues. Should only suppress funlen, dupl, and goconst with justifications.",
|
||||
"assertions": [
|
||||
{
|
||||
"id": "2.1",
|
||||
"text": "Recommends FIXING bodyclose — unclosed HTTP response bodies leak connections, this is a real resource leak"
|
||||
},
|
||||
{
|
||||
"id": "2.2",
|
||||
"text": "Recommends SUPPRESSING funlen on the table-driven test — length is proportional to test case count, splitting would be worse"
|
||||
},
|
||||
{
|
||||
"id": "2.3",
|
||||
"text": "Recommends FIXING errcheck on the database query — unchecked errors in production request handlers cause silent failures"
|
||||
},
|
||||
{
|
||||
"id": "2.4",
|
||||
"text": "Recommends SUPPRESSING dupl on intentional parallel structure — with a justification that the parallel pattern is clearer than abstracting"
|
||||
},
|
||||
{
|
||||
"id": "2.5",
|
||||
"text": "Recommends FIXING sqlclosecheck — unclosed sql.Rows leak database connections"
|
||||
},
|
||||
{
|
||||
"id": "2.6",
|
||||
"text": "Recommends SUPPRESSING goconst in tests — extracting 'application/json' to a constant in tests would reduce clarity"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"name": "golangci-yml-version-2-structure",
|
||||
"description": "Tests knowledge of golangci-lint v2 config structure: version field, linters.enable/disable, formatters section",
|
||||
"prompt": "Create a .golangci.yml configuration file for a Go project. Enable at least govet, staticcheck, errcheck, and gofumpt. Set the timeout to 5 minutes and configure errcheck to also check type assertions.",
|
||||
"trap": "Model uses golangci-lint v1 config format (missing version: \"2\", using enable-all/disable-all, missing formatters section, putting gofumpt in linters instead of formatters).",
|
||||
"assertions": [
|
||||
{
|
||||
"id": "3.1",
|
||||
"text": "Config file has version: \"2\" at the top — golangci-lint v2 requires this field"
|
||||
},
|
||||
{
|
||||
"id": "3.2",
|
||||
"text": "Linters are listed under linters.enable (not enable-all with exclusions) — explicit listing is the recommended approach"
|
||||
},
|
||||
{
|
||||
"id": "3.3",
|
||||
"text": "gofumpt is configured under formatters.enable, NOT under linters.enable — formatters are a separate section in v2"
|
||||
},
|
||||
{
|
||||
"id": "3.4",
|
||||
"text": "errcheck has check-type-assertions: true in linters.settings.errcheck"
|
||||
},
|
||||
{
|
||||
"id": "3.5",
|
||||
"text": "Timeout is set under run.timeout: 5m"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"name": "linter-categories-correctness-vs-style",
|
||||
"description": "Tests understanding of linter domains — which linters catch bugs vs which catch style issues",
|
||||
"prompt": "I'm setting up golangci-lint for a new Go project and can only enable 10 linters due to team constraints. Which 10 should I prioritize and why? Categorize them.",
|
||||
"trap": "Model prioritizes style linters (revive, godot, misspell) over correctness linters (govet, staticcheck, errcheck, nilerr). May also include deprecated or redundant linters.",
|
||||
"assertions": [
|
||||
{
|
||||
"id": "4.1",
|
||||
"text": "Includes govet and staticcheck — these are the highest-value correctness linters that catch real bugs"
|
||||
},
|
||||
{
|
||||
"id": "4.2",
|
||||
"text": "Includes errcheck — unchecked errors are the most common source of silent failures in Go"
|
||||
},
|
||||
{
|
||||
"id": "4.3",
|
||||
"text": "Prioritizes correctness/safety linters over style linters — bug-finding tools provide more value than formatting preferences"
|
||||
},
|
||||
{
|
||||
"id": "4.4",
|
||||
"text": "Includes at least one security linter (bodyclose, gosec, or sqlclosecheck) for resource leak prevention"
|
||||
},
|
||||
{
|
||||
"id": "4.5",
|
||||
"text": "Does NOT include both gocyclo and cyclop (redundant) or both gocognit and gocyclo (overlapping complexity checkers)"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"name": "legacy-codebase-incremental-adoption",
|
||||
"description": "Tests the new-from-rev strategy for adopting linters on legacy code without drowning in warnings",
|
||||
"prompt": "We have a large legacy Go codebase with 2000+ lint warnings. We want to adopt golangci-lint but can't fix everything at once. How should we approach this?",
|
||||
"trap": "Model suggests suppressing all existing warnings with //nolint directives, or disabling linters until the code is clean. Doesn't know about new-from-rev for incremental adoption.",
|
||||
"assertions": [
|
||||
{
|
||||
"id": "5.1",
|
||||
"text": "Recommends setting issues.new-from-rev (e.g., HEAD~1 or main) in .golangci.yml to only lint new/changed code"
|
||||
},
|
||||
{
|
||||
"id": "5.2",
|
||||
"text": "Does NOT suggest adding //nolint directives to all 2000+ existing warnings — that's unmaintainable"
|
||||
},
|
||||
{
|
||||
"id": "5.3",
|
||||
"text": "Suggests gradually cleaning up old code over time while enforcing quality on new code"
|
||||
},
|
||||
{
|
||||
"id": "5.4",
|
||||
"text": "Suggests running golangci-lint run --fix for auto-fixable issues as a quick first pass"
|
||||
},
|
||||
{
|
||||
"id": "5.5",
|
||||
"text": "Mentions using parallel sub-agents or batching fixes by linter category (security, error handling, style) to tackle cleanup efficiently"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"name": "interpreting-lint-output-format",
|
||||
"description": "Tests ability to read lint output format and use the linter name for targeted investigation or suppression",
|
||||
"prompt": "I ran golangci-lint and got this output:\n\n```\nserver/handler.go:42:10: Error return value of `(*DB).Close` is not checked (errcheck)\nserver/handler.go:55:2: response body must be closed (bodyclose)\nserver/auth.go:12:6: func `validateToken` is unused (unused)\nserver/auth.go:30:1: cyclomatic complexity 17 of func `processAuth` is high (> 13) (gocyclo)\nserver/model.go:5:2: exported type `Model` should have comment or be unexported (revive)\n```\n\nFor each warning, explain what it means and whether I should fix or suppress it.",
|
||||
"trap": "Model doesn't use the linter name in parentheses to guide its response. May treat all warnings equally instead of recognizing that errcheck and bodyclose are critical while revive is style.",
|
||||
"assertions": [
|
||||
{
|
||||
"id": "6.1",
|
||||
"text": "Identifies errcheck on DB.Close as a real issue to fix — unchecked database close errors can mask connection problems"
|
||||
},
|
||||
{
|
||||
"id": "6.2",
|
||||
"text": "Identifies bodyclose as a critical resource leak to fix — not suppress"
|
||||
},
|
||||
{
|
||||
"id": "6.3",
|
||||
"text": "Identifies unused validateToken as dead code to either remove or fix — not suppress"
|
||||
},
|
||||
{
|
||||
"id": "6.4",
|
||||
"text": "For gocyclo, evaluates whether processAuth should be refactored or suppressed based on its nature (orchestration function vs genuinely complex logic)"
|
||||
},
|
||||
{
|
||||
"id": "6.5",
|
||||
"text": "For revive comment warning, correctly identifies it as a style issue that's lower priority than the correctness issues above"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 7,
|
||||
"name": "disabled-linters-with-rationale",
|
||||
"description": "Tests understanding of which linters should be disabled and why — the recommended config explicitly disables several with reasons",
|
||||
"prompt": "A colleague wants to enable these linters in our .golangci.yml: exhaustruct, gochecknoglobals, wrapcheck, mnd (magic number detector), and varnamelen. Should we? Explain your reasoning for each.",
|
||||
"trap": "Model enables all of them without considering that they are intentionally excluded from the recommended config due to being too noisy, too opinionated, or breaking idiomatic Go patterns.",
|
||||
"assertions": [
|
||||
{
|
||||
"id": "7.1",
|
||||
"text": "Recommends AGAINST exhaustruct — it requires all struct fields to be set, which breaks Go's zero-value idiom and is extremely noisy"
|
||||
},
|
||||
{
|
||||
"id": "7.2",
|
||||
"text": "Recommends AGAINST gochecknoglobals — there are many valid uses for global variables in Go (loggers, registries, etc.) and a blanket ban is too strict"
|
||||
},
|
||||
{
|
||||
"id": "7.3",
|
||||
"text": "Recommends AGAINST wrapcheck as a default — it forces wrapping all external errors, which is too noisy and not always appropriate"
|
||||
},
|
||||
{
|
||||
"id": "7.4",
|
||||
"text": "Recommends AGAINST mnd — magic number detection is extremely noisy, flagging obvious constants like HTTP status codes"
|
||||
},
|
||||
{
|
||||
"id": "7.5",
|
||||
"text": "Recommends AGAINST varnamelen — Go idiomatically favors short variable names, and this linter conflicts with that philosophy"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 8,
|
||||
"name": "nolintlint-meta-linter",
|
||||
"description": "Tests knowledge that nolintlint enforces proper nolint directive usage and should be enabled",
|
||||
"prompt": "I see //nolint directives scattered throughout our Go codebase. Many are bare '//nolint' without specifying which linter or why. How can I enforce proper nolint hygiene automatically?",
|
||||
"trap": "Model suggests a manual code review process or a custom script instead of enabling the nolintlint linter with require-explanation and require-specific settings.",
|
||||
"assertions": [
|
||||
{
|
||||
"id": "8.1",
|
||||
"text": "Recommends enabling the nolintlint linter — it automatically enforces nolint directive quality"
|
||||
},
|
||||
{
|
||||
"id": "8.2",
|
||||
"text": "Configures nolintlint with require-specific: true to require linter names (not bare //nolint)"
|
||||
},
|
||||
{
|
||||
"id": "8.3",
|
||||
"text": "Configures nolintlint with require-explanation: true to require justification comments"
|
||||
},
|
||||
{
|
||||
"id": "8.4",
|
||||
"text": "Shows the correct config location: linters.settings.nolintlint in .golangci.yml"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 9,
|
||||
"name": "multiple-nolint-comma-syntax",
|
||||
"description": "Tests proper syntax for suppressing multiple linters on one line",
|
||||
"prompt": "I have a line of Go code that triggers both errcheck and gosec warnings. I've confirmed both are false positives in this specific case. How do I suppress both on the same line?",
|
||||
"trap": "Model uses two separate //nolint directives on the same line, or uses //nolint without comma separation, or stacks directives on consecutive lines for the same code line.",
|
||||
"assertions": [
|
||||
{
|
||||
"id": "9.1",
|
||||
"text": "Uses comma-separated linter names in a single directive: //nolint:errcheck,gosec — not two separate //nolint directives"
|
||||
},
|
||||
{
|
||||
"id": "9.2",
|
||||
"text": "Includes a justification comment after the directive explaining why both are false positives"
|
||||
},
|
||||
{
|
||||
"id": "9.3",
|
||||
"text": "The directive is placed on the same line as the flagged code or the line immediately above it"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 10,
|
||||
"name": "common-config-issues",
|
||||
"description": "Tests troubleshooting knowledge for golangci-lint: timeout, v1-to-v2 migration, linter-not-found",
|
||||
"prompt": "I'm getting these errors with golangci-lint:\n1. 'deadline exceeded' when running on our large monorepo\n2. After upgrading to golangci-lint v2, my .golangci.yml throws config errors\n3. 'linter modernize not found' even though I listed it in enable\n\nHow do I fix each?",
|
||||
"trap": "Model doesn't know about the v2 config migration tool, suggests reinstalling for the linter-not-found issue instead of checking the golangci-lint version, or increases concurrency instead of timeout.",
|
||||
"assertions": [
|
||||
{
|
||||
"id": "10.1",
|
||||
"text": "For deadline exceeded: recommends increasing run.timeout in .golangci.yml (default is 5m, may need 10m+ for large repos)"
|
||||
},
|
||||
{
|
||||
"id": "10.2",
|
||||
"text": "For v1 config errors: recommends running golangci-lint migrate to convert the config format to v2"
|
||||
},
|
||||
{
|
||||
"id": "10.3",
|
||||
"text": "For linter not found: recommends checking the golangci-lint version — modernize requires v2.6.0+ or similar newer version"
|
||||
},
|
||||
{
|
||||
"id": "10.4",
|
||||
"text": "Mentions golangci-lint linters command to check available linters in the installed version"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 11,
|
||||
"name": "formatter-vs-linter-distinction",
|
||||
"description": "Tests that formatters (gofumpt, gofmt) are configured in the formatters section, not the linters section, and use the fmt subcommand",
|
||||
"prompt": "I want to enforce consistent code formatting in my Go project using golangci-lint. I want gofumpt with extra rules. How do I set it up?",
|
||||
"trap": "Model puts gofumpt in the linters.enable section instead of formatters.enable (v2 distinction), or doesn't mention the golangci-lint fmt subcommand for formatting.",
|
||||
"assertions": [
|
||||
{
|
||||
"id": "11.1",
|
||||
"text": "Configures gofumpt under formatters.enable, NOT linters.enable — formatters are a separate section in golangci-lint v2"
|
||||
},
|
||||
{
|
||||
"id": "11.2",
|
||||
"text": "Sets gofumpt extra-rules: true under formatters.settings.gofumpt"
|
||||
},
|
||||
{
|
||||
"id": "11.3",
|
||||
"text": "Mentions the golangci-lint fmt ./... command for running formatters — separate from golangci-lint run"
|
||||
},
|
||||
{
|
||||
"id": "11.4",
|
||||
"text": "Notes that gci and goimports are redundant with gofumpt and can be disabled"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,112 @@
|
||||
# Linter Reference
|
||||
|
||||
golangci-lint v2 uses a `.golangci.yml` with `version: "2"` at the project root.
|
||||
|
||||
Key sections of `.golangci.yml`:
|
||||
|
||||
- **`run`** — concurrency, timeout, test inclusion, directory exclusions
|
||||
- **`linters.enable`** / **`linters.disable`** — which linters are active
|
||||
- **`linters.settings`** — per-linter thresholds and options
|
||||
- **`formatters`** — code formatters (gofmt, gofumpt)
|
||||
- **`issues`** — output limits, exclusion rules
|
||||
|
||||
To add a linter: add it to `linters.enable` and optionally configure it in `linters.settings`.
|
||||
|
||||
To disable a linter: move it to `linters.disable` with a comment explaining why.
|
||||
|
||||
## Linter Categories
|
||||
|
||||
The recommended configuration enables linters across these domains:
|
||||
|
||||
| Domain | Linters | Catches |
|
||||
| --- | --- | --- |
|
||||
| Correctness | govet, staticcheck, unused, errcheck, errorlint, nilerr, forcetypeassert, copyloopvar, durationcheck, reassign | Bugs, unchecked errors, stdlib misuse |
|
||||
| Style | gocritic, revive, wsl_v5, whitespace, godot, misspell, dupword, predeclared, errname, asciicheck | Readability, naming, consistency |
|
||||
| Complexity | gocyclo, nestif, funlen, dupl | Overly complex or duplicated code |
|
||||
| Performance | perfsprint, unconvert, ineffassign, goconst | Conversions, string ops, dead assigns |
|
||||
| Security | gosec, bidichk, bodyclose, noctx, containedctx, fatcontext, sqlclosecheck, rowserrcheck | Security issues, resource leaks (HTTP, SQL) |
|
||||
| Logging | sloglint, loggercheck | Structured log consistency |
|
||||
| Testing | thelper, paralleltest, testifylint, usetesting | Test hygiene and best practices |
|
||||
| Modernization | modernize, exptostd, intrange, usestdlibvars, exhaustive, nolintlint | Modern Go idioms, lint hygiene |
|
||||
| Formatting | gofmt, gofumpt | Code formatting |
|
||||
|
||||
All linters are enabled in the [recommended .golangci.yml](../assets/.golangci.yml), organized by domain.
|
||||
|
||||
### Correctness & Safety
|
||||
|
||||
- **govet** — Go's built-in checker: copylocks, printf format mismatches, struct tag validation, context stored in structs, unreachable code, nil dereferences
|
||||
- **staticcheck** — Extensive static analysis: deprecated APIs, common mistakes, unnecessary code, simplifications, misuse of standard library
|
||||
- **unused** — Detects unused variables, functions, types, and struct fields
|
||||
- **errcheck** — Ensures all error returns are checked, including type assertions (configured with `check-type-assertions: true`)
|
||||
- **nilerr** — Detects returning nil error when `err` is non-nil (common source of silent failures)
|
||||
- **forcetypeassert** — Flags type assertions without the comma-ok check (`v := x.(T)` instead of `v, ok := x.(T)`)
|
||||
- **copyloopvar** — Detects loop variable copy issues (Go 1.22+)
|
||||
- **errorlint** — Enforces correct use of `errors.Is`/`errors.As` and `%w` wrapping (Go 1.13+ error wrapping)
|
||||
- **durationcheck** — Detects `time.Duration * time.Duration` multiplication bugs (e.g., `2 * time.Second * time.Minute` produces nanoseconds squared, not seconds)
|
||||
- **reassign** — Detects reassignment of package-level variables outside `init()`, which hides state mutations
|
||||
|
||||
### Style & Readability
|
||||
|
||||
- **gocritic** — Opinionated style checks: unnecessary conversions, range copies, append-assign patterns, redundant code
|
||||
- **revive** — Naming conventions for exported types, unexported returns, receiver naming, error naming, stuttered package names
|
||||
- **wsl_v5** — Whitespace and blank line rules for visual grouping and readability
|
||||
- **whitespace** — Detects trailing whitespace and unnecessary blank lines in function bodies
|
||||
- **godot** — Ensures exported-symbol comments end with a period
|
||||
- **misspell** — Catches common English misspellings in identifiers and comments
|
||||
- **predeclared** — Flags shadowing of Go built-in identifiers (e.g., naming a variable `len`, `cap`, `error`)
|
||||
- **errname** — Enforces error naming conventions: error types suffixed with `Error` (e.g., `DecodeError`), error variables prefixed with `Err` (e.g., `ErrNotFound`)
|
||||
- **dupword** — Detects duplicate words in comments and strings (e.g., "the the", "is is") — often copy-paste artifacts
|
||||
- **asciicheck** — Flags non-ASCII identifiers that enable homoglyph/trojan source attacks (visually identical but different Unicode codepoints)
|
||||
|
||||
### Complexity
|
||||
|
||||
- **gocyclo** — Cyclomatic complexity threshold (configured: 13). Functions exceeding this should be split
|
||||
- **nestif** — Detects deeply nested if/else chains that harm readability
|
||||
- **funlen** — Function length limits (configured: 120 lines, 80 statements)
|
||||
- **dupl** — Code duplication detection (configured: 100 token threshold)
|
||||
|
||||
### Performance
|
||||
|
||||
- **perfsprint** — Suggests faster alternatives to `fmt.Sprintf` (e.g., `strconv.Itoa` instead of `fmt.Sprintf("%d", n)`)
|
||||
- **unconvert** — Detects unnecessary type conversions (e.g., `int(x)` when `x` is already `int`)
|
||||
- **ineffassign** — Detects assignments to variables that are never subsequently read
|
||||
- **goconst** — Detects repeated string/number literals that should be extracted to constants (configured: min 3 chars, min 4 occurrences)
|
||||
|
||||
### Security & Resources
|
||||
|
||||
- **gosec** — Security scanner: SQL injection, hardcoded credentials, weak crypto, path traversal, unsafe usage, and 50+ other rules. The primary SAST tool in the config — never suppress without strong justification.
|
||||
- **bidichk** — Detects dangerous bidirectional Unicode sequences (CVE-2021-42574 trojan source attack — code that looks safe but executes differently)
|
||||
- **noctx** — Detects HTTP requests sent without `context.Context` (prevents proper timeouts and cancellation)
|
||||
- **containedctx** — Flags `context.Context` stored in struct fields instead of passed as a parameter (anti-pattern per Go docs)
|
||||
- **fatcontext** — Detects `context.WithValue`/`WithCancel` in loops, creating unbounded context chains that grow each iteration and cause memory leaks
|
||||
- **bodyclose** — Ensures HTTP response bodies are closed (unclosed bodies leak connections)
|
||||
- **sqlclosecheck** — Ensures `sql.Rows` and `sql.Stmt` are closed after use
|
||||
- **rowserrcheck** — Ensures `sql.Rows.Err()` is checked after iteration
|
||||
|
||||
### Logging
|
||||
|
||||
- **sloglint** — Enforces consistent `log/slog` code style: proper key-value pairing, message formatting, and level usage
|
||||
- **loggercheck** — Validates key-value pair formatting for structured loggers (zap, slog, logr) — detects odd numbers of args, missing keys
|
||||
|
||||
### Testing
|
||||
|
||||
- **thelper** — Ensures test helpers call `t.Helper()` so failures report the correct call site
|
||||
- **paralleltest** — Detects tests and subtests missing `t.Parallel()` calls
|
||||
- **testifylint** — Enforces testify best practices (e.g., `assert.Equal(t, expected, actual)` over `assert.True(t, expected == actual)`)
|
||||
- **usetesting** — Suggests `t.Setenv`/`t.TempDir` instead of `os.Setenv`/`os.MkdirTemp` in tests (automatic cleanup, proper isolation)
|
||||
|
||||
### Modernization & Meta
|
||||
|
||||
- **modernize** — Detects code that can be rewritten using newer Go features (requires golangci-lint v2.6.0+)
|
||||
- **exptostd** — Detects `golang.org/x/exp/` functions that now have stdlib equivalents (e.g., `slices`, `maps`, `cmp` packages added in Go 1.21)
|
||||
- **intrange** — Suggests `range N` over C-style `for i := 0; i < N; i++` loops (Go 1.22+)
|
||||
- **usestdlibvars** — Replaces hardcoded strings/numbers with stdlib constants (e.g., `http.MethodGet` instead of `"GET"`)
|
||||
- **exhaustive** — Ensures switch statements on enum types cover all possible values
|
||||
- **nolintlint** — Enforces proper `//nolint` directive usage: requires linter name and justification comment (configured with `require-explanation` and `require-specific`)
|
||||
|
||||
### Formatting
|
||||
|
||||
Formatters run via `golangci-lint fmt ./...`:
|
||||
|
||||
- **gofmt** — Standard Go formatter (canonical formatting)
|
||||
- **gofumpt** — Stricter formatter with extra rules (configured with `extra-rules: true`): consistent empty lines, grouped imports, simplified code patterns
|
||||
@@ -0,0 +1,68 @@
|
||||
# Nolint Directives
|
||||
|
||||
## Syntax
|
||||
|
||||
```go
|
||||
//nolint:lintername // justification explaining why this suppression is needed
|
||||
```
|
||||
|
||||
Place the directive on the same line as the flagged code, or on the line immediately above it.
|
||||
|
||||
## Rules
|
||||
|
||||
1. **MUST specify the linter name** — bare `//nolint` suppresses all linters on that line and makes it impossible to track what is being suppressed
|
||||
2. **MUST add a justification comment** — future readers (and your future self) need to understand why
|
||||
3. **The `nolintlint` linter enforces both rules** — it will flag bare `//nolint` and missing reasons
|
||||
4. **MUST fix the root cause before suppressing** — only suppress after confirming the issue is a false positive or an intentional pattern
|
||||
|
||||
## Examples
|
||||
|
||||
```go
|
||||
// Specific linter with reason
|
||||
//nolint:errcheck // fire-and-forget logging, error not actionable
|
||||
_ = logger.Sync()
|
||||
|
||||
// Type assertion is safe because preceding type switch guarantees the type
|
||||
v := x.(MyType) //nolint:forcetypeassert // guaranteed by type switch on line 42
|
||||
|
||||
// Orchestration function has inherent complexity
|
||||
//nolint:gocyclo // orchestration function coordinating 8 subsystems
|
||||
func orchestrate() error {
|
||||
|
||||
// Table-driven test with many cases
|
||||
//nolint:funlen // table-driven test, length is proportional to case count
|
||||
func TestParser(t *testing.T) {
|
||||
|
||||
// Intentional parallel structure is clearer than abstracting
|
||||
//nolint:dupl // intentional parallel structure for readability
|
||||
```
|
||||
|
||||
## Multiple Linters
|
||||
|
||||
Suppress multiple linters on one line with comma separation:
|
||||
|
||||
```go
|
||||
//nolint:errcheck,gosec // fire-and-forget in test helper
|
||||
```
|
||||
|
||||
## When to Suppress vs. When to Fix
|
||||
|
||||
**Fix** (almost always):
|
||||
|
||||
- `errcheck` — check the error, even if just logging it
|
||||
- `govet` — these are usually real bugs
|
||||
- `staticcheck` — deprecated API usage, logic errors
|
||||
- `bodyclose`, `sqlclosecheck` — resource leaks are real issues
|
||||
|
||||
**Suppress** (with justification):
|
||||
|
||||
- `funlen` — table-driven tests with many cases
|
||||
- `gocyclo` — orchestration functions where splitting would obscure the flow
|
||||
- `dupl` — intentional parallel structure that is clearer than an abstraction
|
||||
- `exhaustive` — when a default case intentionally handles remaining values
|
||||
- `goconst` — when extracting to a constant would reduce clarity (e.g., test assertions)
|
||||
|
||||
**Never suppress without strong justification**:
|
||||
|
||||
- Security linters (`bodyclose`, `sqlclosecheck`, `rowserrcheck`) — these catch real resource leaks
|
||||
- `errcheck` on production code paths — unchecked errors cause silent failures
|
||||
Reference in New Issue
Block a user