aboutsummaryrefslogtreecommitdiff
path: root/cmp/internal
diff options
context:
space:
mode:
authorJoe Tsai <joetsai@digital-static.net>2019-02-16 18:41:22 -0800
committerGitHub <noreply@github.com>2019-02-16 18:41:22 -0800
commitba10d0b4aceae4ab62c99f38b61d600756d7533b (patch)
treee4339a135f453d6b99a332447e2eb84cc33b401d /cmp/internal
parent47b0945204f5ee05a89cc15d92eec97315340c50 (diff)
downloadgo-cmp-ba10d0b4aceae4ab62c99f38b61d600756d7533b.tar.gz
Move function name logic to function package (#106)
Adjust the logic of NameOf for more recent versions of Go.
Diffstat (limited to 'cmp/internal')
-rw-r--r--cmp/internal/function/func.go42
-rw-r--r--cmp/internal/function/func_test.go51
2 files changed, 91 insertions, 2 deletions
diff --git a/cmp/internal/function/func.go b/cmp/internal/function/func.go
index 4c35ff1..1b4c4c5 100644
--- a/cmp/internal/function/func.go
+++ b/cmp/internal/function/func.go
@@ -2,10 +2,15 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE.md file.
-// Package function identifies function types.
+// Package function provides functionality for identifying function types.
package function
-import "reflect"
+import (
+ "reflect"
+ "regexp"
+ "runtime"
+ "strings"
+)
type funcType int
@@ -47,3 +52,36 @@ func IsType(t reflect.Type, ft funcType) bool {
}
return false
}
+
+var lastIdentRx = regexp.MustCompile(`[_\p{L}][_\p{L}\p{N}]*$`)
+
+// NameOf returns the name of the function value.
+func NameOf(v reflect.Value) string {
+ fnc := runtime.FuncForPC(v.Pointer())
+ if fnc == nil {
+ return "<unknown>"
+ }
+ fullName := fnc.Name() // e.g., "long/path/name/mypkg.(*MyType).(long/path/name/mypkg.myMethod)-fm"
+
+ // Method closures have a "-fm" suffix.
+ fullName = strings.TrimSuffix(fullName, "-fm")
+
+ var name string
+ for len(fullName) > 0 {
+ inParen := strings.HasSuffix(fullName, ")")
+ fullName = strings.TrimSuffix(fullName, ")")
+
+ s := lastIdentRx.FindString(fullName)
+ if s == "" {
+ break
+ }
+ name = s + "." + name
+ fullName = strings.TrimSuffix(fullName, s)
+
+ if i := strings.LastIndexByte(fullName, '('); inParen && i >= 0 {
+ fullName = fullName[:i]
+ }
+ fullName = strings.TrimSuffix(fullName, ".")
+ }
+ return strings.TrimSuffix(name, ".")
+}
diff --git a/cmp/internal/function/func_test.go b/cmp/internal/function/func_test.go
new file mode 100644
index 0000000..61eeccd
--- /dev/null
+++ b/cmp/internal/function/func_test.go
@@ -0,0 +1,51 @@
+// Copyright 2019, 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.md file.
+
+package function
+
+import (
+ "bytes"
+ "reflect"
+ "testing"
+)
+
+type myType struct{ bytes.Buffer }
+
+func (myType) valueMethod() {}
+func (myType) ValueMethod() {}
+
+func (*myType) pointerMethod() {}
+func (*myType) PointerMethod() {}
+
+func TestNameOf(t *testing.T) {
+ tests := []struct {
+ fnc interface{}
+ want string
+ }{
+ {TestNameOf, "function.TestNameOf"},
+ {func() {}, "function.TestNameOf.func1"},
+ {(myType).valueMethod, "function.myType.valueMethod"},
+ {(myType).ValueMethod, "function.myType.ValueMethod"},
+ {(myType{}).valueMethod, "function.myType.valueMethod"},
+ {(myType{}).ValueMethod, "function.myType.ValueMethod"},
+ {(*myType).valueMethod, "function.myType.valueMethod"},
+ {(*myType).ValueMethod, "function.myType.ValueMethod"},
+ {(&myType{}).valueMethod, "function.myType.valueMethod"},
+ {(&myType{}).ValueMethod, "function.myType.ValueMethod"},
+ {(*myType).pointerMethod, "function.myType.pointerMethod"},
+ {(*myType).PointerMethod, "function.myType.PointerMethod"},
+ {(&myType{}).pointerMethod, "function.myType.pointerMethod"},
+ {(&myType{}).PointerMethod, "function.myType.PointerMethod"},
+ {(*myType).Write, "function.myType.Write"},
+ {(&myType{}).Write, "bytes.Buffer.Write"},
+ }
+ for _, tt := range tests {
+ t.Run("", func(t *testing.T) {
+ got := NameOf(reflect.ValueOf(tt.fnc))
+ if got != tt.want {
+ t.Errorf("NameOf() = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}