summaryrefslogtreecommitdiffstats
path: root/tpl/internal
diff options
context:
space:
mode:
authorBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>2020-12-03 13:50:17 +0100
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>2021-02-18 14:11:48 +0100
commitcf3e077da304e6f4d7c22f8296e1382335d055c6 (patch)
treedbcec1614cc8e8de56921f4a316fd316dee07f67 /tpl/internal
parent66beac99c64b5e5fe7bec0bda437ba5858d49a36 (diff)
tpl/internal: Synch Go templates fork with Go 1.16dev
Diffstat (limited to 'tpl/internal')
-rw-r--r--tpl/internal/go_templates/cfg/cfg.go1
-rw-r--r--tpl/internal/go_templates/fmtsort/sort.go2
-rw-r--r--tpl/internal/go_templates/fmtsort/sort_test.go22
-rw-r--r--tpl/internal/go_templates/htmltemplate/clone_test.go16
-rw-r--r--tpl/internal/go_templates/htmltemplate/content_test.go6
-rw-r--r--tpl/internal/go_templates/htmltemplate/escape.go2
-rw-r--r--tpl/internal/go_templates/htmltemplate/escape_test.go2
-rw-r--r--tpl/internal/go_templates/htmltemplate/exec_test.go1711
-rw-r--r--tpl/internal/go_templates/htmltemplate/html.go3
-rw-r--r--tpl/internal/go_templates/htmltemplate/multi_test.go292
-rw-r--r--tpl/internal/go_templates/htmltemplate/template.go58
-rw-r--r--tpl/internal/go_templates/htmltemplate/template_test.go18
-rw-r--r--tpl/internal/go_templates/testenv/testenv.go64
-rw-r--r--tpl/internal/go_templates/texttemplate/doc.go21
-rw-r--r--tpl/internal/go_templates/texttemplate/exec.go1
-rw-r--r--tpl/internal/go_templates/texttemplate/exec_test.go10
-rw-r--r--tpl/internal/go_templates/texttemplate/helper.go59
-rw-r--r--tpl/internal/go_templates/texttemplate/link_test.go66
-rw-r--r--tpl/internal/go_templates/texttemplate/multi_test.go31
-rw-r--r--tpl/internal/go_templates/texttemplate/parse/lex.go88
-rw-r--r--tpl/internal/go_templates/texttemplate/parse/lex_test.go9
-rw-r--r--tpl/internal/go_templates/texttemplate/parse/node.go35
-rw-r--r--tpl/internal/go_templates/texttemplate/parse/parse.go79
-rw-r--r--tpl/internal/go_templates/texttemplate/parse/parse_test.go61
24 files changed, 2520 insertions, 137 deletions
diff --git a/tpl/internal/go_templates/cfg/cfg.go b/tpl/internal/go_templates/cfg/cfg.go
index bdbe9df3e..553021374 100644
--- a/tpl/internal/go_templates/cfg/cfg.go
+++ b/tpl/internal/go_templates/cfg/cfg.go
@@ -58,6 +58,7 @@ const KnownEnv = `
GOSUMDB
GOTMPDIR
GOTOOLDIR
+ GOVCS
GOWASM
GO_EXTLINK_ENABLED
PKG_CONFIG
diff --git a/tpl/internal/go_templates/fmtsort/sort.go b/tpl/internal/go_templates/fmtsort/sort.go
index b01229bd0..7127ba6ac 100644
--- a/tpl/internal/go_templates/fmtsort/sort.go
+++ b/tpl/internal/go_templates/fmtsort/sort.go
@@ -130,7 +130,7 @@ func compare(aVal, bVal reflect.Value) int {
default:
return -1
}
- case reflect.Ptr:
+ case reflect.Ptr, reflect.UnsafePointer:
a, b := aVal.Pointer(), bVal.Pointer()
switch {
case a < b:
diff --git a/tpl/internal/go_templates/fmtsort/sort_test.go b/tpl/internal/go_templates/fmtsort/sort_test.go
index 364c5bf6d..5205a6413 100644
--- a/tpl/internal/go_templates/fmtsort/sort_test.go
+++ b/tpl/internal/go_templates/fmtsort/sort_test.go
@@ -11,6 +11,7 @@ import (
"reflect"
"strings"
"testing"
+ "unsafe"
)
var compareTests = [][]reflect.Value{
@@ -32,6 +33,7 @@ var compareTests = [][]reflect.Value{
ct(reflect.TypeOf(complex128(0+1i)), -1-1i, -1+0i, -1+1i, 0-1i, 0+0i, 0+1i, 1-1i, 1+0i, 1+1i),
ct(reflect.TypeOf(false), false, true),
ct(reflect.TypeOf(&ints[0]), &ints[0], &ints[1], &ints[2]),
+ ct(reflect.TypeOf(unsafe.Pointer(&ints[0])), unsafe.Pointer(&ints[0]), unsafe.Pointer(&ints[1]), unsafe.Pointer(&ints[2])),
ct(reflect.TypeOf(chans[0]), chans[0], chans[1], chans[2]),
ct(reflect.TypeOf(toy{}), toy{0, 1}, toy{0, 2}, toy{1, -1}, toy{1, 1}),
ct(reflect.TypeOf([2]int{}), [2]int{1, 1}, [2]int{1, 2}, [2]int{2, 0}),
@@ -119,6 +121,10 @@ var sortTests = []sortTest{
"PTR0:0 PTR1:1 PTR2:2",
},
{
+ unsafePointerMap(),
+ "UNSAFEPTR0:0 UNSAFEPTR1:1 UNSAFEPTR2:2",
+ },
+ {
map[toy]string{{7, 2}: "72", {7, 1}: "71", {3, 4}: "34"},
"{3 4}:34 {7 1}:71 {7 2}:72",
},
@@ -159,6 +165,14 @@ func sprintKey(key reflect.Value) string {
}
}
return "PTR???"
+ case "unsafe.Pointer":
+ ptr := key.Interface().(unsafe.Pointer)
+ for i := range ints {
+ if ptr == unsafe.Pointer(&ints[i]) {
+ return fmt.Sprintf("UNSAFEPTR%d", i)
+ }
+ }
+ return "UNSAFEPTR???"
case "chan int":
c := key.Interface().(chan int)
for i := range chans {
@@ -185,6 +199,14 @@ func pointerMap() map[*int]string {
return m
}
+func unsafePointerMap() map[unsafe.Pointer]string {
+ m := make(map[unsafe.Pointer]string)
+ for i := 2; i >= 0; i-- {
+ m[unsafe.Pointer(&ints[i])] = fmt.Sprint(i)
+ }
+ return m
+}
+
func chanMap() map[chan int]string {
m := make(map[chan int]string)
for i := 2; i >= 0; i-- {
diff --git a/tpl/internal/go_templates/htmltemplate/clone_test.go b/tpl/internal/go_templates/htmltemplate/clone_test.go
index 2035e7101..7c774e923 100644
--- a/tpl/internal/go_templates/htmltemplate/clone_test.go
+++ b/tpl/internal/go_templates/htmltemplate/clone_test.go
@@ -10,7 +10,7 @@ import (
"bytes"
"errors"
"fmt"
- "io/ioutil"
+ "io"
"strings"
"sync"
"testing"
@@ -18,7 +18,7 @@ import (
"github.com/gohugoio/hugo/tpl/internal/go_templates/texttemplate/parse"
)
-func TestAddParseTree(t *testing.T) {
+func TestAddParseTreeHTML(t *testing.T) {
root := Must(New("root").Parse(`{{define "a"}} {{.}} {{template "b"}} {{.}} "></a>{{end}}`))
tree, err := parse.Parse("t", `{{define "b"}}<a href="{{end}}`, "", "", nil, nil)
if err != nil {
@@ -174,7 +174,7 @@ func TestCloneThenParse(t *testing.T) {
t.Error("adding a template to a clone added it to the original")
}
// double check that the embedded template isn't available in the original
- err := t0.ExecuteTemplate(ioutil.Discard, "a", nil)
+ err := t0.ExecuteTemplate(io.Discard, "a", nil)
if err == nil {
t.Error("expected 'no such template' error")
}
@@ -188,13 +188,13 @@ func TestFuncMapWorksAfterClone(t *testing.T) {
// get the expected error output (no clone)
uncloned := Must(New("").Funcs(funcs).Parse("{{customFunc}}"))
- wantErr := uncloned.Execute(ioutil.Discard, nil)
+ wantErr := uncloned.Execute(io.Discard, nil)
// toClone must be the same as uncloned. It has to be recreated from scratch,
// since cloning cannot occur after execution.
toClone := Must(New("").Funcs(funcs).Parse("{{customFunc}}"))
cloned := Must(toClone.Clone())
- gotErr := cloned.Execute(ioutil.Discard, nil)
+ gotErr := cloned.Execute(io.Discard, nil)
if wantErr.Error() != gotErr.Error() {
t.Errorf("clone error message mismatch want %q got %q", wantErr, gotErr)
@@ -216,7 +216,7 @@ func TestTemplateCloneExecuteRace(t *testing.T) {
go func() {
defer wg.Done()
for i := 0; i < 100; i++ {
- if err := tmpl.Execute(ioutil.Discard, "data"); err != nil {
+ if err := tmpl.Execute(io.Discard, "data"); err != nil {
panic(err)
}
}
@@ -240,7 +240,7 @@ func TestCloneGrowth(t *testing.T) {
tmpl = Must(tmpl.Clone())
Must(tmpl.Parse(`{{define "B"}}Text{{end}}`))
for i := 0; i < 10; i++ {
- tmpl.Execute(ioutil.Discard, nil)
+ tmpl.Execute(io.Discard, nil)
}
if len(tmpl.DefinedTemplates()) > 200 {
t.Fatalf("too many templates: %v", len(tmpl.DefinedTemplates()))
@@ -260,7 +260,7 @@ func TestCloneRedefinedName(t *testing.T) {
for i := 0; i < 2; i++ {
t2 := Must(t1.Clone())
t2 = Must(t2.New(fmt.Sprintf("%d", i)).Parse(page))
- err := t2.Execute(ioutil.Discard, nil)
+ err := t2.Execute(io.Discard, nil)
if err != nil {
t.Fatal(err)
}
diff --git a/tpl/internal/go_templates/htmltemplate/content_test.go b/tpl/internal/go_templates/htmltemplate/content_test.go
index b5de701d3..909a24bc0 100644
--- a/tpl/internal/go_templates/htmltemplate/content_test.go
+++ b/tpl/internal/go_templates/htmltemplate/content_test.go
@@ -404,11 +404,11 @@ func TestTypedContent(t *testing.T) {
}
// Test that we print using the String method. Was issue 3073.
-type stringer struct {
+type myStringer struct {
v int
}
-func (s *stringer) String() string {
+func (s *myStringer) String() string {
return fmt.Sprintf("string=%d", s.v)
}
@@ -421,7 +421,7 @@ func (s *errorer) Error() string {
}
func TestStringer(t *testing.T) {
- s := &stringer{3}
+ s := &myStringer{3}
b := new(bytes.Buffer)
tmpl := Must(New("x").Parse("{{.}}"))
if err := tmpl.Execute(b, s); err != nil {
diff --git a/tpl/internal/go_templates/htmltemplate/escape.go b/tpl/internal/go_templates/htmltemplate/escape.go
index 53b817595..5f4c92a17 100644
--- a/tpl/internal/go_templates/htmltemplate/escape.go
+++ b/tpl/internal/go_templates/htmltemplate/escape.go
@@ -125,6 +125,8 @@ func (e *escaper) escape(c context, n parse.Node) context {
switch n := n.(type) {
case *parse.ActionNode:
return e.escapeAction(c, n)
+ case *parse.CommentNode:
+ return c
case *parse.IfNode:
return e.escapeBranch(c, &n.BranchNode, "if")
case *parse.ListNode:
diff --git a/tpl/internal/go_templates/htmltemplate/escape_test.go b/tpl/internal/go_templates/htmltemplate/escape_test.go
index 075db4e13..c569a9391 100644
--- a/tpl/internal/go_templates/htmltemplate/escape_test.go
+++ b/tpl/internal/go_templates/htmltemplate/escape_test.go
@@ -1825,7 +1825,7 @@ func TestIndirectPrint(t *testing.T) {
}
// This is a test for issue 3272.
-func TestEmptyTemplate(t *testing.T) {
+func TestEmptyTemplateHTML(t *testing.T) {
page := Must(New("page").ParseFiles(os.DevNull))
if err := page.ExecuteTemplate(os.Stdout, "page", "nothing"); err == nil {
t.Fatal("expected error")
diff --git a/tpl/internal/go_templates/htmltemplate/exec_test.go b/tpl/internal/go_templates/htmltemplate/exec_test.go
new file mode 100644
index 000000000..7af582958
--- /dev/null
+++ b/tpl/internal/go_templates/htmltemplate/exec_test.go
@@ -0,0 +1,1711 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Tests for template execution, copied from text/template.
+
+// +build go1.13,!windows
+
+package template
+
+import (
+ "bytes"
+ "errors"
+ "flag"
+ "fmt"
+ "io"
+ "reflect"
+ "strings"
+ "testing"
+
+ template "github.com/gohugoio/hugo/tpl/internal/go_templates/texttemplate"
+)
+
+var debug = flag.Bool("debug", false, "show the errors produced by the tests")
+
+// T has lots of interesting pieces to use to test execution.
+type T struct {
+ // Basics
+ True bool
+ I int
+ U16 uint16
+ X, S string
+ FloatZero float64
+ ComplexZero complex128
+ // Nested structs.
+ U *U
+ // Struct with String method.
+ V0 V
+ V1, V2 *V
+ // Struct with Error method.
+ W0 W
+ W1, W2 *W
+ // Slices
+ SI []int
+ SICap []int
+ SIEmpty []int
+ SB []bool
+ // Arrays
+ AI [3]int
+ // Maps
+ MSI map[string]int
+ MSIone map[string]int // one element, for deterministic output
+ MSIEmpty map[string]int
+ MXI map[interface{}]int
+ MII map[int]int
+ MI32S map[int32]string
+ MI64S map[int64]string
+ MUI32S map[uint32]string
+ MUI64S map[uint64]string
+ MI8S map[int8]string
+ MUI8S map[uint8]string
+ SMSI []map[string]int
+ // Empty interfaces; used to see if we can dig inside one.
+ Empty0 interface{} // nil
+ Empty1 interface{}
+ Empty2 interface{}
+ Empty3 interface{}
+ Empty4 interface{}
+ // Non-empty interfaces.
+ NonEmptyInterface I
+ NonEmptyInterfacePtS *I
+ NonEmptyInterfaceNil I
+ NonEmptyInterfaceTypedNil I
+ // Stringer.
+ Str fmt.Stringer
+ Err error
+ // Pointers
+ PI *int
+ PS *string
+ PSI *[]int
+ NIL *int
+ // Function (not method)
+ BinaryFunc func(string, string) string
+ VariadicFunc func(...string) string
+ VariadicFuncInt func(int, ...string) string
+ NilOKFunc func(*int) bool
+ ErrFunc func() (string, error)
+ PanicFunc func() string
+ // Template to test evaluation of templates.
+ Tmpl *Template
+ // Unexported field; cannot be accessed by template.
+ unexported int
+}
+
+type S []string
+
+func (S) Method0() string {
+ return "M0"
+}
+
+type U struct {
+ V string
+}
+
+type V struct {
+ j int
+}
+
+func (v *V) String() string {
+ if v == nil {
+ return "nilV"
+ }
+ return fmt.Sprintf("<%d>", v.j)
+}
+
+type W struct {
+ k int
+}
+
+func (w *W) Error() string {
+ if w == nil {
+ return "nilW"
+ }
+ return fmt.Sprintf("[%d]", w.k)
+}
+
+var siVal = I(S{"a", "b"})
+
+var tVal = &T{
+ True: true,
+ I: 17,
+ U16: 16,
+ X: "x",
+ S: "xyz",
+ U: &U{"v"},
+ V0: V{6666},
+ V1: &V{7777}, // leave V2 as nil
+ W0: W{888},
+ W1: &W{999}, // leave W2 as nil
+ SI: []int{3, 4, 5},
+ SICap: make([]int, 5, 10),
+ AI: [3]int{3, 4, 5},
+ SB: []bool{true, false},
+ MSI: map[string]int{"one": 1, "two": 2, "three": 3},
+ MSIone: map[string]int{"one": 1},
+ MXI: map[interface{}]int{"one": 1},
+ MII: map[int]int{1: 1},
+ MI32S: map[int32]string{1: "one", 2: "two"},
+ MI64S: map[int64]string{2: "i642", 3: "i643"},
+ MUI32S: map[uint32]string{2: "u322", 3: "u323"},
+ MUI64S: map[uint64]string{2: "ui642", 3: "ui643"},
+ MI8S: map[int8]string{2: "i82", 3: "i83"},
+ MUI8S: map[uint8]string{2: "u82", 3: "u83"},
+ SMSI: []map[string]int{
+ {"one": 1, "two": 2},
+ {"eleven": 11, "twelve": 12},
+ },
+ Empty1: 3,
+ Empty2: "empty2",
+ Empty3: []int{7, 8},
+ Empty4: &U{"UinEmpty"},
+ NonEmptyInterface: &T{X: "x"},
+ NonEmptyInterfacePtS: &siVal,
+ NonEmptyInterfaceTypedNil: (*T)(nil),
+ Str: bytes.NewBuffer([]byte("foozle")),
+ Err: errors.New("erroozle"),
+ PI: newInt(23),
+ PS: newString("a string"),
+ PSI: newIntSlice(21, 22, 23),
+ BinaryFunc: func(a, b string) string { return fmt.Sprintf("[%s=%s]", a, b) },
+ VariadicFunc: func(s ...string) string { return fmt.Sprint("<", strings.Join(s, "+"), ">") },
+ VariadicFuncInt: func(a int, s ...string) string { return fmt.Sprint(a, "=<", strings.Join(s, "+"), ">") },
+ NilOKFunc: func(s *int) bool { return s == nil },
+ ErrFunc: func() (string, error) { return "bla", nil },
+ PanicFunc: func() string { panic("test panic") },
+ Tmpl: Must(New("x").Parse("test template")), // "x" is the value of .X
+}
+
+var tSliceOfNil = []*T{nil}
+
+// A non-empty interface.
+type I interface {
+ Method0() string
+}
+
+var iVal I = tVal
+
+// Helpers for creation.
+func newInt(n int) *int {
+ return &n
+}
+
+func newString(s string) *string {
+ return &s
+}
+
+func newIntSlice(n ...int) *[]int {
+ p := new([]int)
+ *p = make([]int, len(n))
+ copy(*p, n)
+ return p
+}
+
+// Simple methods with and without arguments.
+func (t *T) Method0() string {
+ return "M0"
+}
+
+func (t *T) Method1(a int) int {
+ return a
+}
+
+func (t *T) Method2(a uint16, b string) string {
+ return fmt.Sprintf("Method2: %d %s", a, b)
+}
+
+func (t *T) Method3(v interface{}) string {
+ return fmt.Sprintf("Method3: %v", v)
+}
+
+func (t *T) Copy() *T {
+ n := new(T)
+ *n = *t
+ return n
+}
+
+func (t *T) MAdd(a int, b []int) []int {
+ v := make([]int, len(b))
+ for i, x := range b {
+ v[i] = x + a
+ }
+ return v
+}
+
+var myError = errors.New("my error")
+
+// MyError returns a value and an error according to its argument.
+func (t *T) MyError(error bool) (bool, error) {
+ if error {
+ return true, myError
+ }
+ return false, nil
+}
+
+// A few methods to test chaining.
+func (t *T) GetU() *U {
+ return t.U
+}
+
+func (u *U) TrueFalse(b bool) string {
+ if b {
+ return "true"
+ }
+ return ""
+}
+
+func typeOf(arg interface{}) string {
+ return fmt.Sprintf("%T", arg)
+}
+
+type execTest struct {
+ name string
+ input string
+ output string
+ data interface{}
+ ok bool
+}
+
+// bigInt and bigUint are hex string representing numbers either side
+// of the max int boundary.
+// We do it this way so the test doesn't depend on ints being 32 bits.
+var (
+ bigInt = fmt.Sprintf("0x%x", int(1<<uint(reflect.TypeOf(0).Bits()-1)-1))
+ bigUint = fmt.Sprintf("0x%x", uint(1<<uint(reflect.TypeOf(0).Bits()-1)))
+)
+
+var execTests = []execTest{
+ // Trivial cases.
+ {"empty", "", "", nil, true},
+ {"text", "some text", "some text", nil, true},
+ {"nil action", "{{nil}}", "", nil, false},
+
+ // Ideal constants.
+ {"ideal int", "{{typeOf 3}}", "int", 0, true},
+ {"ideal float", "{{typeOf 1.0}}", "float64", 0, true},
+ {"ideal exp float", "{{typeOf 1e1}}", "float64", 0, true},
+ {"ideal complex", "{{typeOf 1i}}", "complex128", 0, true},
+ {"ideal int", "{{typeOf " + bigInt + "}}", "int", 0, true},
+ {"ideal too big", "{{typeOf " + bigUint + "}}", "", 0, false},
+ {"ideal nil without type", "{{nil}}", "", 0, false},
+
+ // Fields of structs.
+ {".X", "-{{.X}}-", "-x-", tVal, true},
+ {".U.V", "-{{.U.V}}-", "-v-", tVal, true},
+ {".unexported", "{{.unexported}}", "", tVal, false},
+
+ // Fields on maps.
+ {"map .one", "{{.MSI.one}}", "1", tVal, true},
+ {"map .two", "{{.MSI.two}}", "2", tVal, true},
+ {"map .NO", "{{.MSI.NO}}", "", tVal, true}, // NOTE: <no value> in text/template
+ {"map .one interface", "{{.MXI.one}}", "1", tVal, true},
+ {"map .WRONG args", "{{.MSI.one 1}}", "", tVal, false},
+ {"map .WRONG type", "{{.MII.one}}", "", tVal, false},
+
+ // Dots of all kinds to test basic evaluation.
+ {"dot int", "<{{.}}>", "&lt;13>", 13, true},
+ {"dot uint", "<{{.}}>", "&lt;14>", uint(14), true},
+ {"dot float", "<{{.}}>", "&lt;15.1>", 15.1, true},
+ {"dot bool", "<{{.}}>", "&lt;true>", true, true},
+ {"dot complex", "<{{.}}>", "&lt;(16.2-17i)>", 16.2 - 17i, true},
+ {"dot string", "<{{.}}>", "&lt;hello>", "hello", true},
+ {"dot slice", "<{{.}}>", "&lt;[-1 -2 -3]>", []int{-1, -2, -3}, true},
+ {"dot map", "<{{.}}>", "&lt;map[two:22]>", map[string]int{"two": 22}, true},
+ {"dot struct", "<{{.}}>", "&lt;{7 seven}>", struct {
+ a int
+ b string
+ }{7, "seven"}, true},
+
+ // Variables.
+ {"$ int", "{{$}}", "123", 123, true},
+ {"$.I", "{{$.I}}", "17", tVal, true},
+ {"$.U.V", "{{$.U.V}}", "v", tVal, true},
+ {"declare in action", "{{$x := $.U.V}}{{$x}}", "v", tVal, true},
+ {"simple assignment", "{{$x := 2}}{{$x = 3}}{{$x}}", "3", tVal, true},
+ {"nested assignment",
+ "{{$x := 2}}{{if true}}{{$x = 3}}{{end}}{{$x}}",
+ "3", tVal, true},
+ {"nested assignment changes the last declaration",
+ "{{$x := 1}}{{if true}}{{$x := 2}}{{if true}}{{$x = 3}}{{end}}{{end}}{{$x}}",
+ "1", tVal, true},
+
+ // Type with String method.
+ {"V{6666}.String()", "-{{.V0}}-", "-{6666}-", tVal, true}, // NOTE: -<6666>- in text/template
+ {"&V{7777}.String()", "-{{.V1}}-", "-&lt;7777&gt;-", tVal, true},
+ {"(*V)(nil).String()", "-{{.V2}}-", "-nilV-", tVal, true},
+
+ // Type with Error method.
+ {"W{888}.Error()", "-{{.W0}}-", "-{888}-", tVal, true}, // NOTE: -[888] in text/template
+ {"&W{999}.Error()", "-{{.W1}}-", "-[999]-", tVal, true},
+ {"(*W)(nil).Error()", "-{{.W2}}-", "-nilW-", tVal, true},
+
+ // Pointers.
+ {"*int", "{{.PI}}", "23", tVal, true},
+ {"*string", "{{.PS}}", "a string", tVal, true},
+ {"*[]int", "{{.PSI}}", "[21 22 23]", tVal, true},
+ {"*[]int[1]", "{{index .PSI 1}}", "22", tVal, true},
+ {"NIL", "{{.NIL}}", "&lt;nil&gt;", tVal, true},
+
+ // Empty interfaces holding values.
+ {"empty nil", "{{.Empty0}}", "", tVal, true}, // NOTE: <no value> in text/template
+ {"empty with int", "{{.Empty1}}", "3", tVal, true},
+ {"empty with string", "{{.Empty2}}", "empty2", tVal, true},
+ {"empty with slice", "{{.Empty3}}", "[7 8]", tVal, true},
+ {"empty with struct", "{{.Empty4}}", "{UinEmpty}", tVal, true},
+ {"empty with struct, field", "{{.Empty4.V}}", "UinEmpty", tVal, true},
+
+ // Edge cases with <no value> with an interface value
+ {"field on interface", "{{.foo}}", "", nil, true}, // NOTE: <no value> in text/template
+ {"field on parenthesized interface", "{{(.).foo}}", "", nil, true}, // NOTE: <no value> in text/template
+
+ // Issue 31810: Parenthesized first element of pipeline with arguments.
+ // See also TestIssue31810.
+ {"unparenthesized non-function", "{{1 2}}", "", nil, false},
+ {"parenthesized non-function", "{{(1) 2}}", "", nil, false},
+ {"parenthesized non-function with no args", "{{(1)}}", "1", nil, true}, // This is fine.
+
+ // Method calls.
+ {".Method0", "-{{.Method0}}-", "-M0-", tVal, true},
+ {".Method1(1234)", "-{{.Method1 1234}}-", "-1234-", tVal, true},
+ {".Method1(.I)", "-{{.Method1 .I}}-", "-17-", tVal, true},
+ {".Method2(3, .X)", "-{{.Method2 3 .X}}-", "-Method2: 3 x-", tVal, true},
+ {".Method2(.U16, `str`)", "-{{.Method2 .U16 `str`}}-", "-Method2: 16 str-", tVal, true},
+ {".Method2(.U16, $x)", "{{if $x := .X}}-{{.Method2 .U16 $x}}{{end}}-", "-Method2: 16 x-", tVal, true},
+ {".Method3(nil constant)", "-{{.Method3 nil}}-", "-Method3: &lt;nil&gt;-", tVal, true},
+ {".Method3(nil value)", "-{{.Method3 .MXI.unset}}-", "-Method3: &lt;nil&gt;-", tVal, true},
+ {"method on var", "{{if $x := .}}-{{$x.Method2 .U16 $x.X}}{{end}}-", "-Method2: 16 x-", tVal, true},
+ {"method on chained var",
+ "{{range .MSIone}}{{if $.U.TrueFalse $.True}}{{$.U.TrueFalse $.True}}{{else}}WRONG{{end}}{{end}}",
+ "true", tVal, true},
+ {"chained method",
+ "{{range .MSIone}}{{if $.GetU.TrueFalse $.True}}{{$.U.TrueFalse $.True}}{{else}}WRONG{{end}}{{end}}",
+ "true", tVal, true},
+ {"chained method on variable",
+ "{{with $x := .}}{{with .SI}}{{$.GetU.TrueFalse $.True}}{{end}}{{end}}",
+ "true", tVal, true},
+ {".NilOKFunc not nil", "{{call .NilOKFunc .PI}}", "false", 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 typed nil interface value", "{{.NonEmptyInterfaceTypedNil.Method0}}", "M0", tVal, true},
+
+ // Function call builtin.
+ {".BinaryFunc", "{{call .BinaryFunc `1` `2`}}", "[1=2]", tVal, true},
+ {".VariadicFunc0", "{{call .VariadicFunc}}", "&lt;&gt;", tVal, true},
+ {".VariadicFunc2", "{{call .VariadicFunc `he` `llo`}}", "&lt;he&#43;llo&gt;", tVal, true},
+ {".VariadicFuncInt", "{{call .VariadicFuncInt 33 `he` `llo`}}", "33=&lt;he&#43;llo&gt;", tVal, true},
+ {"if .BinaryFunc call", "{{ if .BinaryFunc}}{{call .BinaryFunc `1` `2`}}{{end}}", "[1=2]", tVal, true},
+ {"if not .BinaryFunc call", "{{ if not .BinaryFunc}}{{call .BinaryFunc `1` `2`}}{{else}}No{{end}}", "No", tVal, true},
+ {"Interface Call", `{{stringer .S}}`, "foozle", map[string]interface{}{"S": bytes.NewBufferString("foozle")}, true},
+ {".ErrFunc", "{{call .ErrFunc}}", "bla", tVal, true},
+ {"call nil", "{{call nil}}", "", tVal, false},
+
+ // Erroneous function calls (check args).
+ {".BinaryFuncTooFew", "{{call .BinaryFunc `1`}}", "", tVal, false},
+ {".BinaryFuncTooMany", "{{call .BinaryFunc `1` `2` `3`}}", "", tVal, false},
+ {".BinaryFuncBad0", "{{call .BinaryFunc 1 3}}", "", tVal, false},
+ {".BinaryFuncBad1", "{{call .BinaryFunc `1` 3}}", "", tVal, false},
+ {".VariadicFuncBad0", "{{call .VariadicFunc 3}}", "", tVal, false},
+ {".VariadicFuncIntBad0", "{{call .VariadicFuncInt}}", "", tVal, false},
+ {".VariadicFuncIntBad`", "{{call .VariadicFuncInt `x`}}", "", tVal, false},
+ {".VariadicFuncNilBad", "{{call .VariadicFunc nil}}", "", tVal, false},
+
+ // Pipelines.
+ {"pipeline", "-{{.Method0 | .Method2 .U16}}-", "-Method2: 16 M0-", tVal, true},
+ {"pipeline func", "-{{call .VariadicFunc `llo` | call .VariadicFunc `he` }}-", "-&lt;he&#43;&lt;llo&gt;&gt;-", tVal, true},
+
+ // Nil values aren't missing arguments.
+ {"nil pipeline", "{{ .Empty0 | call .NilOKFunc }}", "true", tVal, true},
+ {"nil call arg", "{{ call .NilOKFunc .Empty0 }}", "true", tVal, true},
+ {"bad nil pipeline", "{{ .Empty0 | .VariadicFunc }}", "", tVal, false},
+
+ // Parenthesized expressions
+ {"parens in pipeline", "{{printf `%d %d %d` (1) (2 | add 3) (add 4 (add 5 6))}}", "1 5 15", tVal, true},
+
+ // Parenthesized expressions with field accesses
+ {"parens: $ in paren", "{{($).X}}", "x", tVal, true},
+ {"parens: $.GetU in paren", "{{($.GetU).V}}", "v", tVal, true},
+ {"parens: $ in paren in pipe", "{{($ | echo).X}}", "x", tVal, true},
+ {"parens: spaces and args", `{{(makemap "up" "down" "left" "right").left}}`, "right", tVal, true},