diff --git a/go/analysis/passes/nilfunc/nilfunc.go b/go/analysis/passes/nilfunc/nilfunc.go new file mode 100644 index 00000000..7a2344ff --- /dev/null +++ b/go/analysis/passes/nilfunc/nilfunc.go @@ -0,0 +1,70 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package nilfunc + +import ( + "go/ast" + "go/token" + "go/types" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" + "golang.org/x/tools/go/ast/inspector" +) + +var Analyzer = &analysis.Analyzer{ + Name: "nilfunc", + Doc: `check for useless comparisons between functions and nil + +A useless comparison is one like f == nil as opposed to f() == nil.`, + Requires: []*analysis.Analyzer{inspect.Analyzer}, + Run: run, +} + +func run(pass *analysis.Pass) (interface{}, error) { + inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) + + nodeFilter := []ast.Node{ + (*ast.BinaryExpr)(nil), + } + inspect.Preorder(nodeFilter, func(n ast.Node) { + e := n.(*ast.BinaryExpr) + + // Only want == or != comparisons. + if e.Op != token.EQL && e.Op != token.NEQ { + return + } + + // Only want comparisons with a nil identifier on one side. + var e2 ast.Expr + switch { + case pass.TypesInfo.Types[e.X].IsNil(): + e2 = e.Y + case pass.TypesInfo.Types[e.Y].IsNil(): + e2 = e.X + default: + return + } + + // Only want identifiers or selector expressions. + var obj types.Object + switch v := e2.(type) { + case *ast.Ident: + obj = pass.TypesInfo.Uses[v] + case *ast.SelectorExpr: + obj = pass.TypesInfo.Uses[v.Sel] + default: + return + } + + // Only want functions. + if _, ok := obj.(*types.Func); !ok { + return + } + + pass.Reportf(e.Pos(), "comparison of function %v %v nil is always %v", obj.Name(), e.Op, e.Op == token.NEQ) + }) + return nil, nil +} diff --git a/go/analysis/passes/nilfunc/nilfunc_test.go b/go/analysis/passes/nilfunc/nilfunc_test.go new file mode 100644 index 00000000..8218e713 --- /dev/null +++ b/go/analysis/passes/nilfunc/nilfunc_test.go @@ -0,0 +1,13 @@ +package nilfunc_test + +import ( + "testing" + + "golang.org/x/tools/go/analysis/analysistest" + "golang.org/x/tools/go/analysis/passes/nilfunc" +) + +func Test(t *testing.T) { + testdata := analysistest.TestData() + analysistest.Run(t, testdata, nilfunc.Analyzer, "a") +} diff --git a/go/analysis/passes/vet/testdata/nilfunc.go b/go/analysis/passes/nilfunc/testdata/src/a/a.go similarity index 59% rename from go/analysis/passes/vet/testdata/nilfunc.go rename to go/analysis/passes/nilfunc/testdata/src/a/a.go index 2ce7bc8c..496009cb 100644 --- a/go/analysis/passes/vet/testdata/nilfunc.go +++ b/go/analysis/passes/nilfunc/testdata/src/a/a.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package testdata +package a func F() {} @@ -20,14 +20,14 @@ func Comparison() { if fn == nil || Fv == nil || t.F == nil { // no error; these func vars or fields may be nil } - if F == nil { // ERROR "comparison of function F == nil is always false" + if F == nil { // want "comparison of function F == nil is always false" panic("can't happen") } - if t.M == nil { // ERROR "comparison of function M == nil is always false" + if t.M == nil { // want "comparison of function M == nil is always false" panic("can't happen") } - if F != nil { // ERROR "comparison of function F != nil is always true" - if t.M != nil { // ERROR "comparison of function M != nil is always true" + if F != nil { // want "comparison of function F != nil is always true" + if t.M != nil { // want "comparison of function M != nil is always true" return } } diff --git a/go/analysis/passes/vet/nilfunc.go b/go/analysis/passes/vet/nilfunc.go deleted file mode 100644 index 4d5cfe39..00000000 --- a/go/analysis/passes/vet/nilfunc.go +++ /dev/null @@ -1,69 +0,0 @@ -// +build ignore - -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -/* -This file contains the code to check for useless function comparisons. -A useless comparison is one like f == nil as opposed to f() == nil. -*/ - -package main - -import ( - "go/ast" - "go/token" - "go/types" -) - -func init() { - register("nilfunc", - "check for comparisons between functions and nil", - checkNilFuncComparison, - binaryExpr) -} - -func checkNilFuncComparison(f *File, node ast.Node) { - e := node.(*ast.BinaryExpr) - - // Only want == or != comparisons. - if e.Op != token.EQL && e.Op != token.NEQ { - return - } - - // Only want comparisons with a nil identifier on one side. - var e2 ast.Expr - switch { - case f.isNil(e.X): - e2 = e.Y - case f.isNil(e.Y): - e2 = e.X - default: - return - } - - // Only want identifiers or selector expressions. - var obj types.Object - switch v := e2.(type) { - case *ast.Ident: - obj = f.pkg.uses[v] - case *ast.SelectorExpr: - obj = f.pkg.uses[v.Sel] - default: - return - } - - // Only want functions. - if _, ok := obj.(*types.Func); !ok { - return - } - - f.Badf(e.Pos(), "comparison of function %v %v nil is always %v", obj.Name(), e.Op, e.Op == token.NEQ) -} - -// isNil reports whether the provided expression is the built-in nil -// identifier. -func (f *File) isNil(e ast.Expr) bool { - return f.pkg.types[e].Type == types.Typ[types.UntypedNil] -}