• Stars
    star
    785
  • Rank 57,957 (Top 2 %)
  • Language
    Go
  • License
    BSD 3-Clause "New...
  • Created almost 5 years ago
  • Updated 3 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Define and run pattern-based custom linting rules.

go-ruleguard

Build Status Build Status PkgGoDev

Logo

Overview

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 it's dedicated to Go only.

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
  • Not restricted to AST rules; it's possible to write a comment-related rule, for example
  • Rules can be installed and distributed as Go modules
  • Rules can be developed, debugged and profiled using the conventional Go tooling
  • Integrated into golangci-lint

It can also be easily embedded into other static analyzers. go-critic can be used as an example.

Quick start

It's advised that you get a binary from the latest release {linux/amd64, linux/arm64, darwin/amd64, darwin/arm64, windows/amd64, windows/arm64}.

If you want to install the ruleguard from source, it's as simple as:

# Installs a `ruleguard` binary under your `$(go env GOPATH)/bin`
$ go install -v github.com/quasilyte/go-ruleguard/cmd/ruleguard@latest

# Get the DSL package (needed to execute the ruleguard files)
$ go get -v -u github.com/quasilyte/go-ruleguard/dsl@latest

If inside a Go module, the dsl package will be installed for the current module, otherwise it installs the package into the $GOPATH and it will be globally available.

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
    	comma-separated list of ruleguard file paths
  -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 rules.go file:

//go:build ruleguard
// +build ruleguard

package gorules

import "github.com/quasilyte/go-ruleguard/dsl"

func dupSubExpr(m dsl.Matcher) {
	m.Match(`$x || $x`,
		`$x && $x`,
		`$x | $x`,
		`$x & $x`).
		Where(m["x"].Pure).
		Report(`suspicious identical LHS and RHS`)
}

func boolExprSimplify(m dsl.Matcher) {
	m.Match(`!($x != $y)`).Suggest(`$x == $y`)
	m.Match(`!($x == $y)`).Suggest(`$x != $y`)
}

func exposedMutex(m dsl.Matcher) {
	isExported := func(v dsl.Var) bool {
		return v.Text.Matches(`^\p{Lu}`)
	}

	m.Match(`type $name struct { $*_; sync.Mutex; $*_ }`).
		Where(isExported(m["name"])).
		Report("do not embed sync.Mutex")

	m.Match(`type $name struct { $*_; sync.RWMutex; $*_ }`).
		Where(isExported(m["name"])).
		Report("do not embed sync.RWMutex")
}

Create a test example.go target file:

package main

import "sync"

type EmbedsMutex struct {
	key int
	sync.Mutex
}

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 rules.go -fix example.go
example.go:5:1: exposedMutex: do not embed sync.Mutex (rules.go:24)
example.go:12:10: boolExprSimplify: suggestion: v1 == v2 (rules.go:15)
example.go:13:10: boolExprSimplify: suggestion: v1 != v2 (rules.go:16)
example.go:14:5: dupSubExpr: suspicious identical LHS and RHS (rules.go:7)

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 the pattern debugging:

$ ruleguard -e 'm.Match(`!($x != $y)`)' example.go
example.go:12:10: !(v1 != v2)

It automatically inserts Report("$$") into the specified pattern.

You can use -debug-group <name> flag to see explanations on why some rules rejected the match (e.g. which Where() condition failed).

The -e generated rule will have e name, so it can be debugged as well.

How does it work?

