Skip to content

A Go (golang) library for writing custom "go vet" style tests

License

BSD-3-Clause, BSD-3-Clause licenses found

Licenses found

BSD-3-Clause
LICENSE
BSD-3-Clause
GO_LICENSE
Notifications You must be signed in to change notification settings

retailnext/stan

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

// 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

// 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

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")
  }
}

About

A Go (golang) library for writing custom "go vet" style tests

Topics

Resources

License

BSD-3-Clause, BSD-3-Clause licenses found

Licenses found

BSD-3-Clause
LICENSE
BSD-3-Clause
GO_LICENSE

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages