mirror of
				https://github.com/gohugoio/hugo.git
				synced 2024-05-11 05:54:58 +00:00 
			
		
		
		
	@@ -178,70 +178,134 @@ const (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var parseTests = []parseTest{
 | 
			
		||||
	{"empty", "", noError,
 | 
			
		||||
		``},
 | 
			
		||||
	{"comment", "{{/*\n\n\n*/}}", noError,
 | 
			
		||||
		``},
 | 
			
		||||
	{"spaces", " \t\n", noError,
 | 
			
		||||
		`" \t\n"`},
 | 
			
		||||
	{"text", "some text", noError,
 | 
			
		||||
		`"some text"`},
 | 
			
		||||
	{"emptyAction", "{{}}", hasError,
 | 
			
		||||
		`{{}}`},
 | 
			
		||||
	{"field", "{{.X}}", noError,
 | 
			
		||||
		`{{.X}}`},
 | 
			
		||||
	{"simple command", "{{printf}}", noError,
 | 
			
		||||
		`{{printf}}`},
 | 
			
		||||
	{"$ invocation", "{{$}}", noError,
 | 
			
		||||
		"{{$}}"},
 | 
			
		||||
	{"variable invocation", "{{with $x := 3}}{{$x 23}}{{end}}", noError,
 | 
			
		||||
		"{{with $x := 3}}{{$x 23}}{{end}}"},
 | 
			
		||||
	{"variable with fields", "{{$.I}}", noError,
 | 
			
		||||
		"{{$.I}}"},
 | 
			
		||||
	{"multi-word command", "{{printf `%d` 23}}", noError,
 | 
			
		||||
		"{{printf `%d` 23}}"},
 | 
			
		||||
	{"pipeline", "{{.X|.Y}}", noError,
 | 
			
		||||
		`{{.X | .Y}}`},
 | 
			
		||||
	{"pipeline with decl", "{{$x := .X|.Y}}", noError,
 | 
			
		||||
		`{{$x := .X | .Y}}`},
 | 
			
		||||
	{"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,
 | 
			
		||||
		`{{(.Y .Z).Field}}`},
 | 
			
		||||
	{"simple if", "{{if .X}}hello{{end}}", noError,
 | 
			
		||||
		`{{if .X}}"hello"{{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}}`},
 | 
			
		||||
	{
 | 
			
		||||
		"empty", "", noError,
 | 
			
		||||
		``,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		"comment", "{{/*\n\n\n*/}}", noError,
 | 
			
		||||
		``,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		"spaces", " \t\n", noError,
 | 
			
		||||
		`" \t\n"`,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		"text", "some text", noError,
 | 
			
		||||
		`"some text"`,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		"emptyAction", "{{}}", hasError,
 | 
			
		||||
		`{{}}`,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		"field", "{{.X}}", noError,
 | 
			
		||||
		`{{.X}}`,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		"simple command", "{{printf}}", noError,
 | 
			
		||||
		`{{printf}}`,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		"$ invocation", "{{$}}", noError,
 | 
			
		||||
		"{{$}}",
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		"variable invocation", "{{with $x := 3}}{{$x 23}}{{end}}", noError,
 | 
			
		||||
		"{{with $x := 3}}{{$x 23}}{{end}}",
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		"variable with fields", "{{$.I}}", noError,
 | 
			
		||||
		"{{$.I}}",
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		"multi-word command", "{{printf `%d` 23}}", noError,
 | 
			
		||||
		"{{printf `%d` 23}}",
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		"pipeline", "{{.X|.Y}}", noError,
 | 
			
		||||
		`{{.X | .Y}}`,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		"pipeline with decl", "{{$x := .X|.Y}}", noError,
 | 
			
		||||
		`{{$x := .X | .Y}}`,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		"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,
 | 
			
		||||
		`{{(.Y .Z).Field}}`,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		"simple if", "{{if .X}}hello{{end}}", noError,
 | 
			
		||||
		`{{if .X}}"hello"{{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.
 | 
			
		||||
	{"trim left", "x \r\n\t{{- 3}}", noError, `"x"{{3}}`},
 | 
			
		||||
	{"trim right", "{{3 -}}\n\n\ty", noError, `{{3}}"y"`},
 | 
			
		||||
@@ -250,8 +314,10 @@ var parseTests = []parseTest{
 | 
			
		||||
	{"comment trim left", "x \r\n\t{{- /* hi */}}", noError, `"x"`},
 | 
			
		||||
	{"comment trim right", "{{/* hi */ -}}\n\n\ty", noError, `"y"`},
 | 
			
		||||
	{"comment trim left and right", "x \r\n\t{{- /* */ -}}\n\n\ty", noError, `"x""y"`},
 | 
			
		||||
	{"block definition", `{{block "foo" .}}hello{{end}}`, noError,
 | 
			
		||||
		`{{template "foo" .}}`},
 | 
			
		||||
	{
 | 
			
		||||
		"block definition", `{{block "foo" .}}hello{{end}}`, noError,
 | 
			
		||||
		`{{template "foo" .}}`,
 | 
			
		||||
	},
 | 
			
		||||
	// Errors.
 | 
			
		||||
	{"unclosed action", "hello{{range", hasError, ""},
 | 
			
		||||
	{"unmatched end", "{{end}}", hasError, ""},
 | 
			
		||||
@@ -401,89 +467,143 @@ func TestErrorContextWithTreeCopy(t *testing.T) {
 | 
			
		||||
// All failures, and the result is a string that must appear in the error message.
 | 
			
		||||
var errorTests = []parseTest{
 | 
			
		||||
	// Check line numbers are accurate.
 | 
			
		||||
	{"unclosed1",
 | 
			
		||||
	{
 | 
			
		||||
		"unclosed1",
 | 
			
		||||
		"line1\n{{",
 | 
			
		||||
		hasError, `unclosed1:2: unexpected unclosed action in command`},
 | 
			
		||||
	{"unclosed2",
 | 
			
		||||
		hasError, `unclosed1:2: unexpected unclosed action in command`,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		"unclosed2",
 | 
			
		||||
		"line1\n{{define `x`}}line2\n{{",
 | 
			
		||||
		hasError, `unclosed2:3: unexpected unclosed action in command`},
 | 
			
		||||
		hasError, `unclosed2:3: unexpected unclosed action in command`,
 | 
			
		||||
	},
 | 
			
		||||
	// Specific errors.
 | 
			
		||||
	{"function",
 | 
			
		||||
	{
 | 
			
		||||
		"function",
 | 
			
		||||
		"{{foo}}",
 | 
			
		||||
		hasError, `function "foo" not defined`},
 | 
			
		||||
	{"comment",
 | 
			
		||||
		hasError, `function "foo" not defined`,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		"comment",
 | 
			
		||||
		"{{/*}}",
 | 
			
		||||
		hasError, `unclosed comment`},
 | 
			
		||||
	{"lparen",
 | 
			
		||||
		hasError, `unclosed comment`,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		"lparen",
 | 
			
		||||
		"{{.X (1 2 3}}",
 | 
			
		||||
		hasError, `unclosed left paren`},
 | 
			
		||||
	{"rparen",
 | 
			
		||||
		hasError, `unclosed left paren`,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		"rparen",
 | 
			
		||||
		"{{.X 1 2 3)}}",
 | 
			
		||||
		hasError, `unexpected ")"`},
 | 
			
		||||
	{"space",
 | 
			
		||||
		hasError, `unexpected ")"`,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		"space",
 | 
			
		||||
		"{{`x`3}}",
 | 
			
		||||
		hasError, `in operand`},
 | 
			
		||||
	{"idchar",
 | 
			
		||||
		hasError, `in operand`,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		"idchar",
 | 
			
		||||
		"{{a#}}",
 | 
			
		||||
		hasError, `'#'`},
 | 
			
		||||
	{"charconst",
 | 
			
		||||
		hasError, `'#'`,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		"charconst",
 | 
			
		||||
		"{{'a}}",
 | 
			
		||||
		hasError, `unterminated character constant`},
 | 
			
		||||
	{"stringconst",
 | 
			
		||||
		hasError, `unterminated character constant`,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		"stringconst",
 | 
			
		||||
		`{{"a}}`,
 | 
			
		||||
		hasError, `unterminated quoted string`},
 | 
			
		||||
	{"rawstringconst",
 | 
			
		||||
		hasError, `unterminated quoted string`,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		"rawstringconst",
 | 
			
		||||
		"{{`a}}",
 | 
			
		||||
		hasError, `unterminated raw quoted string`},
 | 
			
		||||
	{"number",
 | 
			
		||||
		hasError, `unterminated raw quoted string`,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		"number",
 | 
			
		||||
		"{{0xi}}",
 | 
			
		||||
		hasError, `number syntax`},
 | 
			
		||||
	{"multidefine",
 | 
			
		||||
		hasError, `number syntax`,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		"multidefine",
 | 
			
		||||
		"{{define `a`}}a{{end}}{{define `a`}}b{{end}}",
 | 
			
		||||
		hasError, `multiple definition of template`},
 | 
			
		||||
	{"eof",
 | 
			
		||||
		hasError, `multiple definition of template`,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		"eof",
 | 
			
		||||
		"{{range .X}}",
 | 
			
		||||
		hasError, `unexpected EOF`},
 | 
			
		||||
	{"variable",
 | 
			
		||||
		hasError, `unexpected EOF`,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		"variable",
 | 
			
		||||
		// 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}}",
 | 
			
		||||
		hasError, `unexpected ":="`},
 | 
			
		||||
	{"multidecl",
 | 
			
		||||
		hasError, `unexpected ":="`,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		"multidecl",
 | 
			
		||||
		"{{$a,$b,$c := 23}}",
 | 
			
		||||
		hasError, `too many declarations`},
 | 
			
		||||
	{"undefvar",
 | 
			
		||||
		hasError, `too many declarations`,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		"undefvar",
 | 
			
		||||
		"{{$a}}",
 | 
			
		||||
		hasError, `undefined variable`},
 | 
			
		||||
	{"wrongdot",
 | 
			
		||||
		hasError, `undefined variable`,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		"wrongdot",
 | 
			
		||||
		"{{true.any}}",
 | 
			
		||||
		hasError, `unexpected . after term`},
 | 
			
		||||
	{"wrongpipeline",
 | 
			
		||||
		hasError, `unexpected . after term`,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		"wrongpipeline",
 | 
			
		||||
		"{{12|false}}",
 | 
			
		||||
		hasError, `non executable command in pipeline`},
 | 
			
		||||
	{"emptypipeline",
 | 
			
		||||
		hasError, `non executable command in pipeline`,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		"emptypipeline",
 | 
			
		||||
		`{{ ( ) }}`,
 | 
			
		||||
		hasError, `missing value for parenthesized pipeline`},
 | 
			
		||||
	{"multilinerawstring",
 | 
			
		||||
		hasError, `missing value for parenthesized pipeline`,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		"multilinerawstring",
 | 
			
		||||
		"{{ $v := `\n` }} {{",
 | 
			
		||||
		hasError, `multilinerawstring:2: unexpected unclosed action`},
 | 
			
		||||
	{"rangeundefvar",
 | 
			
		||||
		hasError, `multilinerawstring:2: unexpected unclosed action`,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		"rangeundefvar",
 | 
			
		||||
		"{{range $k}}{{end}}",
 | 
			
		||||
		hasError, `undefined variable`},
 | 
			
		||||
	{"rangeundefvars",
 | 
			
		||||
		hasError, `undefined variable`,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		"rangeundefvars",
 | 
			
		||||
		"{{range $k, $v}}{{end}}",
 | 
			
		||||
		hasError, `undefined variable`},
 | 
			
		||||
	{"rangemissingvalue1",
 | 
			
		||||
		hasError, `undefined variable`,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		"rangemissingvalue1",
 | 
			
		||||
		"{{range $k,}}{{end}}",
 | 
			
		||||
		hasError, `missing value for range`},
 | 
			
		||||
	{"rangemissingvalue2",
 | 
			
		||||
		hasError, `missing value for range`,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		"rangemissingvalue2",
 | 
			
		||||
		"{{range $k, $v := }}{{end}}",
 | 
			
		||||
		hasError, `missing value for range`},
 | 
			
		||||
	{"rangenotvariable1",
 | 
			
		||||
		hasError, `missing value for range`,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		"rangenotvariable1",
 | 
			
		||||
		"{{range $k, .}}{{end}}",
 | 
			
		||||
		hasError, `range can only initialize variables`},
 | 
			
		||||
	{"rangenotvariable2",
 | 
			
		||||
		hasError, `range can only initialize variables`,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		"rangenotvariable2",
 | 
			
		||||
		"{{range $k, 123 := .}}{{end}}",
 | 
			
		||||
		hasError, `range can only initialize variables`},
 | 
			
		||||
		hasError, `range can only initialize variables`,
 | 
			
		||||
	},
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestErrors(t *testing.T) {
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user