// Copyright 2018 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 typeutil_test import ( "go/ast" "go/importer" "go/parser" "go/token" "go/types" "strings" "testing" "honnef.co/go/tools/go/types/typeutil" ) func TestStaticCallee(t *testing.T) { const src = `package p import "fmt" type T int func g(int) var f = g var x int type s struct{ f func(int) } func (s) g(int) type I interface{ f(int) } var a struct{b struct{c s}} func calls() { g(x) // a declared func s{}.g(x) // a concrete method a.b.c.g(x) // same fmt.Println(x) // declared func, qualified identifier } func noncalls() { _ = T(x) // a type f(x) // a var panic(x) // a built-in s{}.f(x) // a field I(nil).f(x) // interface method } ` // parse fset := token.NewFileSet() f, err := parser.ParseFile(fset, "p.go", src, 0) if err != nil { t.Fatal(err) } // type-check info := &types.Info{ Uses: make(map[*ast.Ident]types.Object), Selections: make(map[*ast.SelectorExpr]*types.Selection), } cfg := &types.Config{Importer: importer.For("source", nil)} if _, err := cfg.Check("p", fset, []*ast.File{f}, info); err != nil { t.Fatal(err) } for _, decl := range f.Decls { if decl, ok := decl.(*ast.FuncDecl); ok && strings.HasSuffix(decl.Name.Name, "calls") { wantCallee := decl.Name.Name == "calls" // false within func noncalls() ast.Inspect(decl.Body, func(n ast.Node) bool { if call, ok := n.(*ast.CallExpr); ok { fn := typeutil.StaticCallee(info, call) if fn == nil && wantCallee { t.Errorf("%s: StaticCallee returned nil", fset.Position(call.Lparen)) } else if fn != nil && !wantCallee { t.Errorf("%s: StaticCallee returned %s, want nil", fset.Position(call.Lparen), fn) } } return true }) } } }