From 713b95d20b8c016d45a77be367970c6d74fd4838 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Thu, 2 Jun 2022 08:41:20 -0500 Subject: [PATCH 1/2] 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 --- scripts/rules.go | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/scripts/rules.go b/scripts/rules.go index 6113ea40e1676..1c787f4cb08e2 100644 --- a/scripts/rules.go +++ b/scripts/rules.go @@ -69,3 +69,38 @@ func doNotCallTFailNowInsideGoroutine(m dsl.Matcher) { 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) { + m.Import("github.com/coder/coder/coderd/database") + + m.Match(` + $x.InTx(func($y database.Store) error { + $*_ + $*_ = $x.$f($*_) + $*_ + }) + `).Where(m["x"].Text != m["y"].Text && m["x"].Type.Implements("database.Store")). + 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, $*_) + $*_ + }) + `).Where(m["x"].Text != m["y"].Text && m["x"].Type.Implements("database.Store")). + At(m["f"]).Report("Pass the tx database into the '$f' function inside the closure. Use '$y' over $x'") +} From c856e8d762205f29435408c998c447b59415ec8e Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Thu, 2 Jun 2022 09:33:51 -0500 Subject: [PATCH 2/2] update InTx ruleguard --- scripts/rules.go | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/scripts/rules.go b/scripts/rules.go index 1c787f4cb08e2..e0890598c9e87 100644 --- a/scripts/rules.go +++ b/scripts/rules.go @@ -73,22 +73,27 @@ func doNotCallTFailNowInsideGoroutine(m dsl.Matcher) { // 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) { - m.Import("github.com/coder/coder/coderd/database") - + // ':=' and '=' are 2 different matches :( m.Match(` - $x.InTx(func($y database.Store) error { + $x.InTx(func($y) error { $*_ $*_ = $x.$f($*_) $*_ }) - `).Where(m["x"].Text != m["y"].Text && m["x"].Type.Implements("database.Store")). + `, ` + $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. + //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 { $*_ @@ -96,11 +101,17 @@ func InTx(m dsl.Matcher) { $*_ }) `, ` + $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 && m["x"].Type.Implements("database.Store")). + `).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'") }