t.Fatalf("%s: Execute: %v", text, err)
}
if buf.String() != "<1>" {
- t.Fatalf("%s: template output = %q, want %q", text, buf, "<1>")
+ t.Fatalf("%s: template output = %q, want %q", text, &buf, "<1>")
+ }
+ }
+}
+
+func TestInterfaceValues(t *testing.T) {
+ // golang.org/issue/17714.
+ // Before index worked on reflect.Values, interface values
+ // were always implicitly promoted to the underlying value,
+ // except that nil interfaces were promoted to the zero reflect.Value.
+ // Eliminating a round trip to interface{} and back to reflect.Value
+ // eliminated this promotion, breaking these cases.
+ tests := []struct {
+ text string
+ out string
+ }{
+ {`{{index .Nil 1}}`, "ERROR: index of untyped nil"},
+ {`{{index .Slice 2}}`, "2"},
+ {`{{index .Slice .Two}}`, "2"},
+ {`{{call .Nil 1}}`, "ERROR: call of nil"},
+ {`{{call .PlusOne 1}}`, "2"},
+ {`{{call .PlusOne .One}}`, "2"},
+ {`{{and (index .Slice 0) true}}`, "0"},
+ {`{{and .Zero true}}`, "0"},
+ {`{{and (index .Slice 1) false}}`, "false"},
+ {`{{and .One false}}`, "false"},
+ {`{{or (index .Slice 0) false}}`, "false"},
+ {`{{or .Zero false}}`, "false"},
+ {`{{or (index .Slice 1) true}}`, "1"},
+ {`{{or .One true}}`, "1"},
+ {`{{not (index .Slice 0)}}`, "true"},
+ {`{{not .Zero}}`, "true"},
+ {`{{not (index .Slice 1)}}`, "false"},
+ {`{{not .One}}`, "false"},
+ {`{{eq (index .Slice 0) .Zero}}`, "true"},
+ {`{{eq (index .Slice 1) .One}}`, "true"},
+ {`{{ne (index .Slice 0) .Zero}}`, "false"},
+ {`{{ne (index .Slice 1) .One}}`, "false"},
+ {`{{ge (index .Slice 0) .One}}`, "false"},
+ {`{{ge (index .Slice 1) .Zero}}`, "true"},
+ {`{{gt (index .Slice 0) .One}}`, "false"},
+ {`{{gt (index .Slice 1) .Zero}}`, "true"},
+ {`{{le (index .Slice 0) .One}}`, "true"},
+ {`{{le (index .Slice 1) .Zero}}`, "false"},
+ {`{{lt (index .Slice 0) .One}}`, "true"},
+ {`{{lt (index .Slice 1) .Zero}}`, "false"},
+ }
+
+ for _, tt := range tests {
+ tmpl := Must(New("tmpl").Parse(tt.text))
+ var buf bytes.Buffer
+ err := tmpl.Execute(&buf, map[string]interface{}{
+ "PlusOne": func(n int) int {
+ return n + 1
+ },
+ "Slice": []int{0, 1, 2, 3},
+ "One": 1,
+ "Two": 2,
+ "Nil": nil,
+ "Zero": 0,
+ })
+ if strings.HasPrefix(tt.out, "ERROR:") {
+ e := strings.TrimSpace(strings.TrimPrefix(tt.out, "ERROR:"))
+ if err == nil || !strings.Contains(err.Error(), e) {
+ t.Errorf("%s: Execute: %v, want error %q", tt.text, err, e)
+ }
+ continue
+ }
+ if err != nil {
+ t.Errorf("%s: Execute: %v", tt.text, err)
+ continue
+ }
+ if buf.String() != tt.out {
+ t.Errorf("%s: template output = %q, want %q", tt.text, &buf, tt.out)
}
}
}
// arguments. Thus "index x 1 2 3" is, in Go syntax, x[1][2][3]. Each
// indexed item must be a map, slice, or array.
func index(item reflect.Value, indices ...reflect.Value) (reflect.Value, error) {
- v := item
+ v := indirectInterface(item)
if !v.IsValid() {
return reflect.Value{}, fmt.Errorf("index of untyped nil")
}
- for _, index := range indices {
+ for _, i := range indices {
+ index := indirectInterface(i)
var isNil bool
if v, isNil = indirect(v); isNil {
return reflect.Value{}, fmt.Errorf("index of nil pointer")
// call returns the result of evaluating the first argument as a function.
// The function must return 1 result, or 2 results, the second of which is an error.
func call(fn reflect.Value, args ...reflect.Value) (reflect.Value, error) {
- v := fn
+ v := indirectInterface(fn)
if !v.IsValid() {
return reflect.Value{}, fmt.Errorf("call of nil")
}
}
}
argv := make([]reflect.Value, len(args))
- for i, value := range args {
+ for i, arg := range args {
+ value := indirectInterface(arg)
// Compute the expected type. Clumsy because of variadics.
var argType reflect.Type
if !typ.IsVariadic() || i < numIn-1 {
// Boolean logic.
func truth(arg reflect.Value) bool {
- t, _ := isTrue(arg)
+ t, _ := isTrue(indirectInterface(arg))
return t
}
// eq evaluates the comparison a == b || a == c || ...
func eq(arg1 reflect.Value, arg2 ...reflect.Value) (bool, error) {
- v1 := arg1
+ v1 := indirectInterface(arg1)
k1, err := basicKind(v1)
if err != nil {
return false, err
if len(arg2) == 0 {
return false, errNoComparison
}
- for _, v2 := range arg2 {
+ for _, arg := range arg2 {
+ v2 := indirectInterface(arg)
k2, err := basicKind(v2)
if err != nil {
return false, err
}
// lt evaluates the comparison a < b.
-func lt(v1, v2 reflect.Value) (bool, error) {
+func lt(arg1, arg2 reflect.Value) (bool, error) {
+ v1 := indirectInterface(arg1)
k1, err := basicKind(v1)
if err != nil {
return false, err
}
+ v2 := indirectInterface(arg2)
k2, err := basicKind(v2)
if err != nil {
return false, err