package golinters import ( "context" "fmt" "go/ast" "go/token" "strings" "github.com/golangci/golangci-lint/pkg/lint/linter" "github.com/golangci/golangci-lint/pkg/result" ) type Gochecknoglobals struct{} func (Gochecknoglobals) Name() string { return "gochecknoglobals" } func (Gochecknoglobals) Desc() string { return "Checks that no globals are present in Go code" } func (lint Gochecknoglobals) Run(ctx context.Context, lintCtx *linter.Context) ([]result.Issue, error) { var res []result.Issue for _, f := range lintCtx.ASTCache.GetAllValidFiles() { res = append(res, lint.checkFile(f.F, f.Fset)...) } return res, nil } func (lint Gochecknoglobals) checkFile(f *ast.File, fset *token.FileSet) []result.Issue { var res []result.Issue for _, decl := range f.Decls { genDecl, ok := decl.(*ast.GenDecl) if !ok { continue } if genDecl.Tok != token.VAR { continue } for _, spec := range genDecl.Specs { valueSpec := spec.(*ast.ValueSpec) for _, vn := range valueSpec.Names { if isWhitelisted(vn) { continue } res = append(res, result.Issue{ Pos: fset.Position(vn.Pos()), Text: fmt.Sprintf("%s is a global variable", formatCode(vn.Name, nil)), FromLinter: lint.Name(), }) } } } return res } func isWhitelisted(i *ast.Ident) bool { return i.Name == "_" || i.Name == "version" || looksLikeError(i) } // looksLikeError returns true if the AST identifier starts // with 'err' or 'Err', or false otherwise. // // TODO: https://github.com/leighmcculloch/gochecknoglobals/issues/5 func looksLikeError(i *ast.Ident) bool { prefix := "err" if i.IsExported() { prefix = "Err" } return strings.HasPrefix(i.Name, prefix) }