1
0
mirror of https://github.com/golang/go synced 2024-11-19 21:14:43 -07:00

text/template: catch unexported fields during parse

It's a common error to reference unexported field names in templates,
especially for newcomers. This catches the error at parse time rather than
execute time so the rare few who check errors will notice right away.

These were always an error, so the net behavior is unchanged.
Should break no existing code, just identify the error earlier.

R=golang-dev, dsymonds
CC=golang-dev
https://golang.org/cl/6009048
This commit is contained in:
Rob Pike 2012-04-12 15:57:09 +10:00
parent e6c5e2a363
commit 2d0d3d8f9e
3 changed files with 12 additions and 1 deletions

View File

@ -348,7 +348,7 @@ Loop:
l.backup() l.backup()
word := l.input[l.start:l.pos] word := l.input[l.start:l.pos]
if !l.atTerminator() { if !l.atTerminator() {
return l.errorf("unexpected character %+U", r) return l.errorf("bad character %+U", r)
} }
switch { switch {
case key[word] > itemKeyword: case key[word] > itemKeyword:

View File

@ -14,6 +14,7 @@ import (
"runtime" "runtime"
"strconv" "strconv"
"unicode" "unicode"
"unicode/utf8"
) )
// Tree is the representation of a single parsed template. // Tree is the representation of a single parsed template.
@ -473,6 +474,9 @@ Loop:
case itemVariable: case itemVariable:
cmd.append(t.useVar(token.val)) cmd.append(t.useVar(token.val))
case itemField: case itemField:
if !isExported(token.val) {
t.errorf("field %q not exported; cannot be evaluated", token.val)
}
cmd.append(newField(token.val)) cmd.append(newField(token.val))
case itemBool: case itemBool:
cmd.append(newBool(token.val == "true")) cmd.append(newBool(token.val == "true"))
@ -498,6 +502,12 @@ Loop:
return cmd return cmd
} }
// 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)
}
// hasFunction reports if a function name exists in the Tree's maps. // hasFunction reports if a function name exists in the Tree's maps.
func (t *Tree) hasFunction(name string) bool { func (t *Tree) hasFunction(name string) bool {
for _, funcMap := range t.funcs { for _, funcMap := range t.funcs {

View File

@ -230,6 +230,7 @@ var parseTests = []parseTest{
{"invalid punctuation", "{{printf 3, 4}}", hasError, ""}, {"invalid punctuation", "{{printf 3, 4}}", hasError, ""},
{"multidecl outside range", "{{with $v, $u := 3}}{{end}}", hasError, ""}, {"multidecl outside range", "{{with $v, $u := 3}}{{end}}", hasError, ""},
{"too many decls in range", "{{range $u, $v, $w := 3}}{{end}}", hasError, ""}, {"too many decls in range", "{{range $u, $v, $w := 3}}{{end}}", hasError, ""},
{"unexported field", "{{.local}}", hasError, ""},
// Equals (and other chars) do not assignments make (yet). // Equals (and other chars) do not assignments make (yet).
{"bug0a", "{{$x := 0}}{{$x}}", noError, "{{$x := 0}}{{$x}}"}, {"bug0a", "{{$x := 0}}{{$x}}", noError, "{{$x := 0}}{{$x}}"},
{"bug0b", "{{$x = 1}}{{$x}}", hasError, ""}, {"bug0b", "{{$x = 1}}{{$x}}", hasError, ""},