{"id":20730495,"url":"https://github.com/retailnext/stan","last_synced_at":"2025-10-14T02:18:56.918Z","repository":{"id":139302465,"uuid":"118846771","full_name":"retailnext/stan","owner":"retailnext","description":"A Go (golang) library for writing custom \"go vet\" style tests","archived":false,"fork":false,"pushed_at":"2018-10-19T18:28:47.000Z","size":48,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":42,"default_branch":"master","last_synced_at":"2025-01-18T00:22:54.487Z","etag":null,"topics":["ast","go","golang","static-analysis","testing"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/retailnext.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-01-25T01:46:43.000Z","updated_at":"2019-12-24T19:42:59.000Z","dependencies_parsed_at":"2024-06-20T02:35:23.072Z","dependency_job_id":"f8322ee5-7015-4b3b-ad9e-1bdc0ab31ff6","html_url":"https://github.com/retailnext/stan","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/retailnext%2Fstan","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/retailnext%2Fstan/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/retailnext%2Fstan/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/retailnext%2Fstan/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/retailnext","download_url":"https://codeload.github.com/retailnext/stan/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243012694,"owners_count":20221605,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ast","go","golang","static-analysis","testing"],"created_at":"2024-11-17T05:11:41.767Z","updated_at":"2025-10-14T02:18:51.870Z","avatar_url":"https://github.com/retailnext.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# stan\n\nShort for STatic ANalysis, stan's goal is to make it easier to write custom static analysis tests for your golang project.\n\nIn 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.\n\nstan's API and feature set are not stable.\n\n## Standard walk-the-AST approach\n\n```go\n// don't compare time.Time values with ==\n\nfor _, pkg := range stan.Pkgs(\"your/namespace/...\") {\n  timeDotTime := pkg.LookupType(\"time.Time\")\n\n  stan.WalkAST(pkg.Node, func(n ast.Node, ancs stan.Ancestors) {\n    binary, _ := n.(*ast.BinaryExpr)\n    if binary == nil {\n      return\n    }\n\n    if binary.Op != token.EQL \u0026\u0026 binary.Op != token.NEQ {\n      return\n    }\n\n    if types.Identical(pkg.TypeOf(binary.X), timeDotTime) {\n      t.Errorf(\"Use Equal() to compare time.Time values instead of == at %s\", pkg.Pos(binary))\n    }\n  })\n}\n```\n\n## Object based approach\n\n```go\n\n// find *csv.Writer users that call Flush() but never check Error()\n\nfor _, pkg := range stan.Pkgs(\"your/namespace/...\") {\n  naughtyWriters := make(map[types.Object]bool)\n\n  csvWriterFlush := pkg.LookupObject(\"encoding/csv.Writer.Flush\")\n  for _, inv := range pkg.InvocationsOf(csvWriterFlush) {\n    naughtyWriters[inv.Invocant] = true\n  }\n\n  csvWriterError := pkg.LookupObject(\"encoding/csv.Writer.Error\")\n  for _, inv := range pkg.InvocationsOf(csvWriterError) {\n    delete(naughtyWriters, inv.Invocant)\n  }\n\n  for naughty := range naughtyWriters {\n    t.Errorf(\"*csv.Writer calls Flush() but not Error() at %s\", pkg.Pos(naughty))\n  }\n}\n```\n\n## Test your static tests\n\n```go\n\nfunc checkUseTimeEqual(pkg *stan.Package) []error {\n  // above test to catch code comparing time.Time with ==\n}\n\nfunc TestUseTimeEqual(t *testing.T) {\n  // invoke static check on your code\n  for _, pkg := range stan.Pkgs(\"your/namespace/...\") {\n    for _, err := range checkUseTimeEqual(pkg) {\n      t.Error(err)\n    }\n  }\n\n  // unit test your static test\n  pkg := stan.EvalPkg(`\npackage fake\n\nimport \"time\"\n\nfunc foo() {\n  now := time.Now()\n  if now != now.Round(0) {\n    panic(\"oops!\")\n  }\n}\n`)\n\n  errs := checkUseTimeEqual(pkg)\n\n  if len(errs) != 1 {\n    t.Error(\"expected an error\")\n  }\n\n  pkg = stan.EvalPkg(`\npackage fake\n\nimport \"time\"\n\nfunc foo() {\n  now := time.Now()\n  if !now.Equal(now.Round(0)) {\n    panic(\"oops!\")\n  }\n}\n`)\n\n  errs = checkUseTimeEqual(pkg)\n\n  if len(errs) != 0 {\n    t.Error(\"expected no errors\")\n  }\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fretailnext%2Fstan","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fretailnext%2Fstan","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fretailnext%2Fstan/lists"}