cmp/cmpopts/struct_filter.go
func canonicalName(t reflect.Type, sel string) ([]string, error) {
	var name string
	sel = strings.TrimPrefix(sel, ".")
	if sel == "" {
		return nil, fmt.Errorf("name must not be empty")
	}
	if i := strings.IndexByte(sel, '.'); i < 0 {
		name, sel = sel, ""
	} else {
		name, sel = sel[:i], sel[i:]
	}

	// Type must be a struct or pointer to struct.
	if t.Kind() == reflect.Ptr {
		t = t.Elem()
	}
	if t.Kind() != reflect.Struct {
		return nil, fmt.Errorf("%v must be a struct", t)
	}

	// Find the canonical name for this current field name.
	// If the field exists in an embedded struct, then it will be expanded.
	if !isExported(name) {
		// Disallow unexported fields:
		//	* To discourage people from actually touching unexported fields
		//	* FieldByName is buggy (https://golang.org/issue/4876)
		return []string{name}, fmt.Errorf("name must be exported")
	}
	sf, ok := t.FieldByName(name)
	if !ok {
		return []string{name}, fmt.Errorf("does not exist")
	}
	var ss []string
	for i := range sf.Index {
		ss = append(ss, t.FieldByIndex(sf.Index[:i+1]).Name)
	}
	if sel == "" {
		return ss, nil
	}
	ssPost, err := canonicalName(sf.Type, sel)
	return append(ss, ssPost...), err
}
func canonicalName(t reflect.Type, sel string) ([]string, error) {
	var name string
	sel = strings.TrimPrefix(sel, ".")
	if sel == "" {
		return nil, fmt.Errorf("name must not be empty")
	}
	if i := strings.IndexByte(sel, '.'); i < 0 {
		name, sel = sel, ""
	} else {
		name, sel = sel[:i], sel[i:]
	}

	// Type must be a struct or pointer to struct.
	if t.Kind() == reflect.Ptr {
		t = t.Elem()
	}
	if t.Kind() != reflect.Struct {
		return nil, fmt.Errorf("%v must be a struct", t)
	}

	// Find the canonical name for this current field name.
	// If the field exists in an embedded struct, then it will be expanded.
	if !isExported(name) {
		// Disallow unexported fields:
		//	* To discourage people from actually touching unexported fields
		//	* FieldByName is buggy (https://golang.org/issue/4876)
		return []string{name}, fmt.Errorf("name must be exported")
	}
	sf, ok := t.FieldByName(name)
	if !ok {
		return []string{name}, fmt.Errorf("does not exist")
	}
	var ss []string
	for i := range sf.Index {
		ss = append(ss, t.FieldByIndex(sf.Index[:i+1]).Name)
	}
	if sel == "" {
		return ss, nil
	}
	ssPost, err := canonicalName(sf.Type, sel)
	return append(ss, ssPost...), err
}
// canonicalName returns a list of identifiers where any struct field access
// through an embedded field is expanded to include the names of the embedded
// types themselves.
//
// For example, suppose field "Foo" is not directly in the parent struct,
// but actually from an embedded struct of type "Bar". Then, the canonical name
// of "Foo" is actually "Bar.Foo".
//
// Suppose field "Foo" is not directly in the parent struct, but actually
// a field in two different embedded structs of types "Bar" and "Baz".
// Then the selector "Foo" causes a panic since it is ambiguous which one it
// refers to. The user must specify either "Bar.Foo" or "Baz.Foo".