package main import ( "fmt" "go/ast" "go/importer" "go/parser" "go/token" "go/types" "log" ) // !+input const input = `package main import "bytes" func main() { var buf bytes.Buffer if buf.Bytes == nil && bytes.Repeat != nil && main == nil { // ... } } ` //!-input var fset = token.NewFileSet() func main() { f, err := parser.ParseFile(fset, "input.go", input, 0) if err != nil { log.Fatal(err) // parse error } conf := types.Config{Importer: importer.Default()} info := &types.Info{ Defs: make(map[*ast.Ident]types.Object), Uses: make(map[*ast.Ident]types.Object), Types: make(map[ast.Expr]types.TypeAndValue), } if _, err = conf.Check("cmd/hello", fset, []*ast.File{f}, info); err != nil { log.Fatal(err) // type error } ast.Inspect(f, func(n ast.Node) bool { if n != nil { CheckNilFuncComparison(info, n) } return true }) } // !+ // CheckNilFuncComparison reports unintended comparisons // of functions against nil, e.g., "if x.Method == nil {". func CheckNilFuncComparison(info *types.Info, n ast.Node) { e, ok := n.(*ast.BinaryExpr) if !ok { return // not a binary operation } if e.Op != token.EQL && e.Op != token.NEQ { return // not a comparison } // If this is a comparison against nil, find the other operand. var other ast.Expr if info.Types[e.X].IsNil() { other = e.Y } else if info.Types[e.Y].IsNil() { other = e.X } else { return // not a comparison against nil } // Find the object. var obj types.Object switch v := other.(type) { case *ast.Ident: obj = info.Uses[v] case *ast.SelectorExpr: obj = info.Uses[v.Sel] default: return // not an identifier or selection } if _, ok := obj.(*types.Func); !ok { return // not a function or method } fmt.Printf("%s: comparison of function %v %v nil is always %v\n", fset.Position(e.Pos()), obj.Name(), e.Op, e.Op == token.NEQ) } //!- /* //!+output $ go build golang.org/x/example/gotypes/nilfunc $ ./nilfunc input.go:7:5: comparison of function Bytes == nil is always false input.go:7:25: comparison of function Repeat != nil is always true input.go:7:48: comparison of function main == nil is always false //!-output */