I like Golang for being a very opinionated language, variables declared and not used will result in code not compiling, same with unused imports etc. To give a quick example, the code below will not compile:
package main
import "fmt"
func main() {
fmt.Println("Does not compile")
var willNotCompile int
}
An attempt to run it gives:
$> go run will_not_compile.go
# command-line-arguments
./will_not_compile.go:4:6: willNotCompile declared and not used
which is great as golang does not want us declaring variables we will not use,
however if we move the willNotCompile
variable into the file
block:
package main
import "fmt"
var willNotCompile int
func main() {
fmt.Println("Compiles!?")
}
and run it:
$> go run will_not_compile_file_scope.go
Compiles!?
it compiles, this is surprising!? The variable here is also declared but not used (I have searched online and can not find an explanation for this behavior, if you know why please drop me a comment). In my quest to reinforce opinions similar to that of golang, I found a cool tool staticcheck which will catch unused variables of this nature and does even much more.
$> staticcheck will_not_compile_file_scope.go
will_not_compile_file_scope.go:5:5: var willNotCompile is unused (U1000)
This catches the unused variable in the file scope.
staticcheck catches a host of other things, I will cover a few of other nice things staticcheck catches, the documentation is more thorough on this:
- Unused Constants
- Unused Functions
- Unused Embedded Functions
- Unused Struct Fields
- Unnecessary Nil Checks
Unused Constants
package main
const unusedConstant int = 30
func main() {}
$> staticcheck unused_constants.go
unused_constants.go:3:7: const unusedConstant is unused (U1000)
staticcheck is able to detect that the constant
unusedConstant
is truly not used.
Unused Functions
package main
func unused() {}
func main() {}
$> staticcheck unused_function.go
unused_function.go:5:6: func unused is unused (U1000)
The function unused
is truly not used and staticcheck correctly reports this.
Unused Embedded Functions
package main
import "fmt"
type Foo struct {}
func (*Foo) ancestory() {
fmt.Println("Ancestor is foo")
}
func (*Foo) t() {
fmt.Println("Type is Foo")
}
type Bar struct {
Foo
}
func (*Bar) t() {
fmt.Println("Type is Bar")
}
func main() {
bar := &Bar{}
bar.ancestory()
bar.t()
}
$> staticcheck unused_embedded_functions.go
unused_embedded_functions.go:11:13: func (*Foo).t is unused (U1000)
staticcheck correctly detects that the function (*Foo).t
is unused.
Unused Struct Fields:
package main
import "fmt"
func main() {
foo := struct{
a int
b int
}{a: 1}
fmt.Printf("%v\n", foo)
}
$> staticcheck unused_struct_fields.go
unused_struct_fields.go:8:3: field b is unused (U1000)
Unnecessary Nil Checks
package main
func main() {
var foos []int
if foos != nil {
for i := range foos {
_ = i + 2
}
}
var bars map[string]int
if _, ok := bars["bar"]; ok {
delete(bars, "bar")
}
}
$> staticcheck unnecessary_nil_checks.go
unnecessary_nil_checks.go:6:2: unnecessary nil check around range (S1031)
unnecessary_nil_checks.go:14:2: unnecessary guard around call to delete (S1033)
staticcheck is able to way more than this, do read the documentation for its capabilities.
VIM Tip
If you are a vim user, staticcheck output is in the
quickfix format so you
can, use :cexpr system('staticcheck ./...')
in normal mode to make vim
populate your quickfix list with staticcheck warnings then navigate to the
warnings with your standard c{n,p}{f}
Real world example.
I ran it against kubernetes, it did find a bunch of things and I opened a couple of PRs, but this PR: https://github.com/kubernetes/kubernetes/pull/77325/files highlights its capabilities.