]> Cypherpunks repositories - gostls13.git/commitdiff
text/template: fix a couple of parse bugs around identifiers.
authorRob Pike <r@golang.org>
Tue, 13 Mar 2012 20:03:11 +0000 (07:03 +1100)
committerRob Pike <r@golang.org>
Tue, 13 Mar 2012 20:03:11 +0000 (07:03 +1100)
1) Poor error checking in variable declarations admitted
$x=2 or even $x%2.
2) Need white space or suitable termination character
after identifiers, so $x+2 doesn't parse, in case we want it
to mean something one day.
Number 2 in particular prevents mistakes that we will have
to honor later and so is necessary for Go 1.

Fixes #3270.
Fixes #3271.

R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/5795073

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

index 22dedc4f83939777345765418fc0ef22e1db00a3..bd98bd047ec46b8cff57ea9f5f7634e4003cf215 100644 (file)
@@ -93,7 +93,7 @@ var multiExecTests = []execTest{
        {"invoke dot []int", `{{template "dot" .SI}}`, "[3 4 5]", tVal, true},
        {"invoke dotV", `{{template "dotV" .U}}`, "v", tVal, true},
        {"invoke nested int", `{{template "nested" .I}}`, "17", tVal, true},
-       {"variable declared by template", `{{template "nested" $x=.SI}},{{index $x 1}}`, "[3 4 5],4", tVal, true},
+       {"variable declared by template", `{{template "nested" $x:=.SI}},{{index $x 1}}`, "[3 4 5],4", tVal, true},
 
        // User-defined function: test argument evaluator.
        {"testFunc literal", `{{oneArg "joe"}}`, "oneArg=joe", tVal, true},
index 54e75ee0ca25e19d566c64767ef567fb2a556369..7705c0b88ff8c0051620b5efd4af69ee7bbcc2f0 100644 (file)
@@ -347,6 +347,9 @@ Loop:
                default:
                        l.backup()
                        word := l.input[l.start:l.pos]
+                       if !l.atTerminator() {
+                               return l.errorf("unexpected character %+U", r)
+                       }
                        switch {
                        case key[word] > itemKeyword:
                                l.emit(key[word])
@@ -365,6 +368,28 @@ Loop:
        return lexInsideAction
 }
 
+// atTerminator reports whether the input is at valid termination character to
+// appear after an identifier. Mostly to catch cases like "$x+2" not being
+// acceptable without a space, in case we decide one day to implement
+// arithmetic.
+func (l *lexer) atTerminator() bool {
+       r := l.peek()
+       if isSpace(r) {
+               return true
+       }
+       switch r {
+       case eof, ',', '|', ':':
+               return true
+       }
+       // Does r start the delimiter? This can be ambiguous (with delim=="//", $x/2 will
+       // succeed but should fail) but only in extremely rare cases caused by willfully
+       // bad choice of delimiter.
+       if rd, _ := utf8.DecodeRuneInString(l.rightDelim); rd == r {
+               return true
+       }
+       return false
+}
+
 // lexChar scans a character constant. The initial quote is already
 // scanned.  Syntax checking is done by the parse.
 func lexChar(l *lexer) stateFn {
index d67b3888085cfee4dbe119f870a0ed32d2c30dbe..c0087b2785b4ad5a4b7bff4b58b1fd3f0e20d11a 100644 (file)
@@ -326,7 +326,7 @@ func (t *Tree) pipeline(context string) (pipe *PipeNode) {
        for {
                if v := t.peek(); v.typ == itemVariable {
                        t.next()
-                       if next := t.peek(); next.typ == itemColonEquals || next.typ == itemChar {
+                       if next := t.peek(); next.typ == itemColonEquals || (next.typ == itemChar && next.val == ",") {
                                t.next()
                                variable := newVariable(v.val)
                                if len(variable.Ident) != 1 {
index 18c0a8b835e3a57cfabe91f28f677f6b6da7e8ee..b2e788238d3acb200c3416e8c4fe21ce35b86385 100644 (file)
@@ -201,6 +201,10 @@ var parseTests = []parseTest{
                `{{range .X | .M}}"true"{{else}}"false"{{end}}`},
        {"range []int", "{{range .SI}}{{.}}{{end}}", noError,
                `{{range .SI}}{{.}}{{end}}`},
+       {"range 1 var", "{{range $x := .SI}}{{.}}{{end}}", noError,
+               `{{range $x := .SI}}{{.}}{{end}}`},
+       {"range 2 vars", "{{range $x, $y := .SI}}{{.}}{{end}}", noError,
+               `{{range $x, $y := .SI}}{{.}}{{end}}`},
        {"constants", "{{range .SI 1 -3.2i true false 'a'}}{{end}}", noError,
                `{{range .SI 1 -3.2i true false 'a'}}{{end}}`},
        {"template", "{{template `x`}}", noError,
@@ -226,6 +230,17 @@ 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, ""},
+       // Equals (and other chars) do not assignments make (yet).
+       {"bug0a", "{{$x := 0}}{{$x}}", noError, "{{$x := 0}}{{$x}}"},
+       {"bug0b", "{{$x = 1}}{{$x}}", hasError, ""},
+       {"bug0c", "{{$x ! 2}}{{$x}}", hasError, ""},
+       {"bug0d", "{{$x % 3}}{{$x}}", hasError, ""},
+       // Check the parse fails for := rather than comma.
+       {"bug0e", "{{range $x := $y := 3}}{{end}}", hasError, ""},
+       // Another bug: variable read must ignore following punctuation.
+       {"bug1a", "{{$x:=.}}{{$x!2}}", hasError, ""},                     // ! is just illegal here.
+       {"bug1b", "{{$x:=.}}{{$x+2}}", hasError, ""},                     // $x+2 should not parse as ($x) (+2).
+       {"bug1c", "{{$x:=.}}{{$x +2}}", noError, "{{$x := .}}{{$x +2}}"}, // It's OK with a space.
 }
 
 var builtins = map[string]interface{}{