honnef.co/go/tools@v0.5.0-0.dev.0.20240520180541-dcae280a5e87/analysis/code/code.go (about)

     1  // Package code answers structural and type questions about Go code.
     2  package code
     3  
     4  import (
     5  	"flag"
     6  	"fmt"
     7  	"go/ast"
     8  	"go/build/constraint"
     9  	"go/constant"
    10  	"go/token"
    11  	"go/types"
    12  	"path/filepath"
    13  	"strconv"
    14  	"strings"
    15  
    16  	"honnef.co/go/tools/analysis/facts/generated"
    17  	"honnef.co/go/tools/analysis/facts/purity"
    18  	"honnef.co/go/tools/analysis/facts/tokenfile"
    19  	"honnef.co/go/tools/analysis/lint"
    20  	"honnef.co/go/tools/go/ast/astutil"
    21  	"honnef.co/go/tools/go/types/typeutil"
    22  	"honnef.co/go/tools/knowledge"
    23  	"honnef.co/go/tools/pattern"
    24  
    25  	"golang.org/x/tools/go/analysis"
    26  )
    27  
    28  type Positioner interface {
    29  	Pos() token.Pos
    30  }
    31  
    32  func IsOfStringConvertibleByteSlice(pass *analysis.Pass, expr ast.Expr) bool {
    33  	typ, ok := pass.TypesInfo.TypeOf(expr).Underlying().(*types.Slice)
    34  	if !ok {
    35  		return false
    36  	}
    37  	elem := types.Unalias(typ.Elem())
    38  	if LanguageVersion(pass, expr) >= 18 {
    39  		// Before Go 1.18, one could not directly convert from []T (where 'type T byte')
    40  		// to string. See also https://github.com/golang/go/issues/23536.
    41  		elem = elem.Underlying()
    42  	}
    43  	return types.Identical(elem, types.Typ[types.Byte])
    44  }
    45  
    46  func IsOfPointerToTypeWithName(pass *analysis.Pass, expr ast.Expr, name string) bool {
    47  	ptr, ok := types.Unalias(pass.TypesInfo.TypeOf(expr)).(*types.Pointer)
    48  	if !ok {
    49  		return false
    50  	}
    51  	return typeutil.IsTypeWithName(ptr.Elem(), name)
    52  }
    53  
    54  func IsOfTypeWithName(pass *analysis.Pass, expr ast.Expr, name string) bool {
    55  	return typeutil.IsTypeWithName(pass.TypesInfo.TypeOf(expr), name)
    56  }
    57  
    58  func IsInTest(pass *analysis.Pass, node Positioner) bool {
    59  	// FIXME(dh): this doesn't work for global variables with
    60  	// initializers
    61  	f := pass.Fset.File(node.Pos())
    62  	return f != nil && strings.HasSuffix(f.Name(), "_test.go")
    63  }
    64  
    65  // IsMain reports whether the package being processed is a package
    66  // main.
    67  func IsMain(pass *analysis.Pass) bool {
    68  	return pass.Pkg.Name() == "main"
    69  }
    70  
    71  // IsMainLike reports whether the package being processed is a
    72  // main-like package. A main-like package is a package that is
    73  // package main, or that is intended to be used by a tool framework
    74  // such as cobra to implement a command.
    75  //
    76  // Note that this function errs on the side of false positives; it may
    77  // return true for packages that aren't main-like. IsMainLike is
    78  // intended for analyses that wish to suppress diagnostics for
    79  // main-like packages to avoid false positives.
    80  func IsMainLike(pass *analysis.Pass) bool {
    81  	if pass.Pkg.Name() == "main" {
    82  		return true
    83  	}
    84  	for _, imp := range pass.Pkg.Imports() {
    85  		if imp.Path() == "github.com/spf13/cobra" {
    86  			return true
    87  		}
    88  	}
    89  	return false
    90  }
    91  
    92  func SelectorName(pass *analysis.Pass, expr *ast.SelectorExpr) string {
    93  	info := pass.TypesInfo
    94  	sel := info.Selections[expr]
    95  	if sel == nil {
    96  		if x, ok := expr.X.(*ast.Ident); ok {
    97  			pkg, ok := info.ObjectOf(x).(*types.PkgName)
    98  			if !ok {
    99  				// This shouldn't happen
   100  				return fmt.Sprintf("%s.%s", x.Name, expr.Sel.Name)
   101  			}
   102  			return fmt.Sprintf("%s.%s", pkg.Imported().Path(), expr.Sel.Name)
   103  		}
   104  		panic(fmt.Sprintf("unsupported selector: %v", expr))
   105  	}
   106  	if v, ok := sel.Obj().(*types.Var); ok && v.IsField() {
   107  		return fmt.Sprintf("(%s).%s", typeutil.DereferenceR(sel.Recv()), sel.Obj().Name())
   108  	} else {
   109  		return fmt.Sprintf("(%s).%s", sel.Recv(), sel.Obj().Name())
   110  	}
   111  }
   112  
   113  func IsNil(pass *analysis.Pass, expr ast.Expr) bool {
   114  	return pass.TypesInfo.Types[expr].IsNil()
   115  }
   116  
   117  func BoolConst(pass *analysis.Pass, expr ast.Expr) bool {
   118  	val := pass.TypesInfo.ObjectOf(expr.(*ast.Ident)).(*types.Const).Val()
   119  	return constant.BoolVal(val)
   120  }
   121  
   122  func IsBoolConst(pass *analysis.Pass, expr ast.Expr) bool {
   123  	// We explicitly don't support typed bools because more often than
   124  	// not, custom bool types are used as binary enums and the explicit
   125  	// comparison is desired. We err on the side of false negatives and
   126  	// treat aliases like other custom types.
   127  
   128  	ident, ok := expr.(*ast.Ident)
   129  	if !ok {
   130  		return false
   131  	}
   132  	obj := pass.TypesInfo.ObjectOf(ident)
   133  	c, ok := obj.(*types.Const)
   134  	if !ok {
   135  		return false
   136  	}
   137  	basic, ok := c.Type().(*types.Basic)
   138  	if !ok {
   139  		return false
   140  	}
   141  	if basic.Kind() != types.UntypedBool && basic.Kind() != types.Bool {
   142  		return false
   143  	}
   144  	return true
   145  }
   146  
   147  func ExprToInt(pass *analysis.Pass, expr ast.Expr) (int64, bool) {
   148  	tv := pass.TypesInfo.Types[expr]
   149  	if tv.Value == nil {
   150  		return 0, false
   151  	}
   152  	if tv.Value.Kind() != constant.Int {
   153  		return 0, false
   154  	}
   155  	return constant.Int64Val(tv.Value)
   156  }
   157  
   158  func ExprToString(pass *analysis.Pass, expr ast.Expr) (string, bool) {
   159  	val := pass.TypesInfo.Types[expr].Value
   160  	if val == nil {
   161  		return "", false
   162  	}
   163  	if val.Kind() != constant.String {
   164  		return "", false
   165  	}
   166  	return constant.StringVal(val), true
   167  }
   168  
   169  func CallName(pass *analysis.Pass, call *ast.CallExpr) string {
   170  	// See the comment in typeutil.FuncName for why this doesn't require special handling
   171  	// of aliases.
   172  
   173  	fun := astutil.Unparen(call.Fun)
   174  
   175  	// Instantiating a function cannot return another generic function, so doing this once is enough
   176  	switch idx := fun.(type) {
   177  	case *ast.IndexExpr:
   178  		fun = idx.X
   179  	case *ast.IndexListExpr:
   180  		fun = idx.X
   181  	}
   182  
   183  	// (foo)[T] is not a valid instantiationg, so no need to unparen again.
   184  
   185  	switch fun := fun.(type) {
   186  	case *ast.SelectorExpr:
   187  		fn, ok := pass.TypesInfo.ObjectOf(fun.Sel).(*types.Func)
   188  		if !ok {
   189  			return ""
   190  		}
   191  		return typeutil.FuncName(fn)
   192  	case *ast.Ident:
   193  		obj := pass.TypesInfo.ObjectOf(fun)
   194  		switch obj := obj.(type) {
   195  		case *types.Func:
   196  			return typeutil.FuncName(obj)
   197  		case *types.Builtin:
   198  			return obj.Name()
   199  		default:
   200  			return ""
   201  		}
   202  	default:
   203  		return ""
   204  	}
   205  }
   206  
   207  func IsCallTo(pass *analysis.Pass, node ast.Node, name string) bool {
   208  	// See the comment in typeutil.FuncName for why this doesn't require special handling
   209  	// of aliases.
   210  
   211  	call, ok := node.(*ast.CallExpr)
   212  	if !ok {
   213  		return false
   214  	}
   215  	return CallName(pass, call) == name
   216  }
   217  
   218  func IsCallToAny(pass *analysis.Pass, node ast.Node, names ...string) bool {
   219  	// See the comment in typeutil.FuncName for why this doesn't require special handling
   220  	// of aliases.
   221  
   222  	call, ok := node.(*ast.CallExpr)
   223  	if !ok {
   224  		return false
   225  	}
   226  	q := CallName(pass, call)
   227  	for _, name := range names {
   228  		if q == name {
   229  			return true
   230  		}
   231  	}
   232  	return false
   233  }
   234  
   235  func File(pass *analysis.Pass, node Positioner) *ast.File {
   236  	m := pass.ResultOf[tokenfile.Analyzer].(map[*token.File]*ast.File)
   237  	return m[pass.Fset.File(node.Pos())]
   238  }
   239  
   240  // BuildConstraints returns the build constraints for file f. It considers both //go:build lines as well as
   241  // GOOS and GOARCH in file names.
   242  func BuildConstraints(pass *analysis.Pass, f *ast.File) (constraint.Expr, bool) {
   243  	var expr constraint.Expr
   244  	for _, cmt := range f.Comments {
   245  		if len(cmt.List) == 0 {
   246  			continue
   247  		}
   248  		for _, el := range cmt.List {
   249  			if el.Pos() > f.Package {
   250  				break
   251  			}
   252  			if line := el.Text; strings.HasPrefix(line, "//go:build") {
   253  				var err error
   254  				expr, err = constraint.Parse(line)
   255  				if err != nil {
   256  					expr = nil
   257  				}
   258  				break
   259  			}
   260  		}
   261  	}
   262  
   263  	name := pass.Fset.PositionFor(f.Pos(), false).Filename
   264  	oexpr := constraintsFromName(name)
   265  	if oexpr != nil {
   266  		if expr == nil {
   267  			expr = oexpr
   268  		} else {
   269  			expr = &constraint.AndExpr{X: expr, Y: oexpr}
   270  		}
   271  	}
   272  
   273  	return expr, expr != nil
   274  }
   275  
   276  func constraintsFromName(name string) constraint.Expr {
   277  	name = filepath.Base(name)
   278  	name = strings.TrimSuffix(name, ".go")
   279  	name = strings.TrimSuffix(name, "_test")
   280  	var goos, goarch string
   281  	switch strings.Count(name, "_") {
   282  	case 0:
   283  		// No GOOS or GOARCH in the file name.
   284  	case 1:
   285  		_, c, _ := strings.Cut(name, "_")
   286  		if _, ok := knowledge.KnownGOOS[c]; ok {
   287  			goos = c
   288  		} else if _, ok := knowledge.KnownGOARCH[c]; ok {
   289  			goarch = c
   290  		}
   291  	default:
   292  		n := strings.LastIndex(name, "_")
   293  		if _, ok := knowledge.KnownGOOS[name[n+1:]]; ok {
   294  			// The file name is *_stuff_GOOS.go
   295  			goos = name[n+1:]
   296  		} else if _, ok := knowledge.KnownGOARCH[name[n+1:]]; ok {
   297  			// The file name is *_GOOS_GOARCH.go or *_stuff_GOARCH.go
   298  			goarch = name[n+1:]
   299  			_, c, _ := strings.Cut(name[:n], "_")
   300  			if _, ok := knowledge.KnownGOOS[c]; ok {
   301  				// The file name is *_GOOS_GOARCH.go
   302  				goos = c
   303  			}
   304  		} else {
   305  			// The file name could also be something like foo_windows_nonsense.go — and because nonsense
   306  			// isn't a known GOARCH, "windows" won't be interpreted as a GOOS, either.
   307  		}
   308  	}
   309  
   310  	var expr constraint.Expr
   311  	if goos != "" {
   312  		expr = &constraint.TagExpr{Tag: goos}
   313  	}
   314  	if goarch != "" {
   315  		if expr == nil {
   316  			expr = &constraint.TagExpr{Tag: goarch}
   317  		} else {
   318  			expr = &constraint.AndExpr{X: expr, Y: &constraint.TagExpr{Tag: goarch}}
   319  		}
   320  	}
   321  	return expr
   322  }
   323  
   324  // IsGenerated reports whether pos is in a generated file. It ignores
   325  // //line directives.
   326  func IsGenerated(pass *analysis.Pass, pos token.Pos) bool {
   327  	_, ok := Generator(pass, pos)
   328  	return ok
   329  }
   330  
   331  // Generator returns the generator that generated the file containing
   332  // pos. It ignores //line directives.
   333  func Generator(pass *analysis.Pass, pos token.Pos) (generated.Generator, bool) {
   334  	file := pass.Fset.PositionFor(pos, false).Filename
   335  	m := pass.ResultOf[generated.Analyzer].(map[string]generated.Generator)
   336  	g, ok := m[file]
   337  	return g, ok
   338  }
   339  
   340  // MayHaveSideEffects reports whether expr may have side effects. If
   341  // the purity argument is nil, this function implements a purely
   342  // syntactic check, meaning that any function call may have side
   343  // effects, regardless of the called function's body. Otherwise,
   344  // purity will be consulted to determine the purity of function calls.
   345  func MayHaveSideEffects(pass *analysis.Pass, expr ast.Expr, purity purity.Result) bool {
   346  	switch expr := expr.(type) {
   347  	case *ast.BadExpr:
   348  		return true
   349  	case *ast.Ellipsis:
   350  		return MayHaveSideEffects(pass, expr.Elt, purity)
   351  	case *ast.FuncLit:
   352  		// the literal itself cannot have side effects, only calling it
   353  		// might, which is handled by CallExpr.
   354  		return false
   355  	case *ast.ArrayType, *ast.StructType, *ast.FuncType, *ast.InterfaceType, *ast.MapType, *ast.ChanType:
   356  		// types cannot have side effects
   357  		return false
   358  	case *ast.BasicLit:
   359  		return false
   360  	case *ast.BinaryExpr:
   361  		return MayHaveSideEffects(pass, expr.X, purity) || MayHaveSideEffects(pass, expr.Y, purity)
   362  	case *ast.CallExpr:
   363  		if purity == nil {
   364  			return true
   365  		}
   366  		switch obj := typeutil.Callee(pass.TypesInfo, expr).(type) {
   367  		case *types.Func:
   368  			if _, ok := purity[obj]; !ok {
   369  				return true
   370  			}
   371  		case *types.Builtin:
   372  			switch obj.Name() {
   373  			case "len", "cap":
   374  			default:
   375  				return true
   376  			}
   377  		default:
   378  			return true
   379  		}
   380  		for _, arg := range expr.Args {
   381  			if MayHaveSideEffects(pass, arg, purity) {
   382  				return true
   383  			}
   384  		}
   385  		return false
   386  	case *ast.CompositeLit:
   387  		if MayHaveSideEffects(pass, expr.Type, purity) {
   388  			return true
   389  		}
   390  		for _, elt := range expr.Elts {
   391  			if MayHaveSideEffects(pass, elt, purity) {
   392  				return true
   393  			}
   394  		}
   395  		return false
   396  	case *ast.Ident:
   397  		return false
   398  	case *ast.IndexExpr:
   399  		return MayHaveSideEffects(pass, expr.X, purity) || MayHaveSideEffects(pass, expr.Index, purity)
   400  	case *ast.IndexListExpr:
   401  		// In theory, none of the checks are necessary, as IndexListExpr only involves types. But there is no harm in
   402  		// being safe.
   403  		if MayHaveSideEffects(pass, expr.X, purity) {
   404  			return true
   405  		}
   406  		for _, idx := range expr.Indices {
   407  			if MayHaveSideEffects(pass, idx, purity) {
   408  				return true
   409  			}
   410  		}
   411  		return false
   412  	case *ast.KeyValueExpr:
   413  		return MayHaveSideEffects(pass, expr.Key, purity) || MayHaveSideEffects(pass, expr.Value, purity)
   414  	case *ast.SelectorExpr:
   415  		return MayHaveSideEffects(pass, expr.X, purity)
   416  	case *ast.SliceExpr:
   417  		return MayHaveSideEffects(pass, expr.X, purity) ||
   418  			MayHaveSideEffects(pass, expr.Low, purity) ||
   419  			MayHaveSideEffects(pass, expr.High, purity) ||
   420  			MayHaveSideEffects(pass, expr.Max, purity)
   421  	case *ast.StarExpr:
   422  		return MayHaveSideEffects(pass, expr.X, purity)
   423  	case *ast.TypeAssertExpr:
   424  		return MayHaveSideEffects(pass, expr.X, purity)
   425  	case *ast.UnaryExpr:
   426  		if MayHaveSideEffects(pass, expr.X, purity) {
   427  			return true
   428  		}
   429  		return expr.Op == token.ARROW || expr.Op == token.AND
   430  	case *ast.ParenExpr:
   431  		return MayHaveSideEffects(pass, expr.X, purity)
   432  	case nil:
   433  		return false
   434  	default:
   435  		panic(fmt.Sprintf("internal error: unhandled type %T", expr))
   436  	}
   437  }
   438  
   439  func LanguageVersion(pass *analysis.Pass, node Positioner) int {
   440  	// As of Go 1.21, two places can specify the minimum Go version:
   441  	// - 'go' directives in go.mod and go.work files
   442  	// - individual files by using '//go:build'
   443  	//
   444  	// Individual files can upgrade to a higher version than the module version. Individual files
   445  	// can also downgrade to a lower version, but only if the module version is at least Go 1.21.
   446  	//
   447  	// The restriction on downgrading doesn't matter to us. All language changes before Go 1.22 will
   448  	// not type-check on versions that are too old, and thus never reach our analyzes. In practice,
   449  	// such ineffective downgrading will always be useless, as the compiler will not restrict the
   450  	// language features used, and doesn't ever rely on minimum versions to restrict the use of the
   451  	// standard library. However, for us, both choices (respecting or ignoring ineffective
   452  	// downgrading) have equal complexity, but only respecting it has a non-zero chance of reducing
   453  	// noisy positives.
   454  	//
   455  	// The minimum Go versions are exposed via go/ast.File.GoVersion and go/types.Package.GoVersion.
   456  	// ast.File's version is populated by the parser, whereas types.Package's version is populated
   457  	// from the Go version specified in the types.Config, which is set by our package loader, based
   458  	// on the module information provided by go/packages, via 'go list -json'.
   459  	//
   460  	// As of Go 1.21, standard library packages do not present themselves as modules, and thus do
   461  	// not have a version set on their types.Package. In this case, we fall back to the version
   462  	// provided by our '-go' flag. In most cases, '-go' defaults to 'module', which falls back to
   463  	// the Go version that Staticcheck was built with when no module information exists. In the
   464  	// future, the standard library will hopefully be a proper module (see
   465  	// https://github.com/golang/go/issues/61174#issuecomment-1622471317). In that case, the version
   466  	// of standard library packages will match that of the used Go version. At that point,
   467  	// Staticcheck will refuse to work with Go versions that are too new, to avoid misinterpreting
   468  	// code due to language changes.
   469  	//
   470  	// We also lack module information when building in GOPATH mode. In this case, the implied
   471  	// language version is at most Go 1.21, as per https://github.com/golang/go/issues/60915. We
   472  	// don't handle this yet, and it will not matter until Go 1.22.
   473  	//
   474  	// It is not clear how per-file downgrading behaves in GOPATH mode. On the one hand, no module
   475  	// version at all is provided, which should preclude per-file downgrading. On the other hand,
   476  	// https://github.com/golang/go/issues/60915 suggests that the language version is at most 1.21
   477  	// in GOPATH mode, which would allow per-file downgrading. Again it doesn't affect us, as all
   478  	// relevant language changes before Go 1.22 will lead to type-checking failures and never reach
   479  	// us.
   480  	//
   481  	// It is not clear if per-file upgrading is possible in GOPATH mode. This needs clarification.
   482  
   483  	f := File(pass, node)
   484  	var n int
   485  	if v := f.GoVersion; v != "" {
   486  		var ok bool
   487  		n, ok = lint.ParseGoVersion(v)
   488  		if !ok {
   489  			panic(fmt.Sprintf("unexpected failure parsing version %q", v))
   490  		}
   491  	} else if v := pass.Pkg.GoVersion(); v != "" {
   492  		var ok bool
   493  		n, ok = lint.ParseGoVersion(v)
   494  		if !ok {
   495  			panic(fmt.Sprintf("unexpected failure parsing version %q", v))
   496  		}
   497  	} else {
   498  		v, ok := pass.Analyzer.Flags.Lookup("go").Value.(flag.Getter)
   499  		if !ok {
   500  			panic("requested Go version, but analyzer has no version flag")
   501  		}
   502  		n = v.Get().(int)
   503  	}
   504  
   505  	return n
   506  }
   507  
   508  func StdlibVersion(pass *analysis.Pass, node Positioner) int {
   509  	var n int
   510  	if v := pass.Pkg.GoVersion(); v != "" {
   511  		var ok bool
   512  		n, ok = lint.ParseGoVersion(v)
   513  		if !ok {
   514  			panic(fmt.Sprintf("unexpected failure parsing version %q", v))
   515  		}
   516  	} else {
   517  		v, ok := pass.Analyzer.Flags.Lookup("go").Value.(flag.Getter)
   518  		if !ok {
   519  			panic("requested Go version, but analyzer has no version flag")
   520  		}
   521  		n = v.Get().(int)
   522  	}
   523  
   524  	f := File(pass, node)
   525  	if f == nil {
   526  		panic(fmt.Sprintf("no file found for node with position %s", pass.Fset.PositionFor(node.Pos(), false)))
   527  	}
   528  
   529  	if v := f.GoVersion; v != "" {
   530  		nf, err := strconv.Atoi(strings.TrimPrefix(v, "go1."))
   531  		if err != nil {
   532  			panic(fmt.Sprintf("unexpected error: %s", err))
   533  		}
   534  
   535  		if n < 21 {
   536  			// Before Go 1.21, the Go version set in go.mod specified the maximum language version
   537  			// available to the module. It wasn't uncommon to set the version to Go 1.20 but only
   538  			// use 1.20 functionality (both language and stdlib) in files tagged for 1.20, and
   539  			// supporting a lower version overall. As such, a file tagged lower than the module
   540  			// version couldn't expect to have access to the standard library of the version set in
   541  			// go.mod.
   542  			//
   543  			// While Go 1.21's behavior has been backported to 1.19.11 and 1.20.6, users'
   544  			// expectations have not.
   545  			n = nf
   546  		} else {
   547  			// Go 1.21 and newer refuse to build modules that depend on versions newer than the Go
   548  			// version. This means that in a 1.22 module with a file tagged as 1.17, the file can
   549  			// expect to have access to 1.22's standard library.
   550  			//
   551  			// Do note that strictly speaking we're conflating the Go version and the module version in
   552  			// our check. Nothing is stopping a user from using Go 1.17 to build a Go 1.22 module, in
   553  			// which case the 1.17 file will not have acces to the 1.22 standard library. However, we
   554  			// believe that if a module requires 1.21 or newer, then the author clearly expects the new
   555  			// behavior, and doesn't care for the old one. Otherwise they would've specified an older
   556  			// version.
   557  			//
   558  			// In other words, the module version also specifies what it itself actually means, with
   559  			// >=1.21 being a minimum version for the toolchain, and <1.21 being a maximum version for
   560  			// the language.
   561  
   562  			if nf > n {
   563  				n = nf
   564  			}
   565  		}
   566  	}
   567  
   568  	return n
   569  }
   570  
   571  var integerLiteralQ = pattern.MustParse(`(IntegerLiteral tv)`)
   572  
   573  func IntegerLiteral(pass *analysis.Pass, node ast.Node) (types.TypeAndValue, bool) {
   574  	m, ok := Match(pass, integerLiteralQ, node)
   575  	if !ok {
   576  		return types.TypeAndValue{}, false
   577  	}
   578  	return m.State["tv"].(types.TypeAndValue), true
   579  }
   580  
   581  func IsIntegerLiteral(pass *analysis.Pass, node ast.Node, value constant.Value) bool {
   582  	tv, ok := IntegerLiteral(pass, node)
   583  	if !ok {
   584  		return false
   585  	}
   586  	return constant.Compare(tv.Value, token.EQL, value)
   587  }
   588  
   589  // IsMethod reports whether expr is a method call of a named method with signature meth.
   590  // If name is empty, it is not checked.
   591  // For now, method expressions (Type.Method(recv, ..)) are not considered method calls.
   592  func IsMethod(pass *analysis.Pass, expr *ast.SelectorExpr, name string, meth *types.Signature) bool {
   593  	if name != "" && expr.Sel.Name != name {
   594  		return false
   595  	}
   596  	sel, ok := pass.TypesInfo.Selections[expr]
   597  	if !ok || sel.Kind() != types.MethodVal {
   598  		return false
   599  	}
   600  	return types.Identical(sel.Type(), meth)
   601  }
   602  
   603  func RefersTo(pass *analysis.Pass, expr ast.Expr, ident types.Object) bool {
   604  	found := false
   605  	fn := func(node ast.Node) bool {
   606  		ident2, ok := node.(*ast.Ident)
   607  		if !ok {
   608  			return true
   609  		}
   610  		if ident == pass.TypesInfo.ObjectOf(ident2) {
   611  			found = true
   612  			return false
   613  		}
   614  		return true
   615  	}
   616  	ast.Inspect(expr, fn)
   617  	return found
   618  }