A Go linter that reports all named returns in function and method signatures.
I hate named returns in golang because they are error prone (see Why are named returns error prone? below). That's why I wrote this linter. That's all.
Tutorial on how to write your own linter: https://disaev.me/p/writing-useful-go-analysis-linter/
go install github.com/firefart/nonamedreturns@latestPrebuilt binaries for Linux, macOS and Windows are also attached to every release.
nonamedreturns is bundled with golangci-lint. You do not need to install it separately; just enable it in your configuration (see Settings).
The binary is a standard go/analysis checker, so it accepts the usual package patterns:
# check the current module
nonamedreturns ./...
# check a single package
nonamedreturns ./internal/fooAny named return value produces a diagnostic such as:
./foo.go:10:1: named return "err" with type "error" found
To see all available flags run:
nonamedreturns -helpEnable the linter in your .golangci.yml:
linters:
enable:
- nonamedreturnsThen run it as part of your normal lint step:
golangci-lint run ./...The linter has a single setting.
| Type | bool |
| Default | false |
| Standalone flag | -report-error-in-defer |
| golangci-lint key | report-error-in-defer |
A common, legitimate use of a named return is a named error that is inspected or modified inside a defer before the function returns. By default the linter does not report a named return when all of the following are true:
- its type is exactly the built-in
error, - it is referenced (read or assigned) inside a
deferclosure in the same function, and - it is assigned somewhere in the function body (inside the
deferor anywhere else).
Set report-error-in-defer to true if you want these named errors reported as well.
func doRequest(ctx context.Context) (err error) {
span := startSpan(ctx)
defer func() {
// err is read here to record the outcome
if err != nil {
span.RecordError(err)
}
span.End()
}()
err = callSomething()
return
}This is not reported with the default settings (the named error is used in the defer and assigned in the body), but is reported when report-error-in-defer: true.
nonamedreturns -report-error-in-defer=true ./...linters:
enable:
- nonamedreturns
settings:
nonamedreturns:
# report named error if it is assigned inside defer
report-error-in-defer: true-
Shadowing of Variables If you have a named return variable that is also used as a local variable within the function, it can lead to shadowing issues. This occurs when the named return variable is unintentionally shadowed by a local variable with the same name, leading to unexpected behavior.
-
Accidental Changes Developers might inadvertently modify the value of a named return variable within the function, thinking it only affects the local variable and not the actual return value.
-
Readability Issues While named returns can improve readability for method signatures, they can also make the code harder to understand if misused. It may be unclear whether a variable is a local variable or a return variable, especially in larger functions.
-
Unused Variables Named returns often result in variables being declared in the function signature that are not used within the function body. This can lead to confusion and may make the code less maintainable.
-
Unintentional scope increase Named return variables have scope for the whole function, as opposed to local variable which have scope only after they are defined. So, even if you are setting the value at the last few lines of the function, its scope still spans the whole function. This allows the variable to be referenced, before it's assigned the first time.
func test(input s) (ret bool) { .... // ret is accessed before it's first assigned, // this code will not error out as the variable // s already defined. if ret { // do some stuff } .... ret = someRandomFunc(input) // ret is first assigned here } }