Skip to content
Define and run pattern-based custom linting rules
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Type Name Latest commit message Commit time
Failed to load latest commit information.
analyzer rules.go: add empty var/const/type decl check (#44) Jan 10, 2020
docs update logo Dec 23, 2019
dsl/fluent all: add Var.Type.Size and Var.Assignable predicates Dec 26, 2019
internal/ remove module files for now Dec 14, 2019
ruleguard ruleguard: support const exprs as DSL arguments (#33) Jan 6, 2020
.gitignore ruleguard: support const exprs as DSL arguments (#33) Jan 6, 2020
LICENSE Create LICENSE Dec 17, 2019 readme: add RU version of the intro article Jan 8, 2020
rules.go rules.go: add empty var/const/type decl check (#44) Jan 10, 2020


Build Status GoDoc Go Report Card



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.


  • 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.

Quick start

To install ruleguard binary under your $(go env GOPATH)/bin:

$ go get -v -u

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]

  -rules string
    	path to a rules.go file
  -e string
    	execute a single rule from a given string
    	apply all suggested fixes
  -c int
    	display offending line with this many lines of context (default -1)
    	emit JSON output

Create a test example.rules.go file:

// +build ignore

package gorules

import ""

func _(m fluent.Matcher) {
	m.Match(`$x || $x`,
		`$x && $x`).
		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.

How does it work?

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.


Extra references

You can’t perform that action at this time.