tpl/internal/go_templates: Revert formatting

Should make future fork synch easier.
This commit is contained in:
Bjørn Erik Pedersen 2020-12-03 13:47:43 +01:00
parent d90e37e0c6
commit 718e09ed4b
No known key found for this signature in database
GPG key ID: 330E6E2BD4859D8F
21 changed files with 173 additions and 322 deletions

View file

@ -6,12 +6,11 @@ package fmtsort_test
import ( import (
"fmt" "fmt"
"github.com/gohugoio/hugo/tpl/internal/go_templates/fmtsort"
"math" "math"
"reflect" "reflect"
"strings" "strings"
"testing" "testing"
"github.com/gohugoio/hugo/tpl/internal/go_templates/fmtsort"
) )
var compareTests = [][]reflect.Value{ var compareTests = [][]reflect.Value{

View file

@ -427,7 +427,7 @@ func TestStringer(t *testing.T) {
if err := tmpl.Execute(b, s); err != nil { if err := tmpl.Execute(b, s); err != nil {
t.Fatal(err) t.Fatal(err)
} }
expect := "string=3" var expect = "string=3"
if b.String() != expect { if b.String() != expect {
t.Errorf("expected %q got %q", expect, b.String()) t.Errorf("expected %q got %q", expect, b.String())
} }

View file

@ -210,10 +210,8 @@ var cssReplacementTable = []string{
'}': `\7d`, '}': `\7d`,
} }
var ( var expressionBytes = []byte("expression")
expressionBytes = []byte("expression") var mozBindingBytes = []byte("mozbinding")
mozBindingBytes = []byte("mozbinding")
)
// cssValueFilter allows innocuous CSS values in the output including CSS // cssValueFilter allows innocuous CSS values in the output including CSS
// quantities (10px or 25%), ID or class literals (#foo, .bar), keyword values // quantities (10px or 25%), ID or class literals (#foo, .bar), keyword values

View file

@ -891,6 +891,7 @@ func TestEscapeSet(t *testing.T) {
t.Errorf("want\n\t%q\ngot\n\t%q", test.want, got) t.Errorf("want\n\t%q\ngot\n\t%q", test.want, got)
} }
} }
} }
func TestErrors(t *testing.T) { func TestErrors(t *testing.T) {
@ -1087,6 +1088,7 @@ func TestErrors(t *testing.T) {
// Check that we get the same error if we call Execute again. // Check that we get the same error if we call Execute again.
if err := tmpl.Execute(buf, nil); err == nil || err.Error() != got { if err := tmpl.Execute(buf, nil); err == nil || err.Error() != got {
t.Errorf("input=%q: unexpected error on second call %q", test.input, err) t.Errorf("input=%q: unexpected error on second call %q", test.input, err)
} }
} }
} }

View file

