As a software engineer, you always try to improve the quality of your programs. We are looking for the best software development practices and TDD techniques.

"Have no fear of perfection - you'll never reach it."
― Salvador Dalí

In this article we will explore different code inspection tools in Go ecosystem. We will increase our code quality and engineering skills by running tools that will do analysis on our code base and report the suspicious parts of it.

Govet

Vet does analysis on Go source code and reports suspicious constructs. It uses heuristics that do not guarantee all reports are genuine problems. Vet can find errors not caught by the compilers.

It can be invoked in three different ways:

// for go package
$ go tool vet package/path/name
// for files
$ go tool vet source/directory/*.go
// for directory
$ go tool vet source/directory

What should be analysed can be controlled with these flags (extraced from help doc):

  • -all check everything; disabled if any explicit check is requested (default true)
  • -asmdecl check assembly against Go declarations (default unset)
  • -assign check for useless assignments (default unset)
  • -atomic check for common mistaken usages of the sync/atomic package (default unset)
  • -bool check for mistakes involving boolean operators (default unset)
  • -buildtags check that +build tags are valid (default unset)
  • -composites check that composite literals used field-keyed elements (default unset)
  • -compositewhitelist use composite white list; for testing only (default true)
  • -copylocks check that locks are not passed by value (default unset)
  • -methods check that canonically named methods are canonically defined (default unset)
  • -nilfunc check for comparisons between functions and nil (default unset)
  • -printf check printf-like invocations (default unset)
  • -printfuncs string comma-separated list of print function names to check
  • -rangeloops check that range loop variables are used correctly (default unset)
  • -shadow check for shadowed variables (experimental; must be set explicitly) (default unset)
  • -shadowstrict whether to be strict about shadowing; can be noisy
  • -shift check for useless shifts (default unset)
  • -structtags check that struct field tags have canonical format and apply to exported fields as needed (default unset)
  • -tags string comma-separated list of build tags to apply when parsing
  • -test for testing only: sets -all and -shadow
  • -unreachable check for unreachable code (default unset)
  • -unsafeptr check for misuse of unsafe.Pointer (default unset)
  • -unusedfuncs string comma-separated list of functions whose results must be used (default “errors.New,fmt.Errorf,fmt.Sprintf,fmt.Sprint,sort.Reverse”)
  • -unusedresult check for unused result of calls to functions in -unusedfuncs list and methods in -unusedstringmethods list (default unset)
  • -unusedstringmethods string comma-separated list of names of methods of type func() string whose results must be used (default “Error,String”)

Lets use this code snippet from @francesc:

// extracted from tweet: https://twitter.com/francesc/status/491699441506586627
// filename: sample.go
package main

import "fmt"

func main() {
	a := 0
	if a != 1 || a != 2 {
		 a++
	}

	fmt.Printf("a = %s\n", a)
}

Lets see the tool in action:

$ go tool vet sample.go

Vet reports two suspicious constructions. First it reports that the if-condition is suspicious. It’s always true since cannot be both. The second warning reports that %s is used with integer type instead of string.

sample.go:10: suspect or: a != 1 || a != 2
sample.go:14: arg a for printf verb %s of wrong type: int

Golint

Golint differs from gofmt and govet. It prints out style mistakes. Golint is concerned with coding style. It is in use at Google, and it seeks to match the accepted style of the open source Go project.

Golint make suggestions regarding source code. It is not perfect, and has both false positives and false negatives. Do not consider its output as a truth. It will never be trustworthy enough to be enforced automatically as part of a build process.

Installation

go get -u github.com/golang/lint/golint

Usage

// analysis a particular package
$ golint package
// analysis a particular directory
$ golint directory
// analyses a particualr files
$ golint files

Lets lint the following code snippet:

// filename: hr.go
package hr

import "errors"

const MaxAge int = 70

type Person struct {
	Name string
	Age  int
}

func NewPerson(name string) (*Person, error) {
	if name == "" {
		return nil, errors.New("Name is required")
	} else {
		return &Person{Name: name}, nil
	}
}

The command tool give us the following sugestion to improve our source code:

hr.go:5:6: exported type Person should have comment or be unexported
hr.go:9:1: exported function NewPerson should have comment or be unexported
hr.go:12:9: if block ends with a return statement, so drop this else and outdent its block

Neat. Isn’t it?

Errcheck

The errcheck command tools is a program that checks whether a source code has unhandled errors.

Installation

$ go get github.com/kisielk/errcheck

Usage

The following flags can control the tool behavior (extracted from help doc):

  • -abspath print absolute paths to files
  • -asserts if true, check for ignored type assertion results
  • -blank if true, check for errors assigned to blank identifier. By default is false.
  • -ignore value comma-separated list of pairs in pkg:regex format.
  • -ignorepkg string comma-separated list of package paths to ignore
  • -tags value space-separated list of build tags to include (default “”)
  • -verbose produce more verbose logging

Lets have the following snippet:

// filename: logger.go
package logger

import "os"

func Log(path, data string) {
	file, _ := os.Open(path)
	file.Write([]byte(data))
	file.Close()
}

Lets do error handling analysis with errcheck tool:

// Note that analyses of _ errors is skipped by default.
// We enable that by providing -blank flag.
$ errcheck -blank app.go

The following lines are reported as problematic:

web/logger.go:6:8        file, _ := os.Open(path)
web/logger.go:7:12       file.Write([]byte(data))
web/logger.go:8:12       file.Close()

SafeSQL

SafeSQL is a static analysis command line tool that protects against SQL injections.

Installation

$ go get github.com/stripe/safesql

Usage

If SafeSQL passes, your application is safe from SQL injections, however there are many safe programs which SafeSQL will declare potentially unsafe. There are false positives due to the fact that SafeSQL does not recursively trace down query arguments through every function. Second there are many SQL statement to represent compile time constants required for the static analysis algorithm.

Lets see the tool in action with this code snippet:

package database

import (
	"database/sql"
	"log"
)

func Status(username string) string {
	db, err := sql.Open("mysql", "user:password@tcp(127.0.0.1:3306)/hello")
	if err != nil {
		log.Fatal(err)
	}

	defer db.Close()
	sql := "SELECT * FROM user WHERE username='" + username + "'"
	row := db.QueryRow(sql)

	var isLogged bool
	row.Scan(&isLogged)
	if isLogged {
		return "online"
	}

	return "offline"
}

It reports the following SQL injection:

Found 1 potentially unsafe SQL statements:
- /$GOPATH/db/database.go:16:20

It gives the following recommendation:

Please ensure that all SQL queries you use are compile-time constants.
You should always use parameterized queries or prepared statements
instead of building queries from strings.

Defercheck

Defercheck is command tool that checks for repeating defer statements.

Installation

$ go get github.com/opennota/check/cmd/defercheck

Usage

$ defercheck go/parser

It reports the following suspicious defer statement in parser package:

/usr/.../go/parser/parser.go:1929: Repeating defer p.closeScope() inside function parseSwitchStmt

Structcheck

Structcheck is command tool that checks for unused field in structs.

Installation

$ go get github.com/opennota/check/cmd/structcheck

Usage

Lets check the hr codesnippet that we used previously.

$ structcheck -e hr

Available command line flags are:

  • -a Count assignments only
  • -e Report exported fields
  • -t Load test files too

It reports that the Age field of Person struct is unused:

hr: /$GOPATH/src/hr/hr.go:7:2: hr/hr.Person.Age

Varcheck

Varcheck command is doing the same analysis as Structcheck but on global variables and constants.

Installation

$ go get github.com/opennota/check/cmd/varcheck

Usage

Lets inspect the hr package again:

// -e Report exported variables and constants
$ varcheck -e hr

It finds that the Age constant is not used:

hr: /$GOPATH/src/hr/hr.go:5:7: MaxAge

Conclusion

Static single-assignment package provides a very powerful framework for code analysis. It gives the opportunity to build different tools that may increase the code quality and durability of every Go program. I am looking forward to see more and more command tools that will bring our source code to the next level.

The previous articles regarding tools in Go: