Check that no globals are present in Go code.

gochecknoglobals

test Go Report Card

Check that no globals are present in Go code.

Why

Global variables are an input to functions that is not visible in the functions signature, complicate testing, reduces readability and increase the complexity of code.

https://peter.bourgon.org/blog/2017/06/09/theory-of-modern-go.html https://twitter.com/davecheney/status/871939730761547776

Exceptions

There are very few exceptions to the global variable rule. This tool will ignore the following patterns:

  • Variables with an Err prefix
  • Variables named _
  • Variables named version
  • Variables assigned from regexp.MustCompile()
  • Variables with a //go:embed comment

Install

go get 4d63.com/gochecknoglobals

Usage

The linter is built on Go's analysis package and does thus support all the built in flags and features from this type. The analyzer is executed by specifying packages.

gochecknoglobals [package]
gochecknoglobals ./...

By default, test files will not be checked but can be included by adding the -t flag.

gochecknoglobals -t [package]
Comments
  • Add support to whitelist selector expressions

    Add support to whitelist selector expressions

    This will allow a pre defined list of selector expressions X and Sel idents to mark as whitelisted. By doing this we could allow a specific type or function from a specific package such as regexp.MustCompile.


    I had a discussion today regarding how to handle things such as regexp.MustCompile() or pre defined errors as a global variable which this linter doesn't really support. I know you can ignore errors by using specific variable names but I also saw that there were some TODO makers to skip fake errors. I also found #6 which addresses the same issue I have and that you were open to PRs.

    I guess I could and should've asked in the ticket but I thought I had a go at an implementation to handle this. Since you're already open to match the name of the variable, would you be open to define a list of selector expression value that could be whitelisted?

    As seen in the PR I created a 2D array which holds an X ident and a Sel ident which will be matched. I added three defaults to test this; errors New, fmt Errorf and regexp MustCompile. If this is a feasible solution this could even be configurable to allow any expressions, like http Client I use in the test. Regarding errors specific I guess a check if it implements the Error() interface would be better but I still see a use case for a feature like this.

    I added a new test file to show the behaviour and also updated the existing tests. Please let me know what you think! If this is not the way you want to go I totally understand and will close this PR.

  • Use analysis.Analyzer

    Use analysis.Analyzer

    I did a quick update for the linter to use Go's analyzer which has some great features. This linter no longer has to figure out paths to files, tests, filenames, lines etc. It's also super easy to maintain tests since you just add diagnostics to your test package and write a oneliner test using analysistest. I had one issue with testing test files as you can see which resulted in an upstream issue here. The test code generated will have global variables which will make the test not pass.

    The main reason however was to address golangci-lint#1393 and make it easier to keep golangci-lint in sync with this linter.

    After merging this it will be super easy to just add this analyzer to golangci-lint like this.

    Please let me know if you think this is a good idea or if you want any changes to this approach!

  • Ignore variables in test packages/files

    Ignore variables in test packages/files

    It is possible to initialize test variables by TestMain. However global variables are the only way to share data with TestMain and tests. Can we ignore global variables in _test packages or _test.go files in general?

  • How to handle `regexp.MustCompile`

    How to handle `regexp.MustCompile`

    I have a couple of calls to regexp.MustCompile which, in my opinion, make sense to be stored in a global value because they ensure that the regex is valid at compile time. Would it make sense to add an exception for cases like that? So to whitelist a couple of functions that work like regexp.MustCompile from the standard library?

    An example can be found here: https://github.com/mastertinner/latest/blob/master/internal/app/latest/brew/upgrade.go#L13

  • Ignore global error variables (implements #2)

    Ignore global error variables (implements #2)

    Error variables are variables starting with 'err' or 'Err' which were made by either errors.New or fmt.Errorf.

    There still is leeway for improvement such as checking if the node implements the error interface. This would allow us to whitelist custom error variables.

    Fixes #2

  • Ignore global vars prefixed with `{E,e}rr`

    Ignore global vars prefixed with `{E,e}rr`

    x.go:11::warning: ErrHandlerAlreadyExists is a global variable (gochecknoglobals)
    

    It's a common practice to create global error variables such as the one above. It might be wise to not warn about them. What do you think?

  • Accept global var when only one single Go file in the package containing the `func main()`

    Accept global var when only one single Go file in the package containing the `func main()`

    I have an app having the following layout:

    ├── cmd
    │   └── myprj
    │       └── main.go       # package main
    └── pkg
        └── myprj
            ├── featureA.go   # package myprj
            └── featureB.go   # package myprj
    

    The file main.go looks like the following:

    [...]
    
    //nolint:gochecknoglobals
    var (
    	url     = flag.String("url", "", "Host and port")
    	id      = flag.String("id", "", "Identifier")
    	token   = flag.String("token", "", "Authentication Token")
    	table   = flag.String("table", "", "Table name")
    	key     = flag.String("key", "", "Key name")
    	record  = flag.String("record", "", "Record name")
    	symbol  = flag.String("symbol", "", "Symbol ID")
    	topic   = flag.String("topic", "", "Topic ID")
    	start   = flag.String("start", "", "Start time")
    	stop    = flag.String("stop", "", "Start time")
    	limit   = flag.Uint("limit", 0, "Max")
    	columns = flag.String("columns", "", "Column names")
        [...]
        // more flags
    )
    
    func main() {
    	flag.Parse()
    	log.SetLevel(log.DebugLevel)
    	log.Infof("config: -url     = %v", *url)
    	log.Infof("config: -id      = %v", *id)
    	log.Infof("config: -token   = %v", *token)
    	log.Infof("config: -table   = %v", *table)
    	log.Infof("config: -key     = %v", *key)
    	log.Infof("config: -record  = %v", *record)
    	log.Infof("config: -symbol  = %v", *symbol)
    	log.Infof("config: -topic   = %v", *topic)
    	log.Infof("config: -start   = %v", *start)
    	log.Infof("config: -stop    = %v", *stop)
    	log.Infof("config: -limit   = %v", *limit)
    	log.Infof("config: -columns = %v", *columns)
        [...]
        // more flags
    
        [...]
    
        // processing: calling functions in the package myprj
    }
    

    I want to remove the line //nolint:gochecknoglobals.

    I could copy the global variables witin the main() body, but the number of lines becomes too high.

    However, I think in such cases, global variable is acceptable:

    1. the package containing the function main() is composed of one signgle Go file.
    2. the signgle Go file contains very few functions (in such cases, the stuff is in the pkg folder).

    There is no issue with global variable in a tiny main.go file like that.

  • Exclude

    Exclude "Version" global variable

    Many projects populate a "Version" variable at build-time using ldflags. For instance, a Makefile might set a Makefile variable to the latest Git tag and rev, and pass it to a "Version" variable through -X.

    Doing so makes it easy for binaries to include version info (e.g. for a CLI program to print the version number when passed the -v flag). This is a good use-case for a global variable and shouldn't be considered an error, as long as there's only one per project.

  • Ignore global variables prefixed with //go:embed

    Ignore global variables prefixed with //go:embed

    Right now I have to do the following with golangci-lint:

    // Package assets contains templates.
    package assets
    
    import "embed"
    
    var (
            //go:embed templates
            assets embed.FS //nolint: gochecknoglobals // no other possibility
    )
    
    // Get gets the templates in templates folder as embed.FS.
    func Get() embed.FS {
            return assets
    }
    

    Global variables prefixed with the new go 1.16 feature //go:embed should be ingored (like it already does for regular expressions).

    The corresponding golangci-lint issue: https://github.com/golangci/golangci-lint/issues/1727

  • Add exception for version

    Add exception for version

    Current best practice for setting the version of a Go executable requires a global variable. This change adds a narrow exception for variables name Version exactly. It also adds tests to verify that and updates to README to explicitly state the permitted exceptions.

  • vanity import broken

    vanity import broken

    package 4d63.com/gochecknoglobals/checknoglobals: unrecognized import path "4d63.com/gochecknoglobals/checknoglobals": reading https://4d63.com/gochecknoglobals/checknoglobals?go-get=1: 404 Not Found
    
  • checks _test.go

    checks _test.go

    Default on...

    pixley@M-C02DQ6AMML85> golangci-lint --version
    golangci-lint has version 1.49.0 built from cc2d97f3 on 2022-08-24T10:24:37Z
    pixley@M-C02DQ6AMML85> uname -a
    Darwin M-C02DQ6AMML85 21.6.0 Darwin Kernel Version 21.6.0: Wed Aug 10 14:25:27 PDT 2022; root:xnu-8020.141.5~2/RELEASE_X86_64 x86_64
    

    checks _test.go files. I wish I could turn this off but I don't see any yaml config for this.

    So... two errors, one may not be yours. 1) golangci-lint should not check _test.go files by default and 2) I wish there were a yaml option for me to explicitly turn it off.

  • Add exception for template.Must

    Add exception for template.Must

    Hello,

    First, thank you for providing this linter! I am using it as part of golangci-lint. I am now working on a project that have some regex.Must in global variables and have noticed that these are excluded from linter when used as global variables.

    I assume that is because they are in this case compile-time errors if something goes wrong.

    Now I also need to do same thing with templates for policy documents which are part of HTTP request body. Template documents are defined as consts and in global variable I am doing following:

    	adminPolicyTemplate        = template.Must(template.New("AdminPolicy").Parse(AdminPolicy))
    	readerPolicyTemplate       = template.Must(template.New("ReaderPolicy").Parse(ReaderPolicy))
    

    which if anything is wrong with AdminPolicy template document or ReaderPolicy template document are compiler-ime errors, same as for regex. Therefore I think it would be nice to have same exception for template.Must as we have for regex.Must.

  • Improve global error detection

    Improve global error detection

    Only treat global error variables as such if they have an "err" or "Err" prefix and implement the error interface.

    Implements https://github.com/leighmcculloch/gochecknoglobals/issues/5.

  • Add support for ignoring generated files

    Add support for ignoring generated files

    Using the guidance here: https://pkg.go.dev/cmd/go/internal/generate

    This tool should ignore files which contain the ^// Code generated .* DO NOT EDIT\.$ regex match.

    This can be a flag or default, but it should allow for ignoring those files.

  • Best pattern with this code: should I move this global var in my func?

    Best pattern with this code: should I move this global var in my func?

    Using this code:

    var namePatterns = []map[string]string{
    	{`[^\pL-\s']`: ""}, // cut off everything except [alpha, hyphen, whitespace, apostrophe]
    	{`\s{2,}`: " "},    // trim more than two whitespaces to one
    	{`-{2,}`: "-"},     // trim more than two hyphens to one
    	{`'{2,}`: "'"},     // trim more than two apostrophes to one
    	{`( )*-( )*`: "-"}, // trim enclosing whitespaces around hyphen
    }
    
    var nameRegex = regexp.MustCompile(`[\p{L}]([\p{L}|[:space:]\-']*[\p{L}])*`)
    
    func onlyOne(s string) string {
    	for _, v := range namePatterns {
    		for f, r := range v {
    			s = regexp.MustCompile(f).ReplaceAllLiteralString(s, r)
    		}
    	}
    
    	return s
    }
    

    namePatterns is reported as a global variable which is correct, but what is the best pattern here?

    Should I move it in my onlyOne func?

    Isn't it slower doing so?

