]> Cypherpunks.ru repositories - gostls13.git/commitdiff
reflect: add Method.IsExported and StructField.IsExported methods
authorJoe Tsai <joetsai@digital-static.net>
Thu, 15 Oct 2020 01:41:16 +0000 (18:41 -0700)
committerJoe Tsai <thebrokentoaster@gmail.com>
Thu, 25 Feb 2021 21:21:51 +0000 (21:21 +0000)
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>
src/encoding/asn1/asn1.go
src/encoding/asn1/marshal.go
src/encoding/json/encode.go
src/encoding/xml/typeinfo.go
src/net/rpc/server.go
src/reflect/all_test.go
src/reflect/type.go
src/text/template/exec.go

index f9b9cb4930e9890aaf90ab83522d7bf69982509b..cffc06dc9c8689f528203cbdb7251301322aa396 100644 (file)
@@ -914,7 +914,7 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam
                structType := fieldType
 
                for i := 0; i < structType.NumField(); i++ {
-                       if structType.Field(i).PkgPath != "" {
+                       if !structType.Field(i).IsExported() {
                                err = StructuralError{"struct contains unexported fields"}
                                return
                        }
index 0d34d5aa1e815227bc1db6131e8643e7068ac3ab..5b4d786d495576bcf1007e99f11342eb0840bd81 100644 (file)
@@ -488,7 +488,7 @@ func makeBody(value reflect.Value, params fieldParameters) (e encoder, err error
                t := v.Type()
 
                for i := 0; i < t.NumField(); i++ {
-                       if t.Field(i).PkgPath != "" {
+                       if !t.Field(i).IsExported() {
                                return nil, StructuralError{"struct contains unexported fields"}
                        }
                }
index 483b9d8f2d0a3c5d80509a1e03d8835d869c19c4..751f03d33da4b053b438d7c7282e599429838e93 100644 (file)
@@ -1239,19 +1239,18 @@ func typeFields(t reflect.Type) structFields {
                        // Scan f.typ for fields to include.
                        for i := 0; i < f.typ.NumField(); i++ {
                                sf := f.typ.Field(i)
-                               isUnexported := sf.PkgPath != ""
                                if sf.Anonymous {
                                        t := sf.Type
                                        if t.Kind() == reflect.Ptr {
                                                t = t.Elem()
                                        }
-                                       if isUnexported && t.Kind() != reflect.Struct {
+                                       if !sf.IsExported() && t.Kind() != reflect.Struct {
                                                // Ignore embedded fields of unexported non-struct types.
                                                continue
                                        }
                                        // Do not ignore embedded fields of unexported struct types
                                        // since they may have exported fields.
-                               } else if isUnexported {
+                               } else if !sf.IsExported() {
                                        // Ignore unexported non-embedded fields.
                                        continue
                                }
index f30fe58590bd774d6bf5c423506d0c107aae4819..162724ef1a58b02d452a7783a11f523c65c3d6bf 100644 (file)
@@ -60,7 +60,7 @@ func getTypeInfo(typ reflect.Type) (*typeInfo, error) {
                n := typ.NumField()
                for i := 0; i < n; 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
                        }
 
index 9cb928240f1c4de331eee28f3b82953ccaf68b47..074c5b9b0d7a5ecaf84aaef241cb29225a3a2ed4 100644 (file)
@@ -283,7 +283,7 @@ func suitableMethods(typ reflect.Type, reportErr bool) map[string]*methodType {
                mtype := method.Type
                mname := method.Name
                // Method must be exported.
-               if method.PkgPath != "" {
+               if !method.IsExported() {
                        continue
                }
                // Method needs three ins: receiver, *args, *reply.
index 1225d6177d4b4709ff9c4982887adfab9dc2fb33..35cc469d742aff81bd97a8fd29d8f302929b8d71 100644 (file)
@@ -2900,6 +2900,7 @@ func TestFieldPkgPath(t *testing.T) {
                index    []int
                pkgPath  string
                embedded bool
+               exported bool
        }
 
        checkPkgPath := func(name string, s []pkgpathTest) {
@@ -2911,27 +2912,63 @@ func TestFieldPkgPath(t *testing.T) {
                        if got, want := f.Anonymous, test.embedded; 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{
-               {[]int{0}, "", false},             // Exported
-               {[]int{1}, "reflect_test", false}, // unexported
-               {[]int{2}, "", true},              // OtherPkgFields
-               {[]int{2, 0}, "", false},          // OtherExported
-               {[]int{2, 1}, "reflect", false},   // otherUnexported
-               {[]int{3}, "reflect_test", true},  // int
-               {[]int{4}, "reflect_test", true},  // *x
+               {[]int{0}, "", false, true},              // Exported
+               {[]int{1}, "reflect_test", false, false}, // unexported
+               {[]int{2}, "", true, true},               // OtherPkgFields
+               {[]int{2, 0}, "", false, true},           // OtherExported
+               {[]int{2, 1}, "reflect", false, false},   // otherUnexported
+               {[]int{3}, "reflect_test", true, false},  // int
+               {[]int{4}, "reflect_test", true, false},  // *x
        })
 
        type localOtherPkgFields OtherPkgFields
        typ = TypeOf(localOtherPkgFields{})
        checkPkgPath("localOtherPkgFields", []pkgpathTest{
-               {[]int{0}, "", false},        // OtherExported
-               {[]int{1}, "reflect", false}, // otherUnexported
+               {[]int{0}, "", false, true},         // OtherExported
+               {[]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) {
        // Test example from Type documentation.
        var f func(x int, y ...float64)
index eb2030063a38f87d2c7488f2e4c27f3ce0ac6a56..dc235ea8f7447d430728b8578e560c98dd14d8c1 100644 (file)
@@ -568,12 +568,13 @@ func newName(n, tag string, exported bool) name {
 // Method represents a single method.
 type Method struct {
        // Name is the method name.
+       Name string
+
        // PkgPath is the package path that qualifies a lower case (unexported)
        // method name. It is empty for upper case (exported) method names.
        // The combination of PkgPath and Name uniquely identifies a method
        // in a method set.
        // See https://golang.org/ref/spec#Uniqueness_of_identifiers
-       Name    string
        PkgPath string
 
        Type  Type  // method type
@@ -581,6 +582,11 @@ type Method struct {
        Index int   // index for Type.Method
 }
 
+// IsExported reports whether the method is exported.
+func (m Method) IsExported() bool {
+       return m.PkgPath == ""
+}
+
 const (
        kindDirectIface = 1 << 5
        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 {
        // Name is the field name.
        Name string
+
        // PkgPath is the package path that qualifies a lower case (unexported)
        // field name. It is empty for upper case (exported) field names.
        // See https://golang.org/ref/spec#Uniqueness_of_identifiers
@@ -1102,6 +1109,11 @@ type StructField struct {
        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.
 //
 // 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")
        }
 
-       exported := field.PkgPath == ""
-       if exported {
+       if field.IsExported() {
                // Best-effort check for misuse.
                // Since this field will be treated as exported, not much harm done if Unicode lowercase slips through.
                c := field.Name[0]
@@ -2788,7 +2799,7 @@ func runtimeStructField(field StructField) (structField, string) {
 
        resolveReflectType(field.Type.common()) // install in runtime
        f := structField{
-               name:        newName(field.Name, string(field.Tag), exported),
+               name:        newName(field.Name, string(field.Tag), field.IsExported()),
                typ:         field.Type.common(),
                offsetEmbed: offsetEmbed,
        }
index 19154fc6405cc1096d5c0e8730172d7acd016cdb..4637b2035f42a20b451bdae8198215aff60483bf 100644 (file)
@@ -615,7 +615,7 @@ func (s *state) evalField(dot reflect.Value, fieldName string, node parse.Node,
                tField, ok := receiver.Type().FieldByName(fieldName)
                if ok {
                        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)
                        }
                        // If it's a function, we must call it.