You could automate (some) boilerplate Go error handling with a formatter

June 5, 2025

Recently I read [ On ¦ No ] syntactic support for error handling, where the Go team have decided that they're not going to do anything with error handling (contrary to my fears). One part of it sparked a thought about making it less annoying to write basic, boilerplate error handling, and I briefly said something on the Fediverse:

A thought on Go error handling: you could reduce the annoyance of writing the boiler plate by creating a code formatter (along the lines of goimports) that added the standard 'if (err != nil) {...}' stuff to any unhandled 'err' return in your code. Then you'd write code with just 'a, err := Thing(); b, err := Thing2(a)' and so on, and the code formatter would fill in things for you.

This thought was inspired by the model of goimports; my impression is that almost everyone uses goimports to automatically update import statements as part of (auto-)formatting their code (certainly I do). If goimports can update imports as part of 'formatting', then in theory we can extend it to add boilerplate 'check and return' error handling. This would take a set of code like this:

a, err := Thing()
b, err := Thing2(a)
return Thing3(b)

This code would be turned into:

a, err := Thing()
if (err != nil) {
  return <zero value>, err
}
b, err := Thing2(a)
if (err != nil) {
  return <zero value>, err
}
return Thing3(b)

(Here by '<zero value>' I mean some suitable zero value, like 'nil', not the literal text '<zero value>'.)

This would only happen if the error return was assigned to a variable and then the variable was unused (before potentially being reassigned). If you wrote 'a, _ := Thing()', there would be no error check added, and if you re-ran the formatter on the post-formatter code it wouldn't do anything because all of the error variables are used. Determining if the error variable was used would need some control flow analysis, and determining what to return and the syntax of creating appropriate zero values for non-error return values would take some function signature and type analysis.

(As an extra trick, if you had an 'if (err != nil) {return ...}' block already, the formatter could copy the 'return ...' into the blocks it added.)

A Go code formatter isn't the only place you could implement this feature. These days many people would be able to use it if it was a 'code action' in a LSP server such as gopls, which already supports other code actions. As a LSP server code action, you could easily apply it selectively only to a function (or a section of a function), rather than having to trust the formatter to run over your entire file. You could also have a LSP server code action that added the boilerplate error check immediately after a call, so you'd write the function call line and then pick 'add boilerplate error check' at the end.

I don't know if this is a good idea, either as a standalone formatter or as a LSP code feature. I'm not a big user of gopls code actions (I mostly ignore them) and I'm not sure I'd be happy writing code that looked like the starting point. But perhaps some people would be, especially if it was an 'expand this function call to have boilerplate error checks' in gopls.

(I don't write enough Go code to have strong feelings about the boilerplate error handling. At my scale, hand-writing the standard 'if (err != nil)' stuff is fine, and I not infrequently want to do something with the error.)

Written on 05 June 2025.
« Python type checkers work in different ways and can check different things
Adding your own attributes to Python functions and Python typing »

Page tools: View Source.
Search:
Login: Password:

Last modified: Thu Jun 5 22:43:43 2025
This dinky wiki is brought to you by the Insane Hackers Guild, Python sub-branch.