Hello,
I wanted to elaborate a bit of that topic. Error checking in Go is a problem for us with Sonar as it raises the complexity very fast even though most of those statements are basically stopping the function right there with a return and do not add much in the way of complexity. Most of them follow this pattern:
func ValidateUUID(s string) error {
err := uuid.Validate(s)
if err != nil {
return err
}
return nil
}
This is such a common Go trope that IDE like Jetbrain GoLand fold them by default:
If many statement involve error checking, the SonarLint complexity score will raise very fast even if the function is completely flat with no other form of branching. And it explodes exponentially as soon as error checking is nested in other branches like iterations.
Ironically, Sonar can then lead to a raise in complexity as the typical response will either be to raise the complexity threshold for the whole project to scores like 25 or more (and thus raising complexity when statements other than error checking are involved), or multiplicating functions with no other goal than to please the linter (and thus raising complexity by call depth and context switch).
To illustrate a bit further, even if it is a bit artificial, this function already scores at 18 in complexity, which raises an issue with the default threshold of 15:
func ValidateUUID(sl []string) error {
if len(sl) > 0 {
for _, s := range sl {
if s != "" {
err := uuid.Validate(s)
if err != nil {
return err
}
_, err = uuid.Parse(s)
if err != nil {
return err
}
_, err = uuid.NewDCEGroup()
if err != nil {
return err
}
}
}
}
return nil
}
Just ignoring the err != nil
pattern altogether is maybe a tad too extreme, but what about only ignoring branches with just a return
inside? Or giving those a lower score than 1, like 0.2? Or adding a flat score for all such patterns in a whole function or full-fledged branch?
In case you’re unfamiliar with Go, I’m not sure a straight comparison with Java try/catch mechanism really works. It can if you do something else than returning, but returning an error in Go is an explicit way of reproducing the built-in Java/C#/Python/etc exception behaviour which cancels the function and propagates the error in the call stack.