mirror of
https://github.com/coder/coder.git
synced 2026-06-03 13:08:25 +00:00
e6ee7dd652
* chore: Add linting rule to help catch InTx misuse This isn't perfect, as if you nest your misuse in another code block like an if statement, it won't catch it :/. It is better than nothing
118 lines
3.4 KiB
Go
118 lines
3.4 KiB
Go
// Package gorules defines custom lint rules for ruleguard.
|
|
//
|
|
// golangci-lint runs these rules via go-critic, which includes support
|
|
// for ruleguard. All Go files in this directory define lint rules
|
|
// in the Ruleguard DSL; see:
|
|
//
|
|
// - https://go-ruleguard.github.io/by-example/
|
|
// - https://pkg.go.dev/github.com/quasilyte/go-ruleguard/dsl
|
|
//
|
|
// You run one of the following commands to execute your go rules only:
|
|
// golangci-lint run
|
|
// golangci-lint run --disable-all --enable=gocritic
|
|
// Note: don't forget to run `golangci-lint cache clean`!
|
|
package gorules
|
|
|
|
import (
|
|
"github.com/quasilyte/go-ruleguard/dsl"
|
|
)
|
|
|
|
// Use xerrors everywhere! It provides additional stacktrace info!
|
|
//nolint:unused,deadcode,varnamelen
|
|
func xerrors(m dsl.Matcher) {
|
|
m.Import("errors")
|
|
m.Import("fmt")
|
|
m.Import("golang.org/x/xerrors")
|
|
|
|
m.Match("fmt.Errorf($*args)").
|
|
Suggest("xerrors.New($args)").
|
|
Report("Use xerrors to provide additional stacktrace information!")
|
|
|
|
m.Match("errors.$_($msg)").
|
|
Where(m["msg"].Type.Is("string")).
|
|
Suggest("xerrors.New($msg)").
|
|
Report("Use xerrors to provide additional stacktrace information!")
|
|
}
|
|
|
|
// databaseImport enforces not importing any database types into /codersdk.
|
|
//nolint:unused,deadcode,varnamelen
|
|
func databaseImport(m dsl.Matcher) {
|
|
m.Import("github.com/coder/coder/coderd/database")
|
|
m.Match("database.$_").
|
|
Report("Do not import any database types into codersdk").
|
|
Where(m.File().PkgPath.Matches("github.com/coder/coder/codersdk"))
|
|
}
|
|
|
|
// doNotCallTFailNowInsideGoroutine enforces not calling t.FailNow or
|
|
// functions that may themselves call t.FailNow in goroutines outside
|
|
// the main test goroutine. See testing.go:834 for why.
|
|
//nolint:unused,deadcode,varnamelen
|
|
func doNotCallTFailNowInsideGoroutine(m dsl.Matcher) {
|
|
m.Import("testing")
|
|
m.Match(`
|
|
go func($*_){
|
|
$*_
|
|
$require.$_($*_)
|
|
$*_
|
|
}($*_)`).
|
|
At(m["require"]).
|
|
Where(m["require"].Text == "require").
|
|
Report("Do not call functions that may call t.FailNow in a goroutine, as this can cause data races (see testing.go:834)")
|
|
|
|
m.Match(`
|
|
go func($*_){
|
|
$*_
|
|
$t.$fail($*_)
|
|
$*_
|
|
}($*_)`).
|
|
At(m["fail"]).
|
|
Where(m["t"].Type.Implements("testing.TB") && m["fail"].Text.Matches("^(FailNow|Fatal|Fatalf)$")).
|
|
Report("Do not call functions that may call t.FailNow in a goroutine, as this can cause data races (see testing.go:834)")
|
|
}
|
|
|
|
// InTx checks to ensure the database used inside the transaction closure is the transaction
|
|
// database, and not the original database that creates the tx.
|
|
func InTx(m dsl.Matcher) {
|
|
// ':=' and '=' are 2 different matches :(
|
|
m.Match(`
|
|
$x.InTx(func($y) error {
|
|
$*_
|
|
$*_ = $x.$f($*_)
|
|
$*_
|
|
})
|
|
`, `
|
|
$x.InTx(func($y) error {
|
|
$*_
|
|
$*_ := $x.$f($*_)
|
|
$*_
|
|
})
|
|
`).Where(m["x"].Text != m["y"].Text).
|
|
At(m["f"]).
|
|
Report("Do not use the database directly within the InTx closure. Use '$y' instead of '$x'.")
|
|
|
|
//When using a tx closure, ensure that if you pass the db to another
|
|
//function inside the closure, it is the tx.
|
|
//This will miss more complex cases such as passing the db as apart
|
|
//of another struct.
|
|
m.Match(`
|
|
$x.InTx(func($y database.Store) error {
|
|
$*_
|
|
$*_ = $f($*_, $x, $*_)
|
|
$*_
|
|
})
|
|
`, `
|
|
$x.InTx(func($y database.Store) error {
|
|
$*_
|
|
$*_ := $f($*_, $x, $*_)
|
|
$*_
|
|
})
|
|
`, `
|
|
$x.InTx(func($y database.Store) error {
|
|
$*_
|
|
$f($*_, $x, $*_)
|
|
$*_
|
|
})
|
|
`).Where(m["x"].Text != m["y"].Text).
|
|
At(m["f"]).Report("Pass the tx database into the '$f' function inside the closure. Use '$y' over $x'")
|
|
}
|