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