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
 | 
			
		||||
// 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
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -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