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
}
func canonicalName ( t reflect . Type , sel string ) ( [ ] string , error ) { var name string sel = strings . TrimPrefix ( sel , " " ) if sel == " " { return nil , fmt . Errorf ( " " ) } 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 ( " " , 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 ( " " ) } sf , ok := t . FieldByName ( name ) if ! ok { return [ ] string { name } , fmt . Errorf ( " " ) } 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".
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 .