Commit 1adda460 authored by Rob Pike's avatar Rob Pike

exp/template: do not take address of interface variable to find methods.

Also allow struct values as "with" targets.

R=golang-dev, dsymonds
CC=golang-dev
https://golang.org/cl/4809086
parent 7506ee75
...@@ -179,6 +179,8 @@ func isTrue(val reflect.Value) (truth, ok bool) { ...@@ -179,6 +179,8 @@ func isTrue(val reflect.Value) (truth, ok bool) {
truth = val.Float() != 0 truth = val.Float() != 0
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
truth = val.Uint() != 0 truth = val.Uint() != 0
case reflect.Struct:
truth = true // Struct values are always true.
default: default:
return return
} }
...@@ -377,10 +379,10 @@ func (s *state) evalField(dot reflect.Value, fieldName string, args []parse.Node ...@@ -377,10 +379,10 @@ func (s *state) evalField(dot reflect.Value, fieldName string, args []parse.Node
} }
typ := receiver.Type() typ := receiver.Type()
receiver, _ = indirect(receiver) receiver, _ = indirect(receiver)
// Need to get to a value of type *T to guarantee we see all // Unless it's an interface, need to get to a value of type *T to guarantee
// methods of T and *T. // we see all methods of T and *T.
ptr := receiver ptr := receiver
if ptr.CanAddr() { if ptr.Kind() != reflect.Interface && ptr.CanAddr() {
ptr = ptr.Addr() ptr = ptr.Addr()
} }
if method, ok := methodByName(ptr, fieldName); ok { if method, ok := methodByName(ptr, fieldName); ok {
......
...@@ -43,6 +43,8 @@ type T struct { ...@@ -43,6 +43,8 @@ type T struct {
Empty2 interface{} Empty2 interface{}
Empty3 interface{} Empty3 interface{}
Empty4 interface{} Empty4 interface{}
// Non-empty interface.
NonEmptyInterface I
// Pointers // Pointers
PI *int PI *int
PSI *[]int PSI *[]int
...@@ -69,13 +71,14 @@ var tVal = &T{ ...@@ -69,13 +71,14 @@ 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"},
PI: newInt(23), NonEmptyInterface: new(T),
PSI: newIntSlice(21, 22, 23), PI: newInt(23),
Tmpl: Must(New("x").Parse("test template")), // "x" is the value of .X PSI: newIntSlice(21, 22, 23),
Tmpl: Must(New("x").Parse("test template")), // "x" is the value of .X
} }
// A non-empty interface. // A non-empty interface.
...@@ -358,8 +361,12 @@ var execTests = []execTest{ ...@@ -358,8 +361,12 @@ var execTests = []execTest{
// Must separate dot and receiver; otherwise args are evaluated with dot set to variable. // Must separate dot and receiver; otherwise args are evaluated with dot set to variable.
{"bug0", "{{range .MSIone}}{{if $.Method1 .}}X{{end}}{{end}}", "X", tVal, true}, {"bug0", "{{range .MSIone}}{{if $.Method1 .}}X{{end}}{{end}}", "X", tVal, true},
// Do not loop endlessly in indirect for non-empty interfaces. // Do not loop endlessly in indirect for non-empty interfaces.
// The bug appears with *interface only; this is supposed to fail (cannot invoke Method0), but terminate. // The bug appears with *interface only; looped forever.
{"bug1", "{{.Method0}}", "", &iVal, false}, {"bug1", "{{.Method0}}", "M0", &iVal, true},
// Was taking address of interface field, so method set was empty.
{"bug2", "{{$.NonEmptyInterface.Method0}}", "M0", tVal, true},
// Struct values were not legal in with - mere oversight.
{"bug4", "{{with $}}{{.Method0}}{{end}}", "M0", 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