]> Cypherpunks repositories - gostls13.git/commitdiff
text/template: catch unexported fields during parse
authorRob Pike <r@golang.org>
Thu, 12 Apr 2012 05:57:09 +0000 (15:57 +1000)
committerRob Pike <r@golang.org>
Thu, 12 Apr 2012 05:57:09 +0000 (15:57 +1000)
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

src/pkg/text/template/parse/lex.go
src/pkg/text/template/parse/parse.go
src/pkg/text/template/parse/parse_test.go

index 7705c0b88ff8c0051620b5efd4af69ee7bbcc2f0..5674a940b1a5a08554a9f1e6472649506e4a1b22 100644 (file)
@@ -348,7 +348,7 @@ Loop:
                        l.backup()
                        word := l.input[l.start:l.pos]
                        if !l.atTerminator() {
-                               return l.errorf("unexpected character %+U", r)
+                               return l.errorf("bad character %+U", r)
                        }
                        switch {
                        case key[word] > itemKeyword:
index c0087b2785b4ad5a4b7bff4b58b1fd3f0e20d11a..fd8cf433fa4f877baf2b12501777c88601f976d6 100644 (file)
@@ -14,6 +14,7 @@ import (
        "runtime"
        "strconv"
        "unicode"
+       "unicode/utf8"
 )
 
 // Tree is the representation of a single parsed template.
@@ -473,6 +474,9 @@ Loop:
                case itemVariable:
                        cmd.append(t.useVar(token.val))
                case itemField:
+                       if !isExported(token.val) {
+                               t.errorf("field %q not exported; cannot be evaluated", token.val)
+                       }
                        cmd.append(newField(token.val))
                case itemBool:
                        cmd.append(newBool(token.val == "true"))
@@ -498,6 +502,12 @@ Loop:
        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.
 func (t *Tree) hasFunction(name string) bool {
        for _, funcMap := range t.funcs {
index b2e788238d3acb200c3416e8c4fe21ce35b86385..fb98613fe15ce4ca92247a87807f33dc68fb3faf 100644 (file)
@@ -230,6 +230,7 @@ var parseTests = []parseTest{
        {"invalid punctuation", "{{printf 3, 4}}", hasError, ""},
        {"multidecl outside range", "{{with $v, $u := 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).
        {"bug0a", "{{$x := 0}}{{$x}}", noError, "{{$x := 0}}{{$x}}"},
        {"bug0b", "{{$x = 1}}{{$x}}", hasError, ""},