You could automate (some) boilerplate Go error handling with a formatter
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.)
Comments on this page:
|
|