First, it parses ruleguard files (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 is written in terms of dsl API. Ruleguard files contain a set of functions that serve as a rule groups. Every such function accepts a single dsl.Matcher argument that is then used to define and configure rules inside the group.

A rule definition always starts with Match(patterns...) method call and ends with 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 it's 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.

Troubleshooting

For ruleguard to work the dsl package must be available at runtime. If it's not, you are going to see an error like:

$ ruleguard -rules rules.go .
ruleguard: load rules: parse rules file: typechecker error: rules.go:6:8: could not import github.com/quasilyte/go-ruleguard/dsl (can't find import: "github.com/quasilyte/go-ruleguard/dsl")

This is fixed by adding the dsl package to the module:

$ ruleguard-test go get github.com/quasilyte/go-ruleguard/dsl
go: downloading github.com/quasilyte/go-ruleguard v0.3.18
go: downloading github.com/quasilyte/go-ruleguard/dsl v0.3.21
go: added github.com/quasilyte/go-ruleguard/dsl v0.3.21
$ ruleguard-test ruleguard -rules rules.go .
.../test.go:6:5: boolExprSimplify: suggestion: 1 == 0 (rules.go:9)

If you have followed past advise of using a build constraint in the rules.go file, like this:

$ ruleguard-test head -4 rules.go
//go:build ignore
// +build ignore

package gorules

you are going to notice that go.mod file lists the dsl as an indirect dependency:

$ grep dsl go.mod
require github.com/quasilyte/go-ruleguard/dsl v0.3.21 // indirect

If you run go mod tidy now, you are going to notice the dsl package disappears from the go.mod file:

$ go mod tidy
$ grep dsl go.mod
$ 

This is because go mod tidy behaves as if all the build constraints are in effect, with the exception of ignore. This is documented in the go website.

This is fixed by using a different build constraint, like ruleguard or rules.

Documentation

Rule set examples

Note: go-critic and go-perfguard embed the rules using the IR precompilation feature.

Extra references

More Repositories

1

roboden-game

An indirect control real-time strategy game about robot colonies
Go
425
star
2

goism

Not a fan of Emacs Lisp? Hack Emacs in Go!
Go
346
star
3

go-consistent

Source code analyzer that helps you to make your Go programs more consistent.
Go
333
star
4

phpgrep

Syntax-aware grep for PHP code.
Go
236
star
5

gopherkon

Go mascot image constructor. Create your cute own gopher.
TypeScript
198
star
6

go-parsefix

Fixes simple parse errors automatically. Works great in combination with goimports.
Go
84
star
7

pathing

A very fast & zero-allocation, grid-based, pathfinding library for Go.
Go
79
star
8

go-perfguard

CPU-guided performance analyzer for Go
Go
75
star
9

ebitengine-input

A Godot-inspired action input handling system for Ebitengine
Go
70
star
10

go-jdk

Run JVM-based code in Go efficiently
Go
70
star
11

qpprof

A helper tool to work with profile.proto (pprof) files
Go
60
star
12

gocorpus

The code used to serve gocorpus application
Go
51
star
13

astnorm

AST normalization experiment
Go
45
star
14

gophers-and-dragons

Rogue-like game for Go programmers.
Go
44
star
15

gogrep

Syntax-aware Go code search, based on the mvdan/gogrep
Go
38
star
16

go-namecheck

Source code analyzer that helps you to maintain variable/field naming conventions inside your project.
Go
38
star
17

ge

ebiten-based game engine for Go
Go
32
star
18

talks

A collection of slides, notes and other related stuff from talks I have given.
27
star
19

kphp-game

Simple KPHP game, a proof of concept thing (demo)
PHP
26
star
20

decipherism-game

A puzzle game where you solve the encoding machine ciphers
Go
26
star
21

perf-heatmap

Create a heatmap index based on the profile.proto profile data
Go
24
star
22

go-benchrun

Convenience wrapper around "go test" + "benchstat".
Go
22
star
23

gdata

A gamedata package that provides convenient cross-platform storage for games
Go
21
star
24

qbenchstat

My personal, slightly improved version of benchstat utility
Go
20
star
25

parsing-and-go

Go
19
star
26

repolint

Tool to check github user/organization repositories for some simple and common issues.
Go
19
star
27

awesome-kphp

A curated list of amazingly awesome KPHP libraries, resources and software
18
star
28

ebitengine-resource

A resource manager for Ebitengine
Go
17
star
29

concat

Demo repository for habr.com article about faster Go string concatenation.
Go
17
star
30

xm

XM package provides Ebitengine-compatible mod music decoder
Go
16
star
31

uber-rules

A set of ruleguard rules that try to cover some parts of the Uber Go Style Guide (https://github.com/uber-go/guide)
Go
16
star
32

gmath

A Godot-inspired math library for Ebintengine and other Go game engines
Go
15
star
33

regex

Regular expression libraries for Go
Go
15
star
34

sinecord

Create music by the power of math!
Go
14
star
35

yaml5

YAML5 - use YAML like it's JSON5.
Go
14
star
36

quasigo

quasigo is a Go subset interpreter written in Go
Go
13
star
37

cavebots-game

My LD54 game
Go
12
star
38

ebitengine-graphics

A package implementing Graphics primitives for gscene package
Go
11
star
39

go-complex-nums-emulation

Emulating builtin complex numbers with structs of floats and measuring the results
Go
10
star
40

fileprivate

A Go linter that enforces more strict members access rules inside packages
Go
9
star
41

hello-go

Go
9
star
42

gccgo_vs_gc

Comparing GCCGO 1.8.1 (GCC 7.2) vs GC 1.8.1 (and GC 1.10) on x86 (AMD64).
Shell
9
star
43

go-contributing-ru

Go contributing related information in Russian.
Go
9
star
44

avx512test

Utility that was used to generate initial Go AVX-512 encoder test suite.
Assembly
9
star
45

inltest

Package inltest helps you to test that performance-sensitive funcs are inlineable.
Go
9
star
46

gmtk2023

A 2D real-time strategy game made for a GMTK2023 game jam
Go
8
star
47

vscode-gogrep

Structural, syntax-aware search for Go code for VS Code.
TypeScript
8
star
48

benchstat.el

Proper Emacs Lisp benchmarking made simple.
Emacs Lisp
8
star
49

KLua

KLua is a FFI-based Lua5 library that can be used in both PHP and KPHP
PHP
8
star
50

blog-src

quasilyte.github.io sources
CSS
8
star
51

bitfontier

A bitmap font maker for Go
Go
8
star
52

gsignal

A lightweight Godot/Qt inspired signals and slots for Go
Go
7
star
53

KTemplate

KTemplate is a simple text template engine for PHP and KPHP
PHP
6
star
54

vscode-perf-heatmap

TypeScript
6
star
55

gophercon2021-ruleguard

GopherCon Russia 2021 ruleguard workshop
Go
6
star
56

phpsmith

phpsmith creates random PHP and KPHP programs to test their compilers and runtimes
Go
5
star
57

pratt-parsers-go

Pratt parser implemented in Go
Go
5
star
58

KSQLite

KSQLite is a FFI-based SQLite library that can be used in both PHP and KPHP
PHP
5
star
59

quasisolar-mission

The "Quasisolar Mission" game source code
C#
5
star
60

YALWEE

System for generating JIT capable interpreters
Assembly
5
star
61

n2o.el

Nitrous - extra Emacs Lisp optimizer. Transparently makes Emacs faster.
Emacs Lisp
4
star
62

go-n2o

Go external optimizer.
Go
4
star
63

vscode-phpgrep

Structural, syntax-aware search for PHP code for VS Code.
TypeScript
4
star
64

bitsweetfont

A bitmap font for Go, made with bitfontier
Go
4
star
65

gslices

This is my own slices package for go, because I don't like the stdlib API
Go
4
star
66

regexp-lint

Code used to serve regexp-lint application
Go
3
star
67

hiddensugar-game

Go
3
star
68

http-profiling

Go
3
star
69

kphp-sdlite

Simple SDL framework for KPHP
PHP
3
star
70

ktest

Test and benchmark KPHP code
Go
3
star
71

emacs-lispeed

Lispeed = Lisp + Speed
3
star
72

phpgrep-contrib

Extra utilities and docs for the phpgrep
Shell
3
star
73

cffi

Lazy way to call CGo functions.
Go
3
star
74

textocat-php-sdk

utility for using http service
PHP
2
star
75

go-unexport

Unexport symbols from a package under a workspace automatically.
Go
2
star
76

alley-of-reading

Keeping track of books and articles I've read as well as keeping notes about some of them.
2
star
77

devtools

Shared repository for Go developer tools.
Go
2
star
78

XEDq

XEDq brings Intel XED powers into Go space.
Go
2
star
79

gnu-riscv32_ext

Extending GCC riscv32 compiler and spike emulator
Shell
2
star
80

shmup-game

Go
2
star
81

grad_work

Course/Graduate work (which is currently named as "Resembler")
C++
2
star
82

go-perftune

Helper tool for manual Go code optimization.
Go
2
star
83

gopher-arts

Gopher drawings with permissive license. Use them as you like, but preferably for a positive purpose.
2
star
84

kphp-uuid

A simple demo KPHP project
PHP
1
star
85

sonic-pi-tracks

Source code of my Sonic Pi tracks.
Ruby
1
star
86

typ.el

Type inference framework for Emacs. Build better APIs, tools, linters and optimizers with type info!
Emacs Lisp
1
star
87

stdinfo

Go
1
star
88

tquest

A text quest/dialogue tree execution engine for games written in Go
1
star
89

Perl6Scheme

Scheme subset implementation in Perl6. This is a toy.
Perl 6
1
star
90

xedmap

Mappings between XED names and terms to other widespread forms.
Go
1
star
91

Emacs-Lisp-VM

Emacs Lisp bytecode interpreter implemented in Go
Go
1
star
92

RAGF

Raaagf! Red Assembly Goez Fasta!
C++
1
star
93

gscene

A lightweight scene package for Ebitengine
Go
1
star
94

textocat-racket-sdk

Racket
1
star
95

PragMacro

C language extensions via pragma
C
1
star
96

ld55-game

Go
1
star
97

pprofutil

Helper functions for working with profile.proto objects
Go
1
star
98

ebitengine-sound

Audio-related helpers like playlists and sound queues
Go
1
star
99

ktemplate-playground

Sources for the KTemplate playground
TypeScript
1
star
100

kphp-batteries

Unofficial package that provides some PHP functions that are not implemented in KPHP stdlib.
PHP
1
star