go/analysis/passes/nilfunc: split out of vet
Change-Id: Ibbe8dfddfcabd2bb71753c969eef14f92603c17d Reviewed-on: https://go-review.googlesource.com/c/140762 Reviewed-by: Michael Matloob <matloob@golang.org> Run-TryBot: Michael Matloob <matloob@golang.org>
This commit is contained in:
parent
06b1b3f6e3
commit
fe0886716e
|
@ -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
|
||||||
|
}
|
|
@ -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")
|
||||||
|
}
|
|
@ -2,7 +2,7 @@
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
package testdata
|
package a
|
||||||
|
|
||||||
func F() {}
|
func F() {}
|
||||||
|
|
||||||
|
@ -20,14 +20,14 @@ func Comparison() {
|
||||||
if fn == nil || Fv == nil || t.F == nil {
|
if fn == nil || Fv == nil || t.F == nil {
|
||||||
// no error; these func vars or fields may be 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")
|
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")
|
panic("can't happen")
|
||||||
}
|
}
|
||||||
if F != nil { // ERROR "comparison of function F != nil is always true"
|
if F != nil { // want "comparison of function F != nil is always true"
|
||||||
if t.M != nil { // ERROR "comparison of function M != nil is always true"
|
if t.M != nil { // want "comparison of function M != nil is always true"
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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]
|
|
||||||
}
|
|
Loading…
Reference in New Issue