Commit 167a7123 authored by Nodir Turakulov's avatar Nodir Turakulov Committed by Andrew Gerrand

text/template: resolve non-empty interface

Read what a non-empty interface points to.

The deleted lines were added in https://codereview.appspot.com/4810060/,
which attempted to break an infinite loop. That was a long time ago.
If I just delete these lines with current codebase, the test "bug1"
(added in that CL) does not fail.

All new tests fail without this fix.

Fixes #12924

Change-Id: I9370ca44facd6af3019850aa065b936e5a482d37
Reviewed-on: https://go-review.googlesource.com/15809Reviewed-by: 's avatarAndrew Gerrand <adg@golang.org>
parent 65fc379d
...@@ -829,16 +829,11 @@ func (s *state) evalEmptyInterface(dot reflect.Value, n parse.Node) reflect.Valu ...@@ -829,16 +829,11 @@ func (s *state) evalEmptyInterface(dot reflect.Value, n parse.Node) reflect.Valu
} }
// indirect returns the item at the end of indirection, and a bool to indicate if it's nil. // indirect returns the item at the end of indirection, and a bool to indicate if it's nil.
// We indirect through pointers and empty interfaces (only) because
// non-empty interfaces have methods we might need.
func indirect(v reflect.Value) (rv reflect.Value, isNil bool) { func indirect(v reflect.Value) (rv reflect.Value, isNil bool) {
for ; v.Kind() == reflect.Ptr || v.Kind() == reflect.Interface; v = v.Elem() { for ; v.Kind() == reflect.Ptr || v.Kind() == reflect.Interface; v = v.Elem() {
if v.IsNil() { if v.IsNil() {
return v, true return v, true
} }
if v.Kind() == reflect.Interface && v.NumMethod() > 0 {
break
}
} }
return v, false return v, false
} }
......
...@@ -51,8 +51,9 @@ type T struct { ...@@ -51,8 +51,9 @@ type T struct {
Empty2 interface{} Empty2 interface{}
Empty3 interface{} Empty3 interface{}
Empty4 interface{} Empty4 interface{}
// Non-empty interface. // Non-empty interfaces.
NonEmptyInterface I NonEmptyInterface I
NonEmptyInterfacePtS *I
// Stringer. // Stringer.
Str fmt.Stringer Str fmt.Stringer
Err error Err error
...@@ -73,6 +74,12 @@ type T struct { ...@@ -73,6 +74,12 @@ type T struct {
unexported int unexported int
} }
type S []string
func (S) Method0() string {
return "M0"
}
type U struct { type U struct {
V string V string
} }
...@@ -99,6 +106,8 @@ func (w *W) Error() string { ...@@ -99,6 +106,8 @@ func (w *W) Error() string {
return fmt.Sprintf("[%d]", w.k) return fmt.Sprintf("[%d]", w.k)
} }
var siVal = I(S{"a", "b"})
var tVal = &T{ var tVal = &T{
True: true, True: true,
I: 17, I: 17,
...@@ -119,22 +128,23 @@ var tVal = &T{ ...@@ -119,22 +128,23 @@ var tVal = &T{
{"one": 1, "two": 2}, {"one": 1, "two": 2},
{"eleven": 11, "twelve": 12}, {"eleven": 11, "twelve": 12},
}, },
Empty1: 3, Empty1: 3,
Empty2: "empty2", Empty2: "empty2",
Empty3: []int{7, 8}, Empty3: []int{7, 8},
Empty4: &U{"UinEmpty"}, Empty4: &U{"UinEmpty"},
NonEmptyInterface: new(T), NonEmptyInterface: &T{X: "x"},
Str: bytes.NewBuffer([]byte("foozle")), NonEmptyInterfacePtS: &siVal,
Err: errors.New("erroozle"), Str: bytes.NewBuffer([]byte("foozle")),
PI: newInt(23), Err: errors.New("erroozle"),
PS: newString("a string"), PI: newInt(23),
PSI: newIntSlice(21, 22, 23), PS: newString("a string"),
BinaryFunc: func(a, b string) string { return fmt.Sprintf("[%s=%s]", a, b) }, PSI: newIntSlice(21, 22, 23),
VariadicFunc: func(s ...string) string { return fmt.Sprint("<", strings.Join(s, "+"), ">") }, BinaryFunc: func(a, b string) string { return fmt.Sprintf("[%s=%s]", a, b) },
VariadicFuncInt: func(a int, s ...string) string { return fmt.Sprint(a, "=<", strings.Join(s, "+"), ">") }, VariadicFunc: func(s ...string) string { return fmt.Sprint("<", strings.Join(s, "+"), ">") },
NilOKFunc: func(s *int) bool { return s == nil }, VariadicFuncInt: func(a int, s ...string) string { return fmt.Sprint(a, "=<", strings.Join(s, "+"), ">") },
ErrFunc: func() (string, error) { return "bla", nil }, NilOKFunc: func(s *int) bool { return s == nil },
Tmpl: Must(New("x").Parse("test template")), // "x" is the value of .X ErrFunc: func() (string, error) { return "bla", nil },
Tmpl: Must(New("x").Parse("test template")), // "x" is the value of .X
} }
// A non-empty interface. // A non-empty interface.
...@@ -550,6 +560,11 @@ var execTests = []execTest{ ...@@ -550,6 +560,11 @@ var execTests = []execTest{
{"bug16i", "{{\"aaa\"|oneArg}}", "oneArg=aaa", tVal, true}, {"bug16i", "{{\"aaa\"|oneArg}}", "oneArg=aaa", tVal, true},
{"bug16j", "{{1+2i|printf \"%v\"}}", "(1+2i)", tVal, true}, {"bug16j", "{{1+2i|printf \"%v\"}}", "(1+2i)", tVal, true},
{"bug16k", "{{\"aaa\"|printf }}", "aaa", tVal, true}, {"bug16k", "{{\"aaa\"|printf }}", "aaa", tVal, true},
{"bug17a", "{{.NonEmptyInterface.X}}", "x", tVal, true},
{"bug17b", "-{{.NonEmptyInterface.Method1 1234}}-", "-1234-", tVal, true},
{"bug17c", "{{len .NonEmptyInterfacePtS}}", "2", tVal, true},
{"bug17d", "{{index .NonEmptyInterfacePtS 0}}", "a", tVal, true},
{"bug17e", "{{range .NonEmptyInterfacePtS}}-{{.}}-{{end}}", "-a--b-", tVal, true},
} }
func zeroArgs() string { func zeroArgs() string {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment