go-ruleguard dsl
The Go API for authoring ruleguard rule files that match, filter, and report on Go source code.
Repository Health
Technical Analysis
dsl is the package that ruleguard rule files import to define custom static-analysis rules in plain, tooling-friendly Go syntax. Rule authors write ordinary .go files that declare matcher functions accepting a dsl.Matcher argument, then chain Match()/MatchComment(), Where(), Report(), and Suggest() calls to describe what code pattern to look for, what additional type/value constraints must hold, and what warning or quickfix to emit.
Critically, none of the functions in this package actually execute at runtime for the caller — Match, Where, Report, and friends are effectively documented no-ops. The real ruleguard analyzer (in the parent go-ruleguard module) parses rule files as an AST and interprets the call chains directly, which is what lets rule files be checked by go vet, formatted by gofmt, and navigated by any Go-aware editor while still being pure configuration rather than compiled code.
Because it’s just a type-and-signature surface with no runtime behavior of its own, the dsl package is extremely stable and lightweight to depend on — projects add it purely so their rules.go files type-check, then ship the rules to be executed by the separate ruleguard binary or through golangci-lint’s gocritic/ruleguard integration.
What You Get
Matcher.Match/MatchCommentto declare one or more AST or comment-regexp patterns a rule should trigger onMatcher.Whereto attach additional boolean constraints (purity, constness, type identity, and more) that gate whether a match is acceptedMatcher.Report/Suggestto emit a diagnostic message or a quickfix rewrite pattern when a rule matches- A rich
VarAPI exposing captured submatch metadata — type, AST node kind, computed value, addressability, comparability — for use insideWhereconditions Matcher.Import/ImportAsto register package import paths so qualified type names resolve correctly during rule compilationImportRules/Bundleto compose and share rule groups as importable Go packages
Common Use Cases
- Writing a project-specific
rules.gofile withgorulesrules to catch team-specific anti-patterns that generic linters miss - Building and publishing a distributable ruleguard rule bundle as its own Go module for other teams to import
- Wiring custom ruleguard rules into golangci-lint’s gocritic integration for use in CI
- Prototyping and debugging a single lint rule interactively via
ruleguard -ebefore adding it to a permanent rule set - Embedding ruleguard-style dynamic rule execution into a custom static analyzer, following go-critic’s pattern
Under The Hood
Architecture
The dsl package is a pure type-and-signature surface: every exported method (Matcher.Match, Where, Report, Var.Filter, ExprType.Is, and so on) returns a static zero value and has no real logic in its body — the actual rule engine lives in the sibling analyzer/ruleguard packages of the parent go-ruleguard module (not in dsl itself), which parse a rules.go file’s AST, recognize these exact method-chain shapes, and compile them into an internal matcher representation executed against gogrep-derived AST patterns. This split means a rule file that imports dsl type-checks and gofmt-formats like normal Go code while never actually being built or run as Go code — the dsl package exists purely to give the rule-authoring surface real Go types, so editors, go vet, and IDE autocomplete all work against genuine method signatures instead of a bespoke DSL parser.
Tech Stack
dsl is dependency-free Go (module github.com/quasilyte/go-ruleguard/dsl, declared at the conservative go 1.15 in its own go.mod even though the parent repo has moved to go 1.22 with a toolchain pin), split into a handful of small files — dsl.go for the Matcher/Var pattern-matching API, filter.go and types subpackage for the VarFilterContext/types.Type filter surface used inside Var.Filter callbacks, do.go for the DoContext/DoVar rewrite-action API, and bundle.go for cross-package rule composition via ImportRules. The parent repository consuming this package builds the real analyzer on top of golang.org/x/tools’s go/analysis framework, quasilyte/gogrep for AST pattern matching, and go-toolsmith helpers.
Code Quality
There are no dedicated test files inside the dsl package itself — appropriate given its methods are declared zero-value stubs by design, with actual behavior verified by the parent repository’s analyzer/ruleguard test suites that exercise real rule files end-to-end. Naming is disciplined and consistently documented: every exported type and method carries a doc comment explaining semantics and, in several cases, worked examples (e.g. MatchComment’s named-capture-group walkthrough). The parent monorepo enforces gofmt, goimports, govet, staticcheck, gosimple, and several other golangci-lint linters in CI across the whole module, including this package.
API Design
The API reads as a fluent, chainable builder — m.Match(...).Where(...).Report(...) — that mirrors how a rule is described in prose, which keeps rule files readable even as constraints stack up. Method names map directly to Go analysis vocabulary (Deadcode, Addressable, Comparable, AssignableTo, Implements, OfKind) so anyone familiar with go/types semantics can guess correctly, and boilerplate to get started is minimal — a valid rule file needs only a gorules package clause, a dsl import, and one matcher function. The tradeoff is that the API’s true behavior is invisible from the package alone: understanding what a call chain actually does requires knowing it’s interpreted by an external AST-walking compiler rather than executed as written, which is undocumented within the package itself and only becomes clear from the parent project’s README.
Used by 3 apps in this directory
Coder
Devops · Developer Tools · Code Editors
Self-hosted cloud development environments and AI coding agents — defined in Terraform, connected via WireGuard, automatically shut down when idle.
PeerDB
Data Engineering · Databases
Postgres-native ETL that streams change data capture in real time to Snowflake, BigQuery, ClickHouse, S3, and Kafka — up to 10x faster than general-purpose pipelines, managed through a familiar Postgres SQL interface.
Portainer
Devops
A lightweight, open-source web UI that puts Docker, Kubernetes, and Podman management within reach of any team—no CLI expertise required.