aboutsummaryrefslogtreecommitdiff
path: root/src/pkg/text/template/exec.go
diff options
context:
space:
mode:
authorRob Pike <r@golang.org>2012-04-23 15:39:02 +1000
committerRob Pike <r@golang.org>2012-04-23 15:39:02 +1000
commita8098cbcfd7772911f761e787f656f6e685c105e (patch)
tree2bc028cbce7a7aca47d1855b3ab4031bc2cf037e /src/pkg/text/template/exec.go
parent53372903c70e93704cc32dc229d8d83a03bcc457 (diff)
downloadgo-a8098cbcfd7772911f761e787f656f6e685c105e.tar.xz
text/template: detect unexported fields better
Moves the error detection back into execution, where it used to be, and improves the error message. Rolls back most of 6009048, which broke lower-case keys in maps. If it weren't for maps we could detect this at compile time rather than execution time. Fixes #3542. R=golang-dev, dsymonds CC=golang-dev https://golang.org/cl/6098051
Diffstat (limited to 'src/pkg/text/template/exec.go')
-rw-r--r--src/pkg/text/template/exec.go27
1 files changed, 19 insertions, 8 deletions
diff --git a/src/pkg/text/template/exec.go b/src/pkg/text/template/exec.go
index feb434a3be..b8d23d43f9 100644
--- a/src/pkg/text/template/exec.go
+++ b/src/pkg/text/template/exec.go
@@ -12,6 +12,8 @@ import (
"sort"
"strings"
"text/template/parse"
+ "unicode"
+ "unicode/utf8"
)
// state represents the state of an execution. It's not part of the
@@ -414,9 +416,13 @@ func (s *state) evalField(dot reflect.Value, fieldName string, args []parse.Node
return s.evalCall(dot, method, fieldName, args, final)
}
hasArgs := len(args) > 1 || final.IsValid()
- // It's not a method; is it a field of a struct?
+ // It's not a method; must be a field of a struct or an element of a map. The receiver must not be nil.
receiver, isNil := indirect(receiver)
- if receiver.Kind() == reflect.Struct {
+ if isNil {
+ s.errorf("nil pointer evaluating %s.%s", typ, fieldName)
+ }
+ switch receiver.Kind() {
+ case reflect.Struct:
tField, ok := receiver.Type().FieldByName(fieldName)
if ok {
field := receiver.FieldByIndex(tField.Index)
@@ -428,9 +434,11 @@ func (s *state) evalField(dot reflect.Value, fieldName string, args []parse.Node
return field
}
}
- }
- // If it's a map, attempt to use the field name as a key.
- if receiver.Kind() == reflect.Map {
+ if !isExported(fieldName) {
+ s.errorf("%s is not an exported field of struct type %s", fieldName, typ)
+ }
+ case reflect.Map:
+ // If it's a map, attempt to use the field name as a key.
nameVal := reflect.ValueOf(fieldName)
if nameVal.Type().AssignableTo(receiver.Type().Key()) {
if hasArgs {
@@ -439,13 +447,16 @@ func (s *state) evalField(dot reflect.Value, fieldName string, args []parse.Node
return receiver.MapIndex(nameVal)
}
}
- if isNil {
- s.errorf("nil pointer evaluating %s.%s", typ, fieldName)
- }
s.errorf("can't evaluate field %s in type %s", fieldName, typ)
panic("not reached")
}
+// isExported reports whether the field name (which starts with a period) can be accessed.
+func isExported(fieldName string) bool {
+ r, _ := utf8.DecodeRuneInString(fieldName[1:]) // drop the period
+ return unicode.IsUpper(r)
+}
+
var (
errorType = reflect.TypeOf((*error)(nil)).Elem()
fmtStringerType = reflect.TypeOf((*fmt.Stringer)(nil)).Elem()