mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
reflect: add Method.IsExported and StructField.IsExported methods
The IsExported method is a more intuitive helper for checking whether the method or field is exported than checking whether PkgPath is empty. In the same CL, modify the standard library to make use of this helper. Fixes #41563 Change-Id: Iaacfb3b74449501f98e2707aa32095a32bd3c3c1 Reviewed-on: https://go-review.googlesource.com/c/go/+/266197 Trust: Joe Tsai <joetsai@digital-static.net> Run-TryBot: Joe Tsai <thebrokentoaster@gmail.com> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Ian Lance Taylor <iant@golang.org>
This commit is contained in:
parent
7fcf9893f7
commit
b83d073e9e
8 changed files with 68 additions and 21 deletions
|
|
@ -914,7 +914,7 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam
|
||||||
structType := fieldType
|
structType := fieldType
|
||||||
|
|
||||||
for i := 0; i < structType.NumField(); i++ {
|
for i := 0; i < structType.NumField(); i++ {
|
||||||
if structType.Field(i).PkgPath != "" {
|
if !structType.Field(i).IsExported() {
|
||||||
err = StructuralError{"struct contains unexported fields"}
|
err = StructuralError{"struct contains unexported fields"}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -488,7 +488,7 @@ func makeBody(value reflect.Value, params fieldParameters) (e encoder, err error
|
||||||
t := v.Type()
|
t := v.Type()
|
||||||
|
|
||||||
for i := 0; i < t.NumField(); i++ {
|
for i := 0; i < t.NumField(); i++ {
|
||||||
if t.Field(i).PkgPath != "" {
|
if !t.Field(i).IsExported() {
|
||||||
return nil, StructuralError{"struct contains unexported fields"}
|
return nil, StructuralError{"struct contains unexported fields"}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1239,19 +1239,18 @@ func typeFields(t reflect.Type) structFields {
|
||||||
// Scan f.typ for fields to include.
|
// Scan f.typ for fields to include.
|
||||||
for i := 0; i < f.typ.NumField(); i++ {
|
for i := 0; i < f.typ.NumField(); i++ {
|
||||||
sf := f.typ.Field(i)
|
sf := f.typ.Field(i)
|
||||||
isUnexported := sf.PkgPath != ""
|
|
||||||
if sf.Anonymous {
|
if sf.Anonymous {
|
||||||
t := sf.Type
|
t := sf.Type
|
||||||
if t.Kind() == reflect.Ptr {
|
if t.Kind() == reflect.Ptr {
|
||||||
t = t.Elem()
|
t = t.Elem()
|
||||||
}
|
}
|
||||||
if isUnexported && t.Kind() != reflect.Struct {
|
if !sf.IsExported() && t.Kind() != reflect.Struct {
|
||||||
// Ignore embedded fields of unexported non-struct types.
|
// Ignore embedded fields of unexported non-struct types.
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// Do not ignore embedded fields of unexported struct types
|
// Do not ignore embedded fields of unexported struct types
|
||||||
// since they may have exported fields.
|
// since they may have exported fields.
|
||||||
} else if isUnexported {
|
} else if !sf.IsExported() {
|
||||||
// Ignore unexported non-embedded fields.
|
// Ignore unexported non-embedded fields.
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@ func getTypeInfo(typ reflect.Type) (*typeInfo, error) {
|
||||||
n := typ.NumField()
|
n := typ.NumField()
|
||||||
for i := 0; i < n; i++ {
|
for i := 0; i < n; i++ {
|
||||||
f := typ.Field(i)
|
f := typ.Field(i)
|
||||||
if (f.PkgPath != "" && !f.Anonymous) || f.Tag.Get("xml") == "-" {
|
if (!f.IsExported() && !f.Anonymous) || f.Tag.Get("xml") == "-" {
|
||||||
continue // Private field
|
continue // Private field
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -283,7 +283,7 @@ func suitableMethods(typ reflect.Type, reportErr bool) map[string]*methodType {
|
||||||
mtype := method.Type
|
mtype := method.Type
|
||||||
mname := method.Name
|
mname := method.Name
|
||||||
// Method must be exported.
|
// Method must be exported.
|
||||||
if method.PkgPath != "" {
|
if !method.IsExported() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// Method needs three ins: receiver, *args, *reply.
|
// Method needs three ins: receiver, *args, *reply.
|
||||||
|
|
|
||||||
|
|
@ -2900,6 +2900,7 @@ func TestFieldPkgPath(t *testing.T) {
|
||||||
index []int
|
index []int
|
||||||
pkgPath string
|
pkgPath string
|
||||||
embedded bool
|
embedded bool
|
||||||
|
exported bool
|
||||||
}
|
}
|
||||||
|
|
||||||
checkPkgPath := func(name string, s []pkgpathTest) {
|
checkPkgPath := func(name string, s []pkgpathTest) {
|
||||||
|
|
@ -2911,27 +2912,63 @@ func TestFieldPkgPath(t *testing.T) {
|
||||||
if got, want := f.Anonymous, test.embedded; got != want {
|
if got, want := f.Anonymous, test.embedded; got != want {
|
||||||
t.Errorf("%s: Field(%d).Anonymous = %v, want %v", name, test.index, got, want)
|
t.Errorf("%s: Field(%d).Anonymous = %v, want %v", name, test.index, got, want)
|
||||||
}
|
}
|
||||||
|
if got, want := f.IsExported(), test.exported; got != want {
|
||||||
|
t.Errorf("%s: Field(%d).IsExported = %v, want %v", name, test.index, got, want)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
checkPkgPath("testStruct", []pkgpathTest{
|
checkPkgPath("testStruct", []pkgpathTest{
|
||||||
{[]int{0}, "", false}, // Exported
|
{[]int{0}, "", false, true}, // Exported
|
||||||
{[]int{1}, "reflect_test", false}, // unexported
|
{[]int{1}, "reflect_test", false, false}, // unexported
|
||||||
{[]int{2}, "", true}, // OtherPkgFields
|
{[]int{2}, "", true, true}, // OtherPkgFields
|
||||||
{[]int{2, 0}, "", false}, // OtherExported
|
{[]int{2, 0}, "", false, true}, // OtherExported
|
||||||
{[]int{2, 1}, "reflect", false}, // otherUnexported
|
{[]int{2, 1}, "reflect", false, false}, // otherUnexported
|
||||||
{[]int{3}, "reflect_test", true}, // int
|
{[]int{3}, "reflect_test", true, false}, // int
|
||||||
{[]int{4}, "reflect_test", true}, // *x
|
{[]int{4}, "reflect_test", true, false}, // *x
|
||||||
})
|
})
|
||||||
|
|
||||||
type localOtherPkgFields OtherPkgFields
|
type localOtherPkgFields OtherPkgFields
|
||||||
typ = TypeOf(localOtherPkgFields{})
|
typ = TypeOf(localOtherPkgFields{})
|
||||||
checkPkgPath("localOtherPkgFields", []pkgpathTest{
|
checkPkgPath("localOtherPkgFields", []pkgpathTest{
|
||||||
{[]int{0}, "", false}, // OtherExported
|
{[]int{0}, "", false, true}, // OtherExported
|
||||||
{[]int{1}, "reflect", false}, // otherUnexported
|
{[]int{1}, "reflect", false, false}, // otherUnexported
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestMethodPkgPath(t *testing.T) {
|
||||||
|
type I interface {
|
||||||
|
x()
|
||||||
|
X()
|
||||||
|
}
|
||||||
|
typ := TypeOf((*interface {
|
||||||
|
I
|
||||||
|
y()
|
||||||
|
Y()
|
||||||
|
})(nil)).Elem()
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
pkgPath string
|
||||||
|
exported bool
|
||||||
|
}{
|
||||||
|
{"X", "", true},
|
||||||
|
{"Y", "", true},
|
||||||
|
{"x", "reflect_test", false},
|
||||||
|
{"y", "reflect_test", false},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
m, _ := typ.MethodByName(test.name)
|
||||||
|
if got, want := m.PkgPath, test.pkgPath; got != want {
|
||||||
|
t.Errorf("MethodByName(%q).PkgPath = %q, want %q", test.name, got, want)
|
||||||
|
}
|
||||||
|
if got, want := m.IsExported(), test.exported; got != want {
|
||||||
|
t.Errorf("MethodByName(%q).IsExported = %v, want %v", test.name, got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestVariadicType(t *testing.T) {
|
func TestVariadicType(t *testing.T) {
|
||||||
// Test example from Type documentation.
|
// Test example from Type documentation.
|
||||||
var f func(x int, y ...float64)
|
var f func(x int, y ...float64)
|
||||||
|
|
|
||||||
|
|
@ -568,12 +568,13 @@ func newName(n, tag string, exported bool) name {
|
||||||
// Method represents a single method.
|
// Method represents a single method.
|
||||||
type Method struct {
|
type Method struct {
|
||||||
// Name is the method name.
|
// Name is the method name.
|
||||||
|
Name string
|
||||||
|
|
||||||
// PkgPath is the package path that qualifies a lower case (unexported)
|
// PkgPath is the package path that qualifies a lower case (unexported)
|
||||||
// method name. It is empty for upper case (exported) method names.
|
// method name. It is empty for upper case (exported) method names.
|
||||||
// The combination of PkgPath and Name uniquely identifies a method
|
// The combination of PkgPath and Name uniquely identifies a method
|
||||||
// in a method set.
|
// in a method set.
|
||||||
// See https://golang.org/ref/spec#Uniqueness_of_identifiers
|
// See https://golang.org/ref/spec#Uniqueness_of_identifiers
|
||||||
Name string
|
|
||||||
PkgPath string
|
PkgPath string
|
||||||
|
|
||||||
Type Type // method type
|
Type Type // method type
|
||||||
|
|
@ -581,6 +582,11 @@ type Method struct {
|
||||||
Index int // index for Type.Method
|
Index int // index for Type.Method
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsExported reports whether the method is exported.
|
||||||
|
func (m Method) IsExported() bool {
|
||||||
|
return m.PkgPath == ""
|
||||||
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
kindDirectIface = 1 << 5
|
kindDirectIface = 1 << 5
|
||||||
kindGCProg = 1 << 6 // Type.gc points to GC program
|
kindGCProg = 1 << 6 // Type.gc points to GC program
|
||||||
|
|
@ -1090,6 +1096,7 @@ func (t *interfaceType) MethodByName(name string) (m Method, ok bool) {
|
||||||
type StructField struct {
|
type StructField struct {
|
||||||
// Name is the field name.
|
// Name is the field name.
|
||||||
Name string
|
Name string
|
||||||
|
|
||||||
// PkgPath is the package path that qualifies a lower case (unexported)
|
// PkgPath is the package path that qualifies a lower case (unexported)
|
||||||
// field name. It is empty for upper case (exported) field names.
|
// field name. It is empty for upper case (exported) field names.
|
||||||
// See https://golang.org/ref/spec#Uniqueness_of_identifiers
|
// See https://golang.org/ref/spec#Uniqueness_of_identifiers
|
||||||
|
|
@ -1102,6 +1109,11 @@ type StructField struct {
|
||||||
Anonymous bool // is an embedded field
|
Anonymous bool // is an embedded field
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsExported reports whether the field is exported.
|
||||||
|
func (f StructField) IsExported() bool {
|
||||||
|
return f.PkgPath == ""
|
||||||
|
}
|
||||||
|
|
||||||
// A StructTag is the tag string in a struct field.
|
// A StructTag is the tag string in a struct field.
|
||||||
//
|
//
|
||||||
// By convention, tag strings are a concatenation of
|
// By convention, tag strings are a concatenation of
|
||||||
|
|
@ -2771,8 +2783,7 @@ func runtimeStructField(field StructField) (structField, string) {
|
||||||
panic("reflect.StructOf: field \"" + field.Name + "\" is anonymous but has PkgPath set")
|
panic("reflect.StructOf: field \"" + field.Name + "\" is anonymous but has PkgPath set")
|
||||||
}
|
}
|
||||||
|
|
||||||
exported := field.PkgPath == ""
|
if field.IsExported() {
|
||||||
if exported {
|
|
||||||
// Best-effort check for misuse.
|
// Best-effort check for misuse.
|
||||||
// Since this field will be treated as exported, not much harm done if Unicode lowercase slips through.
|
// Since this field will be treated as exported, not much harm done if Unicode lowercase slips through.
|
||||||
c := field.Name[0]
|
c := field.Name[0]
|
||||||
|
|
@ -2788,7 +2799,7 @@ func runtimeStructField(field StructField) (structField, string) {
|
||||||
|
|
||||||
resolveReflectType(field.Type.common()) // install in runtime
|
resolveReflectType(field.Type.common()) // install in runtime
|
||||||
f := structField{
|
f := structField{
|
||||||
name: newName(field.Name, string(field.Tag), exported),
|
name: newName(field.Name, string(field.Tag), field.IsExported()),
|
||||||
typ: field.Type.common(),
|
typ: field.Type.common(),
|
||||||
offsetEmbed: offsetEmbed,
|
offsetEmbed: offsetEmbed,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -615,7 +615,7 @@ func (s *state) evalField(dot reflect.Value, fieldName string, node parse.Node,
|
||||||
tField, ok := receiver.Type().FieldByName(fieldName)
|
tField, ok := receiver.Type().FieldByName(fieldName)
|
||||||
if ok {
|
if ok {
|
||||||
field := receiver.FieldByIndex(tField.Index)
|
field := receiver.FieldByIndex(tField.Index)
|
||||||
if tField.PkgPath != "" { // field is unexported
|
if !tField.IsExported() {
|
||||||
s.errorf("%s is an unexported field of struct type %s", fieldName, typ)
|
s.errorf("%s is an unexported field of struct type %s", fieldName, typ)
|
||||||
}
|
}
|
||||||
// If it's a function, we must call it.
|
// If it's a function, we must call it.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue