analysis-based Go linter that runs dynamically loaded rules.
You write the rules, ruleguard
checks whether they are satisfied.
ruleguard
has some similarities with GitHub CodeQL, but only focuses on Go code queries.
Features:
- Custom linting rules without re-compilation and Go plugins.
- Diagnostics are written in a declarative way.
- Quickfix actions support.
- Powerful match filtering features, like expression type pattern matching.
ruleguard
comes with rules.go file that can be used as a foundation to write your own rules file.
To install ruleguard
binary under your $(go env GOPATH)/bin
:
$ go get -v -u github.com/quasilyte/go-ruleguard/...
If $GOPATH/bin
is under your system $PATH
, ruleguard
command should be available after that.
$ ruleguard -help
ruleguard: execute dynamic gogrep-based rules
Usage: ruleguard [-flag] [package]
Flags:
-rules string
path to a rules.go file
-e string
execute a single rule from a given string
-fix
apply all suggested fixes
-c int
display offending line with this many lines of context (default -1)
-json
emit JSON output
Create a test example.rules.go
file:
// +build ignore
package gorules
import "github.com/quasilyte/go-ruleguard/dsl/fluent"
func _(m fluent.Matcher) {
m.Match(`$x || $x`,
`$x && $x`).
Where(m["x"].Pure).
Report(`suspicious identical LHS and RHS`)
m.Match(`!($x != $y)`).Suggest(`$x == $y`)
m.Match(`!($x == $y)`).Suggest(`$x != $y`)
}
Create a test example.go
target file:
package main
func main() {
var v1, v2 int
println(!(v1 != v2))
println(!(v1 == v2))
if v1 == 0 && v1 == 0 {
println("hello, world!")
}
}
Run ruleguard
on that target file:
$ ruleguard -rules example.rules.go -fix example.go
example.go:5:10: hint: suggested: v1 == v2
example.go:6:10: hint: suggested: v1 != v2
example.go:7:5: error: suspicious identical LHS and RHS
Since we ran ruleguard
with -fix
argument, both suggested changes are applied to example.go
.
There is also a -e
mode that is useful during pattern debugging:
$ ruleguard -e 'm.Match(`!($x != $y)`)' example.go
example.go:5:10: !(v1 != v2)
It automatically inserts Report("$$")
into the specified pattern.
ruleguard
parses gorules (e.g. rules.go
) during the start to load the rule set.
Loaded rules are then used to check the specified targets (Go files, packages).
The rules.go
file itself is never compiled, nor executed.
A rules.go
file, as interpreted by a dsl/fluent
API, is a set of functions that serve as a rule groups. Every function accepts a single fluent.Matcher
argument that is then used to define and configure rules inside the group.
A rule definition always starts from a Match(patterns...)
method call and ends with a Report(message)
method call.
There can be additional calls in between these two. For example, a Where(cond)
call applies constraints to a match to decide whether its accepted or rejected. So even if there is a match for a pattern, it won't produce a report message unless it satisfies a Where()
condition.
To learn more, check out the documentation and/or the source code.
- Example rule files: rules.go
- gorules format documentation
- dsl/fluent package reference
- ruleguard package reference
- Introduction article: EN, RU
- gogrep - underlying AST matching engine
- NoVerify: Dynamic Rules for Static Analysis