]> Cypherpunks repositories - gostls13.git/commitdiff
text/template/parse: fix interaction of break/continue keywords and functions
authorEli Bendersky <eliben@golang.org>
Thu, 3 Nov 2022 19:16:06 +0000 (12:16 -0700)
committerGopher Robot <gobot@golang.org>
Fri, 4 Nov 2022 12:56:08 +0000 (12:56 +0000)
Fixes a bug that was introduced in CL 410414; in that CL, to avoid
a race condition in the initialization of the lexer, the setting
of the breakOK and continueOK options was moved to before
Tree.funcs was populated from parameters. As a result, the parser
missed the fact that 'break' and 'continue' were defined as functions.

Following CL 421883 race conditions are no longer an issue, so
the simplest fix is just to move the initialization where it was
before - in startParse, after t.funcs has been set.

Fixes #56538

Change-Id: I3b99fe9ad12255a4f6eb9a00eb3f64529ff055c0
Reviewed-on: https://go-review.googlesource.com/c/go/+/447775
Run-TryBot: Eli Bendersky‎ <eliben@golang.org>
Auto-Submit: Eli Bendersky‎ <eliben@golang.org>
Reviewed-by: Rob Pike <r@golang.org>
Reviewed-by: Eli Bendersky <eliben@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@google.com>
src/text/template/parse/parse.go
src/text/template/parse/parse_test.go

index 87b7618f75769eed96d5f10074abd6579ab8fccb..d43d5334ba04f3c3f7bdbee23378f1155d53a716 100644 (file)
@@ -223,6 +223,11 @@ func (t *Tree) startParse(funcs []map[string]any, lex *lexer, treeSet map[string
        t.vars = []string{"$"}
        t.funcs = funcs
        t.treeSet = treeSet
+       lex.options = lexOptions{
+               emitComment: t.Mode&ParseComments != 0,
+               breakOK:     !t.hasFunction("break"),
+               continueOK:  !t.hasFunction("continue"),
+       }
 }
 
 // stopParse terminates parsing.
@@ -241,11 +246,6 @@ func (t *Tree) Parse(text, leftDelim, rightDelim string, treeSet map[string]*Tre
        defer t.recover(&err)
        t.ParseName = t.Name
        lexer := lex(t.Name, text, leftDelim, rightDelim)
-       lexer.options = lexOptions{
-               emitComment: t.Mode&ParseComments != 0,
-               breakOK:     !t.hasFunction("break"),
-               continueOK:  !t.hasFunction("continue"),
-       }
        t.startParse(funcs, lexer, treeSet)
        t.text = text
        t.parse()
index f6a9fdd8720c3e00c2f23f05c5c1c3d721f7fdff..59e0a17412235b1c2645543e49e02c0b5ff9827b 100644 (file)
@@ -391,6 +391,36 @@ func TestParseWithComments(t *testing.T) {
        }
 }
 
+func TestKeywordsAndFuncs(t *testing.T) {
+       // Check collisions between functions and new keywords like 'break'. When a
+       // break function is provided, the parser should treat 'break' as a function,
+       // not a keyword.
+       textFormat = "%q"
+       defer func() { textFormat = "%s" }()
+
+       inp := `{{range .X}}{{break 20}}{{end}}`
+       {
+               // 'break' is a defined function, don't treat it as a keyword: it should
+               // accept an argument successfully.
+               var funcsWithKeywordFunc = map[string]any{
+                       "break": func(in any) any { return in },
+               }
+               tmpl, err := New("").Parse(inp, "", "", make(map[string]*Tree), funcsWithKeywordFunc)
+               if err != nil || tmpl == nil {
+                       t.Errorf("with break func: unexpected error: %v", err)
+               }
+       }
+
+       {
+               // No function called 'break'; treat it as a keyword. Results in a parse
+               // error.
+               tmpl, err := New("").Parse(inp, "", "", make(map[string]*Tree), make(map[string]any))
+               if err == nil || tmpl != nil {
+                       t.Errorf("without break func: expected error; got none")
+               }
+       }
+}
+
 func TestSkipFuncCheck(t *testing.T) {
        oldTextFormat := textFormat
        textFormat = "%q"