revive

GitHub

πŸ”₯ ~6x faster, stricter, configurable, extensible, and beautiful drop-in replacement for golint

RAW Rules

AGENTS.md

# AGENTS.md

Guidance for AI coding agents (Claude Code, Copilot, Cursor, Codex, etc.) working in this repository.

Human contributors: see [CONTRIBUTING.md](CONTRIBUTING.md) and [DEVELOPING.md](DEVELOPING.md) first β€” this file is a complement, not a replacement.

## 1. What this project is

`revive` is a fast, configurable, extensible Go linter.
It parses Go source via `go/ast` (+ `go/types` for typed rules), runs a configurable set of rules, and emits findings through pluggable formatters.

Top-level packages:

- `cli/` β€” command-line entry point (`main.go` defers to `cli.RunRevive`).
- `lint/` β€” core linter engine, rule interfaces (`Rule`, `ConfigurableRule`), `File`, `Failure`, `Severity`, and the in-memory `Config` types.
- `rule/` β€” one file per rule (100+ rules). Untyped rules also listed in `untyped.toml`.
- `formatter/` β€” output formatters (default, json, sarif, stylish, friendly, …).
- `config/` β€” config file loading (TOML), defaults, and the registries of available rules and formatters.
- `revivelib/` β€” programmatic API for embedding revive.
- `test/` β€” rule tests, one `_test.go` per rule.
- `testdata/` β€” Go source fixtures consumed by rule tests.
- `internal/` β€” helpers not part of the public API.

## 2. Coding standards β€” read these first

Before writing Go, read [`.github/instructions/go.instructions.md`](.github/instructions/go.instructions.md).
It is the single source of truth for naming, error handling, concurrency, testing style, and modern Go (1.21+) idioms that this project expects.
**Do not duplicate or contradict it here.**

In addition to that file:

- The project targets the Go version in [`go.mod`](go.mod) (currently `go 1.25.0`).
  Use stdlib features available at that version (`min`/`max`, `slices`, `maps`, `cmp.Or`, `errors.Join`, range-over-int, `slog`, etc.)
  instead of hand-rolled equivalents.
- `revive` lints itself. Code must pass `revive --config revive.toml ./...` **and** `golangci-lint run`.
  See [`.golangci.yml`](.golangci.yml) for the strict config.

## 3. Build, test, lint

All workflows go through the [`Makefile`](Makefile):

```sh
make build # builds ./revive with version ldflags
make test  # go test -v -race ./...
make lint  # revive + golangci-lint
make fmt   # golangci-lint fmt
make tidy  # go mod tidy -diff (fails on drift)
make all   # test + lint + build
```

Run a single rule's tests:

```sh
go test -run TestUnusedParam ./test/...
```