Go linter to check the struct literal to use field name

Structfield Find struct literals using non-labeled fields. The structfield analysis reports the usage of struct literal using non-labeled fields more

Aug 23, 2021
Log4j check with golang

log4jcheck Install go install github.com/michael1026/log4jcheck@latest Example Usage cat URLs | log4jcheck -user-agent -referer -server example.burpc

Dec 11, 2021
Clean-Swift source and test code auto-generator. It can save you time typing 500-600 lines of code.
Clean-Swift source and test code auto-generator. It can save you time typing 500-600 lines of code.

Clean-Swift source & test code auto generator Overview Run Output Basic Usage make config.yaml target_project_name: Miro // target project name copyri

Apr 13, 2022
Code generator that generates boilerplate code for a go http server

http-bootstrapper This is a code generator that uses go templates to generate a bootstrap code for a go http server. Usage Generate go http server cod

Nov 20, 2021
efaceconv - Code generation tool for high performance conversion from interface{} to immutable type without allocations.

efaceconv High performance conversion from interface{} to immutable types without additional allocations This is tool for go generate and common lib (

May 14, 2022
Type-driven code generation for Go

What’s this? gen is a code-generation tool for Go. It’s intended to offer generics-like functionality on your types. Out of the box, it offers offers

Jan 4, 2023
Versatile Go code generator.
Versatile Go code generator.

Generis Versatile Go code generator. Description Generis is a lightweight code preprocessor adding the following features to the Go language : Generic

Nov 30, 2022
Code Generation for Functional Programming, Concurrency and Generics in Golang

goderive goderive derives mundane golang functions that you do not want to maintain and keeps them up to date. It does this by parsing your go code fo

Dec 25, 2022
Golang source code parsing, usage like reflect package

gotype Golang source code parsing, usage like reflect package English 简体中文 Usage API Documentation Examples License Pouch is licensed under the MIT Li

Dec 9, 2022
Code generation tools for Go.

interfaces Code generation tools for Go's interfaces. Tools available in this repository: cmd/interfacer cmd/structer cmd/interfacer Generates an inte

Dec 23, 2022
Jennifer is a code generator for Go

Jennifer Jennifer is a code generator for Go. package main import ( "fmt" . "github.com/dave/jennifer/jen" ) func main() { f := NewFile("m

Jan 4, 2023
bebop is a bebop parser written in Go, for generating Go code.

bebop is a bebop parser written in Go, for generating Go code. bebop can read .bop files and output .go files representing them: package main i

Dec 24, 2022
Reload Go code in a running process at function/method level granularity

got reload? Function/method-level stateful hot reloading for Go! Status Very much work in progress.

Nov 9, 2022
Go library that provides fuzzy string matching optimized for filenames and code symbols in the style of Sublime Text, VSCode, IntelliJ IDEA et al.
Go library that provides fuzzy string matching optimized for filenames and code symbols in the style of Sublime Text, VSCode, IntelliJ IDEA et al.

Go library that provides fuzzy string matching optimized for filenames and code symbols in the style of Sublime Text, VSCode, IntelliJ IDEA et al. This library is external dependency-free. It only depends on the Go standard library.

Dec 27, 2022
Example code for Go generics

go-generics-example Example code for Go generics. Usage $ go build -gcflags=-G=3 Requirements Go 1.17 or later Advertise Go 言語にやってくる Generics は我々に何をも

Dec 30, 2022
this is an api that execute your deno code and send you the output

this a simple api that execute your deno code and send you the output, has not limit per request example request: in deno: const rawResponse = await f

Dec 23, 2022
Hotswap provides a solution for reloading your go code without restarting your server, interrupting or blocking any ongoing procedure.
Hotswap provides a solution for reloading your go code without restarting your server, interrupting or blocking any ongoing procedure.

Hotswap provides a solution for reloading your go code without restarting your server, interrupting or blocking any ongoing procedure. Hotswap is built upon the plugin mechanism.

Jan 5, 2023
this is a easy breaker by golang code

tfgo-breaker 1. Intro This is a easy breaker by golang code. U can use it in your project quickly. Support function break, timeout, auto dry-run. 2. D

Sep 17, 2022
Source code of Liteloader Tools

LiteLoader Tools This repository store the source code of some LiteLoader Tools Prebuilt Binary see /bin folder Image2Binary [Golang] convert Image(jp

Aug 30, 2022