https://github.com/retailnext/stan
A Go (golang) library for writing custom "go vet" style tests
https://github.com/retailnext/stan
ast go golang static-analysis testing
Last synced: 9 months ago
JSON representation
A Go (golang) library for writing custom "go vet" style tests
- Host: GitHub
- URL: https://github.com/retailnext/stan
- Owner: retailnext
- License: bsd-3-clause
- Created: 2018-01-25T01:46:43.000Z (over 8 years ago)
- Default Branch: master
- Last Pushed: 2018-10-19T18:28:47.000Z (over 7 years ago)
- Last Synced: 2025-01-18T00:22:54.487Z (over 1 year ago)
- Topics: ast, go, golang, static-analysis, testing
- Language: Go
- Homepage:
- Size: 46.9 KB
- Stars: 1
- Watchers: 42
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# stan
Short for STatic ANalysis, stan's goal is to make it easier to write custom static analysis tests for your golang project.
In addition to *ast.Package, *types.Info and *types.Package, stan provides a higher level API to make it easier when your objective relates to particular objects/types.
stan's API and feature set are not stable.
## Standard walk-the-AST approach
```go
// don't compare time.Time values with ==
for _, pkg := range stan.Pkgs("your/namespace/...") {
timeDotTime := pkg.LookupType("time.Time")
stan.WalkAST(pkg.Node, func(n ast.Node, ancs stan.Ancestors) {
binary, _ := n.(*ast.BinaryExpr)
if binary == nil {
return
}
if binary.Op != token.EQL && binary.Op != token.NEQ {
return
}
if types.Identical(pkg.TypeOf(binary.X), timeDotTime) {
t.Errorf("Use Equal() to compare time.Time values instead of == at %s", pkg.Pos(binary))
}
})
}
```
## Object based approach
```go
// find *csv.Writer users that call Flush() but never check Error()
for _, pkg := range stan.Pkgs("your/namespace/...") {
naughtyWriters := make(map[types.Object]bool)
csvWriterFlush := pkg.LookupObject("encoding/csv.Writer.Flush")
for _, inv := range pkg.InvocationsOf(csvWriterFlush) {
naughtyWriters[inv.Invocant] = true
}
csvWriterError := pkg.LookupObject("encoding/csv.Writer.Error")
for _, inv := range pkg.InvocationsOf(csvWriterError) {
delete(naughtyWriters, inv.Invocant)
}
for naughty := range naughtyWriters {
t.Errorf("*csv.Writer calls Flush() but not Error() at %s", pkg.Pos(naughty))
}
}
```
## Test your static tests
```go
func checkUseTimeEqual(pkg *stan.Package) []error {
// above test to catch code comparing time.Time with ==
}
func TestUseTimeEqual(t *testing.T) {
// invoke static check on your code
for _, pkg := range stan.Pkgs("your/namespace/...") {
for _, err := range checkUseTimeEqual(pkg) {
t.Error(err)
}
}
// unit test your static test
pkg := stan.EvalPkg(`
package fake
import "time"
func foo() {
now := time.Now()
if now != now.Round(0) {
panic("oops!")
}
}
`)
errs := checkUseTimeEqual(pkg)
if len(errs) != 1 {
t.Error("expected an error")
}
pkg = stan.EvalPkg(`
package fake
import "time"
func foo() {
now := time.Now()
if !now.Equal(now.Round(0)) {
panic("oops!")
}
}
`)
errs = checkUseTimeEqual(pkg)
if len(errs) != 0 {
t.Error("expected no errors")
}
}
```