xref: /aosp_15_r20/external/go-cmp/cmp/internal/function/func.go (revision 88d15eac089d7f20c739ff1001d56b91872b21a1)
1// Copyright 2017, The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5// Package function provides functionality for identifying function types.
6package function
7
8import (
9	"reflect"
10	"regexp"
11	"runtime"
12	"strings"
13)
14
15type funcType int
16
17const (
18	_ funcType = iota
19
20	tbFunc  // func(T) bool
21	ttbFunc // func(T, T) bool
22	trbFunc // func(T, R) bool
23	tibFunc // func(T, I) bool
24	trFunc  // func(T) R
25
26	Equal             = ttbFunc // func(T, T) bool
27	EqualAssignable   = tibFunc // func(T, I) bool; encapsulates func(T, T) bool
28	Transformer       = trFunc  // func(T) R
29	ValueFilter       = ttbFunc // func(T, T) bool
30	Less              = ttbFunc // func(T, T) bool
31	ValuePredicate    = tbFunc  // func(T) bool
32	KeyValuePredicate = trbFunc // func(T, R) bool
33)
34
35var boolType = reflect.TypeOf(true)
36
37// IsType reports whether the reflect.Type is of the specified function type.
38func IsType(t reflect.Type, ft funcType) bool {
39	if t == nil || t.Kind() != reflect.Func || t.IsVariadic() {
40		return false
41	}
42	ni, no := t.NumIn(), t.NumOut()
43	switch ft {
44	case tbFunc: // func(T) bool
45		if ni == 1 && no == 1 && t.Out(0) == boolType {
46			return true
47		}
48	case ttbFunc: // func(T, T) bool
49		if ni == 2 && no == 1 && t.In(0) == t.In(1) && t.Out(0) == boolType {
50			return true
51		}
52	case trbFunc: // func(T, R) bool
53		if ni == 2 && no == 1 && t.Out(0) == boolType {
54			return true
55		}
56	case tibFunc: // func(T, I) bool
57		if ni == 2 && no == 1 && t.In(0).AssignableTo(t.In(1)) && t.Out(0) == boolType {
58			return true
59		}
60	case trFunc: // func(T) R
61		if ni == 1 && no == 1 {
62			return true
63		}
64	}
65	return false
66}
67
68var lastIdentRx = regexp.MustCompile(`[_\p{L}][_\p{L}\p{N}]*$`)
69
70// NameOf returns the name of the function value.
71func NameOf(v reflect.Value) string {
72	fnc := runtime.FuncForPC(v.Pointer())
73	if fnc == nil {
74		return "<unknown>"
75	}
76	fullName := fnc.Name() // e.g., "long/path/name/mypkg.(*MyType).(long/path/name/mypkg.myMethod)-fm"
77
78	// Method closures have a "-fm" suffix.
79	fullName = strings.TrimSuffix(fullName, "-fm")
80
81	var name string
82	for len(fullName) > 0 {
83		inParen := strings.HasSuffix(fullName, ")")
84		fullName = strings.TrimSuffix(fullName, ")")
85
86		s := lastIdentRx.FindString(fullName)
87		if s == "" {
88			break
89		}
90		name = s + "." + name
91		fullName = strings.TrimSuffix(fullName, s)
92
93		if i := strings.LastIndexByte(fullName, '('); inParen && i >= 0 {
94			fullName = fullName[:i]
95		}
96		fullName = strings.TrimSuffix(fullName, ".")
97	}
98	return strings.TrimSuffix(name, ".")
99}
100