Dominik Honnef

What's happening in Go tip (2013-09-13)

This week we’ll concentrate on changes to go vet and gofmt, two important parts of Go’s tooling, as well as some changes to cgo, some of which are especially important to users of OS X.

What’s happening

Improved go vet

Relevant CLs: CL 12936046, CL 12811043, CL 10895043, CL 13102043, CL 10409047

A number of changes make go vet even more useful for checking the correctness of your code:

go vet will now warn about recursive Stringer implementations. A common mistake is writing code like the following:

func (t T) String() string {
  return fmt.Sprintf("%v", t)
}

This will lead to infinite recursion because the %v verb will also use the String() method of T. go vet now correctly warns about this.

Another printf-related improvement is the detection of redundant calls to String() and Error(). Given a type T that implements the Stringer interface and a value t of type T, the following call to String() is redundant:

fmt.Fprintf("%s", t.String())

go vet will now suggest that this method call is redundant, because fmt will already call it for us. The same applies to Error().

One last printf-related improvement is improved checking of types for various verbs.

  1. %b will no longer allow complex numbers, while %e, %f and %g do.

  2. %p will only accept types that actually represent pointers (maps, channels, slices, unsafe.Pointer and functions/methods.)

  3. %s will allow []byte in addition to string.

  4. Matching is recursive, which means that map[K]V and []T will be allowed if the verb matches K, V and T. That is, go vet will for example not complain about

     fmt.Printf("%d", []int{1, 2, 3})
    

The next change has nothing to do with printf, but function comparison. In Go, functions and methods can be compared against nil (with == and !=). This check, however, only makes sense when using variables; named functions and methods will never be nil. More importantly, Go 1.1 added method values, which can lead to code like the following, which will compile but not do what you intended:

if t.SomeMethod != nil {
  // ...
}

We wanted to compare the return value of SomeMethod() with nil, but we forgot the parentheses. This way, the check will always evaluate to true. go vet will now warn about this kind of pointless comparison.

The last go vet change is about shadowing. One particularly nasty class of programmer mistakes in Go is unintentional shadowing of variables, especially in combination with the := operator.

A new, optional, module that will check for probably unintentional shadowing has been added. It is still considered an experimental feature and too noisy, which is why it needs to be enabled explicitly, by using go tool vet -shadow *.go.

An overview of all kinds of shadowing it does and does not warn about can be found in the included test data. Many of these errors are of stylistic nature to avoid programmer confusion and do not actually affect the program, but that isn’t unusual, go vet is often about good style as well as correctness.

Improved gofmt

Relevant Cls: CL 12837044

CL 12837044 improves gofmt’s handling of import statements. For one, it improves sorting of imports to also consider import name and comment, in addition to the import path. The major improvement, however, is the automatic removal of duplicate imports.

Given the following file

package main

import (
  "fmt"
  "fmt"
)

func main() { fmt.Println("test") }

gofmt will remove the duplicate fmt import and only leave one of them. This is especially interesting when using editors that allow adding imports via a menu or keypress (for example go-mode in Emacs with C-c C-a) because now you can blindly add an import when you think you need it, without having to make sure that the import didn’t exist already¹.

gofmt will make sure to always delete the right entries. Imports must have identical or no import names to be checked for duplicates, and only imports with no attached comments will be removed.

Given

import (
  "fmt" // bar
  "fmt" // foo


  a "foo"
  b "foo"
  b "foo"
)

gofmt will only delete the duplicate b "foo" import and leave the rest alone.

¹: The Go compiler refuses to compile a program with duplicate imports.

Changes to cgo

Relevant CLs: CL 12350044, CL 13413047

An obvious trend in OS X is the preference of clang over gcc, to the point that Xcode 5 comes with a gcc that symlinks to clang. Because of that, cgo will use clang instead of gcc on OS X 10.8 and later.

The move to clang also requires Go to provide its own wrapper for C.malloc because malloc on OS X 10.9 is defined as taking a ulong instead of a size_t – a change that doesn’t matter to C in this case, but does matter to Go because of a stricter type system. A side-effect of this wrapper is that it implements a feature asked for in Issue 3403: Crashing if C.malloc (and by extension C.CString) cannot allocate memory instead of returning nil.

And in case it wasn’t clear enough: If you’re using OS X 10.9 / Xcode 5, you do need Go tip or cgo will not work.