From 29cbad6c72a734bda3fe117da7bfe7344f3de26d Mon Sep 17 00:00:00 2001 From: Rob Pike Date: Wed, 12 Jan 2011 00:25:17 -0800 Subject: [PATCH] template: give "unexported" error when accessing an unexported field. R=adg, rsc CC=golang-dev https://golang.org/cl/3903043 --- src/pkg/template/template.go | 12 ++++++------ src/pkg/template/template_test.go | 23 +++++++++++++++++++++++ 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/src/pkg/template/template.go b/src/pkg/template/template.go index 3abfc2eaec1..a67dbf8ad24 100644 --- a/src/pkg/template/template.go +++ b/src/pkg/template/template.go @@ -596,7 +596,7 @@ func (t *Template) parse() { // Evaluate interfaces and pointers looking for a value that can look up the name, via a // struct field, method, or map key, and return the result of the lookup. -func lookup(v reflect.Value, name string) reflect.Value { +func (t *Template) lookup(st *state, v reflect.Value, name string) reflect.Value { for v != nil { typ := v.Type() if n := v.Type().NumMethod(); n > 0 { @@ -605,7 +605,7 @@ func lookup(v reflect.Value, name string) reflect.Value { mtyp := m.Type if m.Name == name && mtyp.NumIn() == 1 && mtyp.NumOut() == 1 { if !isExported(name) { - return nil + t.execError(st, t.linenum, "name not exported: %s in type %s", name, st.data.Type()) } return v.Method(i).Call(nil)[0] } @@ -618,7 +618,7 @@ func lookup(v reflect.Value, name string) reflect.Value { v = av.Elem() case *reflect.StructValue: if !isExported(name) { - return nil + t.execError(st, t.linenum, "name not exported: %s in type %s", name, st.data.Type()) } return av.FieldByName(name) case *reflect.MapValue: @@ -652,14 +652,14 @@ loop: // The value coming in (st.data) might need indirecting to reach // a struct while the return value is not indirected - that is, // it represents the actual named field. -func (st *state) findVar(s string) reflect.Value { +func (t *Template) findVar(st *state, s string) reflect.Value { if s == "@" { return st.data } data := st.data for _, elem := range strings.Split(s, ".", -1) { // Look up field; data must be a struct or map. - data = lookup(data, elem) + data = t.lookup(st, data, elem) if data == nil { return nil } @@ -692,7 +692,7 @@ func empty(v reflect.Value) bool { // Look up a variable or method, up through the parent if necessary. func (t *Template) varValue(name string, st *state) reflect.Value { - field := st.findVar(name) + field := t.findVar(st, name) if field == nil { if st.parent == nil { t.execError(st, t.linenum, "name not found: %s in type %s", name, st.data.Type()) diff --git a/src/pkg/template/template_test.go b/src/pkg/template/template_test.go index f60c0127e2d..57f297e8f0a 100644 --- a/src/pkg/template/template_test.go +++ b/src/pkg/template/template_test.go @@ -12,6 +12,7 @@ import ( "io/ioutil" "json" "os" + "strings" "testing" ) @@ -635,3 +636,25 @@ func TestHTMLFormatterWithByte(t *testing.T) { t.Errorf("munged []byte, expected: %s got: %s", s, bs) } } + +type UF struct { + I int + s string +} + +func TestReferenceToUnexported(t *testing.T) { + u := &UF{3, "hello"} + var buf bytes.Buffer + input := "{.section @}{I}{s}{.end}" + tmpl, err := Parse(input, nil) + if err != nil { + t.Fatal("unexpected parse error:", err) + } + err = tmpl.Execute(u, &buf) + if err == nil { + t.Fatal("expected execute error, got none") + } + if strings.Index(err.String(), "not exported") < 0 { + t.Fatal("expected unexported error; got", err) + } +}