Logging during local runs: set `REVIVE_LOG_LEVEL` (`debug|info|warn|error`) β€” logs go to stderr. See [DEVELOPING.md](DEVELOPING.md#logging).

## 4. Adding or modifying a rule

The canonical example is [`rule/argument_limit.go`](rule/argument_limit.go). For each new rule:

1. **Implementation** β€” `rule/<rule_name>.go`. Implement `lint.Rule`:

    ```golang
    Name() string
    Apply(*lint.File, lint.Arguments) []lint.Failure
    ```

    If the rule takes arguments, also implement `lint.ConfigurableRule.Configure(lint.Arguments) error`.
    Validate arguments there and return errors rather than panicking.
2. **Naming** β€” `Name()` returns `kebab-case` (e.g. `argument-limit`).
   The Go type is `ArgumentsLimitRule`. Source file is `argument_limit.go`. Keep these three in lockstep.
3. **Concurrency** β€” `Apply` may be called concurrently for different files.
   Do not mutate rule state from `Apply`. Mutate only in `Configure`, which is called once.
4. **Typed vs untyped** β€” if the rule uses `file.Pkg.TypeCheck()` (or anything from `go/types`), it is typed.
   Otherwise it is untyped and **must** be added to [`untyped.toml`](untyped.toml). Keep that file sorted and in sync.
5. **Tests** β€” add `test/<rule_name>_test.go` and a fixture under `testdata/<rule_name>.go` (and `_test.go`, `.gold`, or sub-dirs as needed).
   Use the existing test harness; do not introduce assertion libraries.
6. **Documentation** β€” add a `## <rule-name>` section to [`RULES_DESCRIPTIONS.md`](RULES_DESCRIPTIONS.md), in alphabetical order.
   Include configuration shape, an `### Examples` block, and a one-line entry in the rules table in [`README.md`](README.md).
   The TOC in both files is generated by `markdown-toc` (see DEVELOPING.md Β§Lint Markdown files) β€” regenerate it; don't hand-edit.
7. **Defaults** β€” hard-code defaults as constants in the rule file and apply them in `Configure` when arguments are missing
   (see `defaultArgumentsLimit` in `rule/argument_limit.go`). Bundle-level defaults live in `defaults.toml` / `revive.toml`.
8. **Register** β€” append the rule to `allRules` in [`config/config.go`](config/config.go) so the CLI can discover it.

## 5. Adding a formatter

Implement `lint.Formatter`:

```golang
Format(<-chan lint.Failure, lint.Config) (string, error)
Name() string
```

Place the implementation in `formatter/<name>.go`, append it to `allFormatters` in [`config/config.go`](config/config.go)
(so `config.GetFormatter` can find it), and add a row to the formatters table in [`README.md`](README.md).

## 6. Markdown changes

[`README.md`](README.md) and [`RULES_DESCRIPTIONS.md`](RULES_DESCRIPTIONS.md) are linted by `markdownlint-cli2`,
have generated tables of contents (`markdown-toc`), and have code snippets formatted by `mdsf`.
If you edit them, run the three tools listed in [DEVELOPING.md Β§Lint Markdown files](DEVELOPING.md#lint-markdown-files) β€”
CI will reject hand-edited TOCs and unformatted snippets.

Use ```` ```go ```` for Go code. Use ```` ```golang ```` only for snippets that are intentionally non-compilable.

Line length in this and other Markdown files is capped at 150 characters (200 inside code blocks); wrap accordingly.

## 7. Commits and pull requests

- Match the existing commit style (see `git log`): conventional-style prefixes such as `feature:`, `fix:`, `fix(deps):`, `chore(deps):`,
  often followed by `#<PR>`.
- Keep PRs focused and atomic. Open an issue first for non-trivial changes β€” see [CONTRIBUTING.md](CONTRIBUTING.md).
- The PR template lives at [`.github/PULL_REQUEST_TEMPLATE.md`](.github/PULL_REQUEST_TEMPLATE.md);
  fill in motivation, test coverage, and link the originating issue.
- Run `make all` locally before pushing. CI runs the same checks plus Markdown lint, TOC check, and `mdsf verify`.

## 8. Things agents should *not* do

- Don't silence lint findings with `//nolint` or `// revive:disable` to make CI green β€” fix the underlying code instead.
  Suppressions need a justification comment and reviewer approval.
- Don't relax thresholds in [`.golangci.yml`](.golangci.yml) or [`revive.toml`](revive.toml) to avoid fixing a finding.
- Don't add a dependency without a strong reason; `revive` deliberately keeps its dependency tree small. Run `go mod tidy` afterwards.
- Don't introduce assertion libraries (`testify`, `gomega`, …) β€” the project uses the standard `testing` package by design.
- Don't reformat or restructure files unrelated to the change. Keep diffs reviewable.
- Don't edit generated TOCs in `README.md` / `RULES_DESCRIPTIONS.md` by hand.
- Don't add a rule to `untyped.toml` unless you've verified it really doesn't touch type info β€”
  getting this wrong silently breaks the untyped fast path.

## 9. Where to look when stuck

| Need                                 | File / dir                                               |
| ------------------------------------ | -------------------------------------------------------- |
| Go style and idioms                  | `.github/instructions/go.instructions.md`                |
| Build / test / lint commands         | `Makefile`, `DEVELOPING.md`                              |
| How to write a rule (worked example) | `rule/argument_limit.go` + `test/argument_limit_test.go` |
| Rule interfaces, `File`, `Failure`   | `lint/`                                                  |
| Default config bundles               | `defaults.toml`, `revive.toml`                           |
| User-facing rule docs                | `RULES_DESCRIPTIONS.md`                                  |
| CLI flags & behavior                 | `cli/`                                                   |
| Programmatic embedding               | `revivelib/`                                             |