Golang style linters, multiline statements, and blocks with whitespace
The golang ci lint linters whitespace, lll, and wsl can put you in a cycle of warnings about your code quality. By adding a comment to the right place you can appease all three.
Whew, this is a long title. No idea whether it's going to show up on the search engines. Anyhow...
So as I’m working through Advent of Code 2021 (yes, still), I keep getting super annoying linter messages. I use a fairly comprehensive golangci-lint ruleset, but the three that are relevant here are these:
linters:
disable-all: true
enable:
- lll
- whitespace
- wrapcheck
There are others, plus the configurations, but that’s not important right now.
So these three have a very weird interaction where you can get into a cycle of “wait, how do I even solve this?” Take this piece of Go code for example:
func something(in someStruct) {
if in.xFrom > cubeUpperLimit || in.yFrom > cubeUpperLimit || in.zFrom > cubeUpperLimit || in.xTo < cubeLowerLimit || in.yTo < cubeLowerLimit || in.zTo < cubeLowerLimit {
return instruction{}, errors.New("out of bounds")
}
// the rest of the function
}
That’s a pretty long if condition. The lll
linter will complain that the line is too long:
line is 170 characters (lll)
if in.xFrom > cubeUpperLimit || in.yFrom > cubeUpperLimit || in.zFrom > cubeUpperLimit || in.xTo < cubeLowerLimit || in.yTo < cubeLowerLimit || in.zTo < cubeLowerLimit {
Okay, let’s break it up:
func something(in someStruct) {
if in.xFrom > cubeUpperLimit ||
in.yFrom > cubeUpperLimit ||
in.zFrom > cubeUpperLimit ||
in.xTo < cubeLowerLimit ||
in.yTo < cubeLowerLimit ||
in.zTo < cubeLowerLimit {
return instruction{}, errors.New("out of bounds")
}
// rest of the function
}
Looks better. Except now I'm getting this error:
multi-line statement should be followed by a newline (whitespace)
in.zTo < cubeLowerLimit {
No problem, I can just add a newline after the opening {
func something(in someStruct) {
if in.xFrom > cubeUpperLimit ||
in.yFrom > cubeUpperLimit ||
in.zFrom > cubeUpperLimit ||
in.xTo < cubeLowerLimit ||
in.yTo < cubeLowerLimit ||
in.zTo < cubeLowerLimit {
return instruction{}, errors.New("out of bounds")
}
// rest of the function
}
Hah, no dice, I’m now getting this:
block should not start with a whitespace (wsl)
in.zTo < cubeLowerLimit {
^
And therein lies the issue... seems like these three linters can’t be satisfied at the same time. Scouring the internet I found two vaguely helpful links: https://github.com/ultraware/whitespace/issues/1 and https://github.com/golang/lint/issues/459. Both are above whitespace after multiline if statements, but neither tackle the contradiction between these two linting rules together.
Solution
The only way I could make everything happy is if I add a code comment to the line immediately after the if
block, like so:
func something(in someStruct) {
if in.xFrom > cubeUpperLimit ||
in.yFrom > cubeUpperLimit ||
in.zFrom > cubeUpperLimit ||
in.xTo < cubeLowerLimit ||
in.yTo < cubeLowerLimit ||
in.zTo < cubeLowerLimit {
// If any part of the cube is outside our bounds, then return an error.
return instruction{}, errors.New("out of bounds")
}
// rest of the function
}
This way the lines aren’t too long, there’s a newline after the multi-line statement, and the block does not start with whitespace. Win–win–win!
Photo by JESHOOTS.COM on Unsplash