gotypes,build: add Implement (#58)

By using Implement you can provide a definition of a function, taking the signature from a stub in the package. One major benefit of this approach is it makes it easy to handle external types in the function signature.

Updates #55
This commit is contained in:
Michael McLoughlin
2019-01-22 22:35:01 -08:00
committed by GitHub
parent 9c913ee847
commit eb225e9d2c
12 changed files with 191 additions and 2 deletions

View File

@@ -3,6 +3,7 @@ package gotypes
import (
"bytes"
"errors"
"fmt"
"go/token"
"go/types"
"strconv"
@@ -31,6 +32,20 @@ func NewSignatureVoid() *Signature {
return NewSignature(nil, types.NewSignature(nil, nil, nil, false))
}
// LookupSignature returns the signature of the named function in the provided package.
func LookupSignature(pkg *types.Package, name string) (*Signature, error) {
scope := pkg.Scope()
obj := scope.Lookup(name)
if obj == nil {
return nil, fmt.Errorf("could not find function \"%s\"", name)
}
s, ok := obj.Type().(*types.Signature)
if !ok {
return nil, fmt.Errorf("object \"%s\" does not have signature type", name)
}
return NewSignature(pkg, s), nil
}
// ParseSignature builds a Signature by parsing a Go function type expression.
// The function type must reference builtin types only; see
// ParseSignatureInPackage if custom types are required.
@@ -67,7 +82,12 @@ func (s *Signature) Bytes() int { return s.Params().Bytes() + s.Results().Bytes(
// String writes Signature as a string. This does not include the "func" keyword.
func (s *Signature) String() string {
var buf bytes.Buffer
types.WriteSignature(&buf, s.sig, types.RelativeTo(s.pkg))
types.WriteSignature(&buf, s.sig, func(pkg *types.Package) string {
if pkg == s.pkg {
return ""
}
return pkg.Name()
})
return buf.String()
}

View File

@@ -5,8 +5,64 @@ import (
"go/types"
"strings"
"testing"
"golang.org/x/tools/go/packages"
)
func TestLookupSignature(t *testing.T) {
pkg := LoadPackageTypes(t, "math")
s, err := LookupSignature(pkg, "Frexp")
if err != nil {
t.Fatal(err)
}
expect, err := ParseSignature("func(f float64) (frac float64, exp int)")
if err != nil {
t.Fatal(err)
}
if s.String() != expect.String() {
t.Errorf("\n got: %s\nexpect: %s\n", s, expect)
}
}
func TestLookupSignatureErrors(t *testing.T) {
cases := []struct {
PackagePath string
FunctionName string
ExpectedError string
}{
{"runtime", "HmmIdk", "could not find function \"HmmIdk\""},
{"crypto", "Decrypter", "object \"Decrypter\" does not have signature type"},
{"encoding/base64", "StdEncoding", "object \"StdEncoding\" does not have signature type"},
}
for _, c := range cases {
pkg := LoadPackageTypes(t, c.PackagePath)
_, err := LookupSignature(pkg, c.FunctionName)
if err == nil {
t.Fatalf("expected error looking up '%s' in package '%s'", c.FunctionName, c.PackagePath)
}
if err.Error() != c.ExpectedError {
t.Fatalf("wrong error message\n got: %q\nexpect: %q", err.Error(), c.ExpectedError)
}
}
}
func LoadPackageTypes(t *testing.T, path string) *types.Package {
t.Helper()
cfg := &packages.Config{
Mode: packages.LoadTypes,
}
pkgs, err := packages.Load(cfg, path)
if err != nil {
t.Fatal(err)
}
if len(pkgs) != 1 {
t.Fatal("expected to load exactly one package")
}
return pkgs[0].Types
}
func TestParseSignature(t *testing.T) {
cases := []struct {
Expr string