Golang code inspection tools

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):

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):

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:

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:

Tags# ,