aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRob Pike <r@golang.org>2011-01-12 00:25:17 -0800
committerRob Pike <r@golang.org>2011-01-12 00:25:17 -0800
commit29cbad6c72a734bda3fe117da7bfe7344f3de26d (patch)
tree65b84db1cfdc30e1138d8b207f56a3e37465c684
parente3c9565b438cb2eb4be6c2bd57696e378f754e46 (diff)
downloadgo-29cbad6c72a734bda3fe117da7bfe7344f3de26d.tar.gz
go-29cbad6c72a734bda3fe117da7bfe7344f3de26d.zip
template: give "unexported" error when accessing an unexported field.
R=adg, rsc CC=golang-dev https://golang.org/cl/3903043
-rw-r--r--src/pkg/template/template.go12
-rw-r--r--src/pkg/template/template_test.go23
2 files changed, 29 insertions, 6 deletions
diff --git a/src/pkg/template/template.go b/src/pkg/template/template.go
index 3abfc2eaec..a67dbf8ad2 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 f60c0127e2..57f297e8f0 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)
+ }
+}