@ -82,6 +82,7 @@ func Example() {
// <div><strong>no rows</strong></div> // <div><strong>no rows</strong></div>
// </body> // </body>
// </html> // </html>
} }
func Example_autoescaping() { func Example_autoescaping() {
@ -122,6 +123,7 @@ func Example_escape() {
// \"Fran \u0026 Freddie\'s Diner\" \u003Ctasty@example.com\u003E // \"Fran \u0026 Freddie\'s Diner\" \u003Ctasty@example.com\u003E
// \"Fran \u0026 Freddie\'s Diner\"32\u003Ctasty@example.com\u003E // \"Fran \u0026 Freddie\'s Diner\"32\u003Ctasty@example.com\u003E
// %22Fran+%26+Freddie%27s+Diner%2232%3Ctasty%40example.com%3E // %22Fran+%26+Freddie%27s+Diner%2232%3Ctasty%40example.com%3E
} }
func ExampleTemplate_Delims() { func ExampleTemplate_Delims() {

View file

@ -337,7 +337,6 @@ var jsStrNormReplacementTable = []string{
'<': `\u003c`, '<': `\u003c`,
'>': `\u003e`, '>': `\u003e`,
} }
var jsRegexpReplacementTable = []string{ var jsRegexpReplacementTable = []string{
0: `\u0000`, 0: `\u0000`,
'\t': `\t`, '\t': `\t`,

View file

@ -206,8 +206,7 @@ func TestJSStrEscaper(t *testing.T) {
{"<!--", `\u003c!--`}, {"<!--", `\u003c!--`},
{"-->", `--\u003e`}, {"-->", `--\u003e`},
// From https://code.google.com/p/doctype/wiki/ArticleUtf7 // From https://code.google.com/p/doctype/wiki/ArticleUtf7
{ {"+ADw-script+AD4-alert(1)+ADw-/script+AD4-",
"+ADw-script+AD4-alert(1)+ADw-/script+AD4-",
`\u002bADw-script\u002bAD4-alert(1)\u002bADw-\/script\u002bAD4-`, `\u002bADw-script\u002bAD4-alert(1)\u002bADw-\/script\u002bAD4-`,
}, },
// Invalid UTF-8 sequence // Invalid UTF-8 sequence

View file

@ -41,10 +41,8 @@ var transitionFunc = [...]func(context, []byte) (context, int){
stateError: tError, stateError: tError,
} }
var ( var commentStart = []byte("<!--")
commentStart = []byte("<!--") var commentEnd = []byte("-->")
commentEnd = []byte("-->")
)
// tText is the context transition function for the text state. // tText is the context transition function for the text state.
func tText(c context, s []byte) (context, int) { func tText(c context, s []byte) (context, int) {

View file

@ -42,6 +42,7 @@ func TestFindEndTag(t *testing.T) {
} }
func BenchmarkTemplateSpecialTags(b *testing.B) { func BenchmarkTemplateSpecialTags(b *testing.B) {
r := struct { r := struct {
Name, Gift string Name, Gift string
}{"Aunt Mildred", "bone china tea set"} }{"Aunt Mildred", "bone china tea set"}

View file

@ -13,6 +13,7 @@ package testenv
import ( import (
"errors" "errors"
"flag" "flag"
"github.com/gohugoio/hugo/tpl/internal/go_templates/cfg"
"os" "os"
"os/exec" "os/exec"
"path/filepath" "path/filepath"
@ -21,8 +22,6 @@ import (
"strings" "strings"
"sync" "sync"
"testing" "testing"
"github.com/gohugoio/hugo/tpl/internal/go_templates/cfg"
) )
// Builder reports the name of the builder running this test // Builder reports the name of the builder running this test

View file

@ -12,10 +12,8 @@ import (
"syscall" "syscall"
) )
var ( var symlinkOnce sync.Once
symlinkOnce sync.Once var winSymlinkErr error
winSymlinkErr error
)
func initWinHasSymlink() { func initWinHasSymlink() {
tmpdir, err := ioutil.TempDir("", "symtest") tmpdir, err := ioutil.TempDir("", "symtest")

View file

@ -34,7 +34,7 @@ Josie
Name, Gift string Name, Gift string
Attended bool Attended bool
} }
recipients := []Recipient{ var recipients = []Recipient{
{"Aunt Mildred", "bone china tea set", true}, {"Aunt Mildred", "bone china tea set", true},
{"Uncle John", "moleskin pants", false}, {"Uncle John", "moleskin pants", false},
{"Cousin Rodney", "", false}, {"Cousin Rodney", "", false},

View file

@ -6,13 +6,12 @@ package template
import ( import (
"fmt" "fmt"
"github.com/gohugoio/hugo/tpl/internal/go_templates/fmtsort"
"github.com/gohugoio/hugo/tpl/internal/go_templates/texttemplate/parse"
"io" "io"
"reflect" "reflect"
"runtime" "runtime"
"strings" "strings"
"github.com/gohugoio/hugo/tpl/internal/go_templates/fmtsort"
"github.com/gohugoio/hugo/tpl/internal/go_templates/texttemplate/parse"
) )
// maxExecDepth specifies the maximum stack depth of templates within // maxExecDepth specifies the maximum stack depth of templates within

View file

@ -318,16 +318,12 @@ var execTests = []execTest{
{"$.U.V", "{{$.U.V}}", "v", tVal, true}, {"$.U.V", "{{$.U.V}}", "v", tVal, true},
{"declare in action", "{{$x := $.U.V}}{{$x}}", "v", tVal, true}, {"declare in action", "{{$x := $.U.V}}{{$x}}", "v", tVal, true},
{"simple assignment", "{{$x := 2}}{{$x = 3}}{{$x}}", "3", tVal, true}, {"simple assignment", "{{$x := 2}}{{$x = 3}}{{$x}}", "3", tVal, true},
{ {"nested assignment",
"nested assignment",
"{{$x := 2}}{{if true}}{{$x = 3}}{{end}}{{$x}}", "{{$x := 2}}{{if true}}{{$x = 3}}{{end}}{{$x}}",
"3", tVal, true, "3", tVal, true},
}, {"nested assignment changes the last declaration",
{
"nested assignment changes the last declaration",
"{{$x := 1}}{{if true}}{{$x := 2}}{{if true}}{{$x = 3}}{{end}}{{end}}{{$x}}", "{{$x := 1}}{{if true}}{{$x := 2}}{{if true}}{{$x = 3}}{{end}}{{end}}{{$x}}",
"1", tVal, true, "1", tVal, true},
},
// Type with String method. // Type with String method.
{"V{6666}.String()", "-{{.V0}}-", "-<6666>-", tVal, true}, {"V{6666}.String()", "-{{.V0}}-", "-<6666>-", tVal, true},
@ -374,21 +370,15 @@ var execTests = []execTest{
{".Method3(nil constant)", "-{{.Method3 nil}}-", "-Method3: <nil>-", tVal, true}, {".Method3(nil constant)", "-{{.Method3 nil}}-", "-Method3: <nil>-", tVal, true},
{".Method3(nil value)", "-{{.Method3 .MXI.unset}}-", "-Method3: <nil>-", tVal, true}, {".Method3(nil value)", "-{{.Method3 .MXI.unset}}-", "-Method3: <nil>-", tVal, true},
{"method on var", "{{if $x := .}}-{{$x.Method2 .U16 $x.X}}{{end}}-", "-Method2: 16 x-", tVal, true}, {"method on var", "{{if $x := .}}-{{$x.Method2 .U16 $x.X}}{{end}}-", "-Method2: 16 x-", tVal, true},
{ {"method on chained var",
"method on chained var",
"{{range .MSIone}}{{if $.U.TrueFalse $.True}}{{$.U.TrueFalse $.True}}{{else}}WRONG{{end}}{{end}}", "{{range .MSIone}}{{if $.U.TrueFalse $.True}}{{$.U.TrueFalse $.True}}{{else}}WRONG{{end}}{{end}}",
"true", tVal, true, "true", tVal, true},
}, {"chained method",
{
"chained method",
"{{range .MSIone}}{{if $.GetU.TrueFalse $.True}}{{$.U.TrueFalse $.True}}{{else}}WRONG{{end}}{{end}}", "{{range .MSIone}}{{if $.GetU.TrueFalse $.True}}{{$.U.TrueFalse $.True}}{{else}}WRONG{{end}}{{end}}",
"true", tVal, true, "true", tVal, true},
}, {"chained method on variable",
{
"chained method on variable",
"{{with $x := .}}{{with .SI}}{{$.GetU.TrueFalse $.True}}{{end}}{{end}}", "{{with $x := .}}{{with .SI}}{{$.GetU.TrueFalse $.True}}{{end}}{{end}}",
"true", tVal, true, "true", tVal, true},
},
{".NilOKFunc not nil", "{{call .NilOKFunc .PI}}", "false", tVal, true}, {".NilOKFunc not nil", "{{call .NilOKFunc .PI}}", "false", tVal, true},
{".NilOKFunc nil", "{{call .NilOKFunc nil}}", "true", tVal, true}, {".NilOKFunc nil", "{{call .NilOKFunc nil}}", "true", tVal, true},
{"method on nil value from slice", "-{{range .}}{{.Method1 1234}}{{end}}-", "-1234-", tSliceOfNil, true}, {"method on nil value from slice", "-{{range .}}{{.Method1 1234}}{{end}}-", "-1234-", tSliceOfNil, true},
@ -474,14 +464,10 @@ var execTests = []execTest{
{"printf lots", `{{printf "%d %s %g %s" 127 "hello" 7-3i .Method0}}`, "127 hello (7-3i) M0", tVal, true}, {"printf lots", `{{printf "%d %s %g %s" 127 "hello" 7-3i .Method0}}`, "127 hello (7-3i) M0", tVal, true},
// HTML. // HTML.
{ {"html", `{{html "<script>alert(\"XSS\");</script>"}}`,
"html", `{{html "<script>alert(\"XSS\");</script>"}}`, "&lt;script&gt;alert(&#34;XSS&#34;);&lt;/script&gt;", nil, true},
"&lt;script&gt;alert(&#34;XSS&#34;);&lt;/script&gt;", nil, true, {"html pipeline", `{{printf "<script>alert(\"XSS\");</script>" | html}}`,
}, "&lt;script&gt;alert(&#34;XSS&#34;);&lt;/script&gt;", nil, true},
{
"html pipeline", `{{printf "<script>alert(\"XSS\");</script>" | html}}`,
"&lt;script&gt;alert(&#34;XSS&#34;);&lt;/script&gt;", nil, true,
},
{"html", `{{html .PS}}`, "a string", tVal, true}, {"html", `{{html .PS}}`, "a string", tVal, true},
{"html typed nil", `{{html .NIL}}`, "&lt;nil&gt;", tVal, true}, {"html typed nil", `{{html .NIL}}`, "&lt;nil&gt;", tVal, true},
{"html untyped nil", `{{html .Empty0}}`, "&lt;no value&gt;", tVal, true}, {"html untyped nil", `{{html .Empty0}}`, "&lt;no value&gt;", tVal, true},
@ -839,7 +825,7 @@ var delimPairs = []string{
func TestDelims(t *testing.T) { func TestDelims(t *testing.T) {
const hello = "Hello, world" const hello = "Hello, world"
value := struct{ Str string }{hello} var value = struct{ Str string }{hello}
for i := 0; i < len(delimPairs); i += 2 { for i := 0; i < len(delimPairs); i += 2 {
text := ".Str" text := ".Str"
left := delimPairs[i+0] left := delimPairs[i+0]
@ -862,7 +848,7 @@ func TestDelims(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("delim %q text %q parse err %s", left, text, err) t.Fatalf("delim %q text %q parse err %s", left, text, err)
} }
b := new(bytes.Buffer) var b = new(bytes.Buffer)
err = tmpl.Execute(b, value) err = tmpl.Execute(b, value)
if err != nil { if err != nil {
t.Fatalf("delim %q exec err %s", left, err) t.Fatalf("delim %q exec err %s", left, err)
@ -963,7 +949,7 @@ const treeTemplate = `
` `
func TestTree(t *testing.T) { func TestTree(t *testing.T) {
tree := &Tree{ var tree = &Tree{
1, 1,
&Tree{ &Tree{
2, &Tree{ 2, &Tree{
@ -1209,7 +1195,7 @@ var cmpTests = []cmpTest{
func TestComparison(t *testing.T) { func TestComparison(t *testing.T) {
b := new(bytes.Buffer) b := new(bytes.Buffer)
cmpStruct := struct { var cmpStruct = struct {
Uthree, Ufour uint Uthree, Ufour uint
NegOne, Three int NegOne, Three int
Ptr, NilPtr *int Ptr, NilPtr *int

View file

@ -77,6 +77,7 @@ func (t *executer) Execute(p Preparer, wr io.Writer, data interface{}) error {
} }
return tmpl.executeWithState(state, value) return tmpl.executeWithState(state, value)
} }
// Prepare returns a template ready for execution. // Prepare returns a template ready for execution.

View file

@ -85,4 +85,5 @@ Method: {{ .Hello1 "v1" }}
c.Assert(got, qt.Contains, "hello hugo") c.Assert(got, qt.Contains, "hello hugo")
c.Assert(got, qt.Contains, "Map: av") c.Assert(got, qt.Contains, "Map: av")
c.Assert(got, qt.Contains, "Method: v2 v1") c.Assert(got, qt.Contains, "Method: v2 v1")
} }

View file

@ -11,9 +11,8 @@ package template
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"testing"
"github.com/gohugoio/hugo/tpl/internal/go_templates/texttemplate/parse" "github.com/gohugoio/hugo/tpl/internal/go_templates/texttemplate/parse"
"testing"
) )
const ( const (
@ -30,32 +29,22 @@ type multiParseTest struct {
} }
var multiParseTests = []multiParseTest{ var multiParseTests = []multiParseTest{
{ {"empty", "", noError,
"empty", "", noError,
nil, nil,
nil, nil},
}, {"one", `{{define "foo"}} FOO {{end}}`, noError,
{
"one", `{{define "foo"}} FOO {{end}}`, noError,
[]string{"foo"}, []string{"foo"},
[]string{" FOO "}, []string{" FOO "}},
}, {"two", `{{define "foo"}} FOO {{end}}{{define "bar"}} BAR {{end}}`, noError,
{
"two", `{{define "foo"}} FOO {{end}}{{define "bar"}} BAR {{end}}`, noError,
[]string{"foo", "bar"}, []string{"foo", "bar"},
[]string{" FOO ", " BAR "}, []string{" FOO ", " BAR "}},
},
// errors // errors
{ {"missing end", `{{define "foo"}} FOO `, hasError,
"missing end", `{{define "foo"}} FOO `, hasError,
nil, nil,
nil},
{"malformed name", `{{define "foo}} FOO `, hasError,
nil, nil,
}, nil},
{
"malformed name", `{{define "foo}} FOO `, hasError,
nil,
nil,
},
} }
func TestMultiParse(t *testing.T) { func TestMultiParse(t *testing.T) {
@ -411,7 +400,7 @@ func TestIssue19294(t *testing.T) {
// by the contents of "stylesheet", but if the internal map associating // by the contents of "stylesheet", but if the internal map associating
// names with templates is built in the wrong order, the empty block // names with templates is built in the wrong order, the empty block
// looks non-empty and this doesn't happen. // looks non-empty and this doesn't happen.
inlined := map[string]string{ var inlined = map[string]string{
"stylesheet": `{{define "stylesheet"}}stylesheet{{end}}`, "stylesheet": `{{define "stylesheet"}}stylesheet{{end}}`,
"xhtml": `{{block "stylesheet" .}}{{end}}`, "xhtml": `{{block "stylesheet" .}}{{end}}`,
} }

View file

@ -249,6 +249,7 @@ func (a *ActionNode) tree() *Tree {
func (a *ActionNode) Copy() Node { func (a *ActionNode) Copy() Node {
return a.tr.newAction(a.Pos, a.Line, a.Pipe.CopyPipe()) return a.tr.newAction(a.Pos, a.Line, a.Pipe.CopyPipe())
} }
// CommandNode holds a command (a pipeline inside an evaluating action). // CommandNode holds a command (a pipeline inside an evaluating action).

View file

@ -453,7 +453,7 @@ func (t *Tree) parseControl(allowElseIf bool, context string) (pos Pos, line int
var next Node var next Node
list, next = t.itemList() list, next = t.itemList()
switch next.Type() { switch next.Type() {
case nodeEnd: // done case nodeEnd: //done
case nodeElse: case nodeElse:
if allowElseIf { if allowElseIf {
// Special case for "else if". If the "else" is followed immediately by an "if", // Special case for "else if". If the "else" is followed immediately by an "if",

View file

@ -178,134 +178,70 @@ const (
) )
var parseTests = []parseTest{ var parseTests = []parseTest{
{ {"empty", "", noError,
"empty", "", noError, ``},
``, {"comment", "{{/*\n\n\n*/}}", noError,
}, ``},
{ {"spaces", " \t\n", noError,
"comment", "{{/*\n\n\n*/}}", noError, `" \t\n"`},
``, {"text", "some text", noError,
}, `"some text"`},
{ {"emptyAction", "{{}}", hasError,
"spaces", " \t\n", noError, `{{}}`},
`" \t\n"`, {"field", "{{.X}}", noError,
}, `{{.X}}`},
{ {"simple command", "{{printf}}", noError,
"text", "some text", noError, `{{printf}}`},
`"some text"`, {"$ invocation", "{{$}}", noError,
}, "{{$}}"},
{ {"variable invocation", "{{with $x := 3}}{{$x 23}}{{end}}", noError,
"emptyAction", "{{}}", hasError, "{{with $x := 3}}{{$x 23}}{{end}}"},
`{{}}`, {"variable with fields", "{{$.I}}", noError,
}, "{{$.I}}"},
{ {"multi-word command", "{{printf `%d` 23}}", noError,
"field", "{{.X}}", noError, "{{printf `%d` 23}}"},
`{{.X}}`, {"pipeline", "{{.X|.Y}}", noError,
}, `{{.X | .Y}}`},
{ {"pipeline with decl", "{{$x := .X|.Y}}", noError,
"simple command", "{{printf}}", noError, `{{$x := .X | .Y}}`},
`{{printf}}`, {"nested pipeline", "{{.X (.Y .Z) (.A | .B .C) (.E)}}", noError,
}, `{{.X (.Y .Z) (.A | .B .C) (.E)}}`},
{ {"field applied to parentheses", "{{(.Y .Z).Field}}", noError,
"$ invocation", "{{$}}", noError, `{{(.Y .Z).Field}}`},
"{{$}}", {"simple if", "{{if .X}}hello{{end}}", noError,
}, `{{if .X}}"hello"{{end}}`},
{ {"if with else", "{{if .X}}true{{else}}false{{end}}", noError,
"variable invocation", "{{with $x := 3}}{{$x 23}}{{end}}", noError, `{{if .X}}"true"{{else}}"false"{{end}}`},
"{{with $x := 3}}{{$x 23}}{{end}}", {"if with else if", "{{if .X}}true{{else if .Y}}false{{end}}", noError,
}, `{{if .X}}"true"{{else}}{{if .Y}}"false"{{end}}{{end}}`},
{ {"if else chain", "+{{if .X}}X{{else if .Y}}Y{{else if .Z}}Z{{end}}+", noError,
"variable with fields", "{{$.I}}", noError, `"+"{{if .X}}"X"{{else}}{{if .Y}}"Y"{{else}}{{if .Z}}"Z"{{end}}{{end}}{{end}}"+"`},
"{{$.I}}", {"simple range", "{{range .X}}hello{{end}}", noError,
}, `{{range .X}}"hello"{{end}}`},
{ {"chained field range", "{{range .X.Y.Z}}hello{{end}}", noError,
"multi-word command", "{{printf `%d` 23}}", noError, `{{range .X.Y.Z}}"hello"{{end}}`},
"{{printf `%d` 23}}", {"nested range", "{{range .X}}hello{{range .Y}}goodbye{{end}}{{end}}", noError,
}, `{{range .X}}"hello"{{range .Y}}"goodbye"{{end}}{{end}}`},
{ {"range with else", "{{range .X}}true{{else}}false{{end}}", noError,
"pipeline", "{{.X|.Y}}", noError, `{{range .X}}"true"{{else}}"false"{{end}}`},
`{{.X | .Y}}`, {"range over pipeline", "{{range .X|.M}}true{{else}}false{{end}}", noError,
}, `{{range .X | .M}}"true"{{else}}"false"{{end}}`},
{ {"range []int", "{{range .SI}}{{.}}{{end}}", noError,
"pipeline with decl", "{{$x := .X|.Y}}", noError, `{{range .SI}}{{.}}{{end}}`},
`{{$x := .X | .Y}}`, {"range 1 var", "{{range $x := .SI}}{{.}}{{end}}", noError,
}, `{{range $x := .SI}}{{.}}{{end}}`},
{ {"range 2 vars", "{{range $x, $y := .SI}}{{.}}{{end}}", noError,
"nested pipeline", "{{.X (.Y .Z) (.A | .B .C) (.E)}}", noError, `{{range $x, $y := .SI}}{{.}}{{end}}`},
`{{.X (.Y .Z) (.A | .B .C) (.E)}}`, {"constants", "{{range .SI 1 -3.2i true false 'a' nil}}{{end}}", noError,
}, `{{range .SI 1 -3.2i true false 'a' nil}}{{end}}`},
{ {"template", "{{template `x`}}", noError,
"field applied to parentheses", "{{(.Y .Z).Field}}", noError, `{{template "x"}}`},
`{{(.Y .Z).Field}}`, {"template with arg", "{{template `x` .Y}}", noError,
}, `{{template "x" .Y}}`},
{ {"with", "{{with .X}}hello{{end}}", noError,
"simple if", "{{if .X}}hello{{end}}", noError, `{{with .X}}"hello"{{end}}`},
`{{if .X}}"hello"{{end}}`, {"with with else", "{{with .X}}hello{{else}}goodbye{{end}}", noError,
}, `{{with .X}}"hello"{{else}}"goodbye"{{end}}`},
{
"if with else", "{{if .X}}true{{else}}false{{end}}", noError,
`{{if .X}}"true"{{else}}"false"{{end}}`,
},
{
"if with else if", "{{if .X}}true{{else if .Y}}false{{end}}", noError,
`{{if .X}}"true"{{else}}{{if .Y}}"false"{{end}}{{end}}`,
},
{
"if else chain", "+{{if .X}}X{{else if .Y}}Y{{else if .Z}}Z{{end}}+", noError,
`"+"{{if .X}}"X"{{else}}{{if .Y}}"Y"{{else}}{{if .Z}}"Z"{{end}}{{end}}{{end}}"+"`,
},
{
"simple range", "{{range .X}}hello{{end}}", noError,
`{{range .X}}"hello"{{end}}`,
},
{
"chained field range", "{{range .X.Y.Z}}hello{{end}}", noError,
`{{range .X.Y.Z}}"hello"{{end}}`,
},
{
"nested range", "{{range .X}}hello{{range .Y}}goodbye{{end}}{{end}}", noError,
`{{range .X}}"hello"{{range .Y}}"goodbye"{{end}}{{end}}`,
},
{
"range with else", "{{range .X}}true{{else}}false{{end}}", noError,
`{{range .X}}"true"{{else}}"false"{{end}}`,
},
{
"range over pipeline", "{{range .X|.M}}true{{else}}false{{end}}", noError,
`{{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' nil}}{{end}}", noError,
`{{range .SI 1 -3.2i true false 'a' nil}}{{end}}`,
},
{
"template", "{{template `x`}}", noError,
`{{template "x"}}`,
},
{
"template with arg", "{{template `x` .Y}}", noError,
`{{template "x" .Y}}`,
},
{
"with", "{{with .X}}hello{{end}}", noError,
`{{with .X}}"hello"{{end}}`,
},
{
"with with else", "{{with .X}}hello{{else}}goodbye{{end}}", noError,
`{{with .X}}"hello"{{else}}"goodbye"{{end}}`,
},
// Trimming spaces. // Trimming spaces.
{"trim left", "x \r\n\t{{- 3}}", noError, `"x"{{3}}`}, {"trim left", "x \r\n\t{{- 3}}", noError, `"x"{{3}}`},
{"trim right", "{{3 -}}\n\n\ty", noError, `{{3}}"y"`}, {"trim right", "{{3 -}}\n\n\ty", noError, `{{3}}"y"`},
@ -314,10 +250,8 @@ var parseTests = []parseTest{
{"comment trim left", "x \r\n\t{{- /* hi */}}", noError, `"x"`}, {"comment trim left", "x \r\n\t{{- /* hi */}}", noError, `"x"`},
{"comment trim right", "{{/* hi */ -}}\n\n\ty", noError, `"y"`}, {"comment trim right", "{{/* hi */ -}}\n\n\ty", noError, `"y"`},
{"comment trim left and right", "x \r\n\t{{- /* */ -}}\n\n\ty", noError, `"x""y"`}, {"comment trim left and right", "x \r\n\t{{- /* */ -}}\n\n\ty", noError, `"x""y"`},
{ {"block definition", `{{block "foo" .}}hello{{end}}`, noError,
"block definition", `{{block "foo" .}}hello{{end}}`, noError, `{{template "foo" .}}`},
`{{template "foo" .}}`,
},
// Errors. // Errors.
{"unclosed action", "hello{{range", hasError, ""}, {"unclosed action", "hello{{range", hasError, ""},
{"unmatched end", "{{end}}", hasError, ""}, {"unmatched end", "{{end}}", hasError, ""},
@ -467,143 +401,89 @@ func TestErrorContextWithTreeCopy(t *testing.T) {
// All failures, and the result is a string that must appear in the error message. // All failures, and the result is a string that must appear in the error message.
var errorTests = []parseTest{ var errorTests = []parseTest{
// Check line numbers are accurate. // Check line numbers are accurate.
{ {"unclosed1",
"unclosed1",
"line1\n{{", "line1\n{{",
hasError, `unclosed1:2: unexpected unclosed action in command`, hasError, `unclosed1:2: unexpected unclosed action in command`},
}, {"unclosed2",
{
"unclosed2",
"line1\n{{define `x`}}line2\n{{", "line1\n{{define `x`}}line2\n{{",
hasError, `unclosed2:3: unexpected unclosed action in command`, hasError, `unclosed2:3: unexpected unclosed action in command`},
},
// Specific errors. // Specific errors.
{ {"function",
"function",
"{{foo}}", "{{foo}}",
hasError, `function "foo" not defined`, hasError, `function "foo" not defined`},
}, {"comment",
{
"comment",
"{{/*}}", "{{/*}}",
hasError, `unclosed comment`, hasError, `unclosed comment`},
}, {"lparen",
{
"lparen",
"{{.X (1 2 3}}", "{{.X (1 2 3}}",
hasError, `unclosed left paren`, hasError, `unclosed left paren`},
}, {"rparen",
{
"rparen",
"{{.X 1 2 3)}}", "{{.X 1 2 3)}}",
hasError, `unexpected ")"`, hasError, `unexpected ")"`},
}, {"space",
{
"space",
"{{`x`3}}", "{{`x`3}}",
hasError, `in operand`, hasError, `in operand`},
}, {"idchar",
{
"idchar",
"{{a#}}", "{{a#}}",
hasError, `'#'`, hasError, `'#'`},
}, {"charconst",
{
"charconst",
"{{'a}}", "{{'a}}",
hasError, `unterminated character constant`, hasError, `unterminated character constant`},
}, {"stringconst",
{
"stringconst",
`{{"a}}`, `{{"a}}`,
hasError, `unterminated quoted string`, hasError, `unterminated quoted string`},
}, {"rawstringconst",
{
"rawstringconst",
"{{`a}}", "{{`a}}",
hasError, `unterminated raw quoted string`, hasError, `unterminated raw quoted string`},
}, {"number",
{
"number",
"{{0xi}}", "{{0xi}}",
hasError, `number syntax`, hasError, `number syntax`},
}, {"multidefine",
{
"multidefine",
"{{define `a`}}a{{end}}{{define `a`}}b{{end}}", "{{define `a`}}a{{end}}{{define `a`}}b{{end}}",
hasError, `multiple definition of template`, hasError, `multiple definition of template`},
}, {"eof",
{
"eof",
"{{range .X}}", "{{range .X}}",
hasError, `unexpected EOF`, hasError, `unexpected EOF`},
}, {"variable",
{
"variable",
// Declare $x so it's defined, to avoid that error, and then check we don't parse a declaration. // Declare $x so it's defined, to avoid that error, and then check we don't parse a declaration.
"{{$x := 23}}{{with $x.y := 3}}{{$x 23}}{{end}}", "{{$x := 23}}{{with $x.y := 3}}{{$x 23}}{{end}}",
hasError, `unexpected ":="`, hasError, `unexpected ":="`},
}, {"multidecl",
{
"multidecl",
"{{$a,$b,$c := 23}}", "{{$a,$b,$c := 23}}",
hasError, `too many declarations`, hasError, `too many declarations`},
}, {"undefvar",
{
"undefvar",
"{{$a}}", "{{$a}}",
hasError, `undefined variable`, hasError, `undefined variable`},
}, {"wrongdot",
{
"wrongdot",
"{{true.any}}", "{{true.any}}",
hasError, `unexpected . after term`, hasError, `unexpected . after term`},
}, {"wrongpipeline",
{
"wrongpipeline",
"{{12|false}}", "{{12|false}}",
hasError, `non executable command in pipeline`, hasError, `non executable command in pipeline`},
}, {"emptypipeline",
{
"emptypipeline",
`{{ ( ) }}`, `{{ ( ) }}`,
hasError, `missing value for parenthesized pipeline`, hasError, `missing value for parenthesized pipeline`},
}, {"multilinerawstring",
{
"multilinerawstring",
"{{ $v := `\n` }} {{", "{{ $v := `\n` }} {{",
hasError, `multilinerawstring:2: unexpected unclosed action`, hasError, `multilinerawstring:2: unexpected unclosed action`},
}, {"rangeundefvar",
{
"rangeundefvar",
"{{range $k}}{{end}}", "{{range $k}}{{end}}",
hasError, `undefined variable`, hasError, `undefined variable`},
}, {"rangeundefvars",
{
"rangeundefvars",
"{{range $k, $v}}{{end}}", "{{range $k, $v}}{{end}}",
hasError, `undefined variable`, hasError, `undefined variable`},
}, {"rangemissingvalue1",
{
"rangemissingvalue1",
"{{range $k,}}{{end}}", "{{range $k,}}{{end}}",
hasError, `missing value for range`, hasError, `missing value for range`},
}, {"rangemissingvalue2",
{
"rangemissingvalue2",
"{{range $k, $v := }}{{end}}", "{{range $k, $v := }}{{end}}",
hasError, `missing value for range`, hasError, `missing value for range`},
}, {"rangenotvariable1",
{
"rangenotvariable1",
"{{range $k, .}}{{end}}", "{{range $k, .}}{{end}}",
hasError, `range can only initialize variables`, hasError, `range can only initialize variables`},
}, {"rangenotvariable2",
{
"rangenotvariable2",
"{{range $k, 123 := .}}{{end}}", "{{range $k, 123 := .}}{{end}}",
hasError, `range can only initialize variables`, hasError, `range can only initialize variables`},
},
} }
func TestErrors(t *testing.T) { func TestErrors(t *testing.T) {

View file

@ -5,10 +5,9 @@
package template package template
import ( import (
"github.com/gohugoio/hugo/tpl/internal/go_templates/texttemplate/parse"
"reflect" "reflect"
"sync" "sync"
"github.com/gohugoio/hugo/tpl/internal/go_templates/texttemplate/parse"
) )
// common holds the information shared by related templates. // common holds the information shared by related templates.