From 1adda4601aa9c28199f265d5826fd9c31ee73f64 Mon Sep 17 00:00:00 2001 From: Rob Pike Date: Tue, 9 Aug 2011 17:07:29 +1000 Subject: [PATCH] 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 --- src/pkg/exp/template/exec.go | 8 +++++--- src/pkg/exp/template/exec_test.go | 25 ++++++++++++++++--------- 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/src/pkg/exp/template/exec.go b/src/pkg/exp/template/exec.go index 6fc1da4a49c..7d7a9c73268 100644 --- a/src/pkg/exp/template/exec.go +++ b/src/pkg/exp/template/exec.go @@ -179,6 +179,8 @@ func isTrue(val reflect.Value) (truth, ok bool) { truth = val.Float() != 0 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: truth = val.Uint() != 0 + case reflect.Struct: + truth = true // Struct values are always true. default: return } @@ -377,10 +379,10 @@ func (s *state) evalField(dot reflect.Value, fieldName string, args []parse.Node } typ := receiver.Type() receiver, _ = indirect(receiver) - // Need to get to a value of type *T to guarantee we see all - // methods of T and *T. + // Unless it's an interface, need to get to a value of type *T to guarantee + // we see all methods of T and *T. ptr := receiver - if ptr.CanAddr() { + if ptr.Kind() != reflect.Interface && ptr.CanAddr() { ptr = ptr.Addr() } if method, ok := methodByName(ptr, fieldName); ok { diff --git a/src/pkg/exp/template/exec_test.go b/src/pkg/exp/template/exec_test.go index 415f170080d..b7884744027 100644 --- a/src/pkg/exp/template/exec_test.go +++ b/src/pkg/exp/template/exec_test.go @@ -43,6 +43,8 @@ type T struct { Empty2 interface{} Empty3 interface{} Empty4 interface{} + // Non-empty interface. + NonEmptyInterface I // Pointers PI *int PSI *[]int @@ -69,13 +71,14 @@ var tVal = &T{ {"one": 1, "two": 2}, {"eleven": 11, "twelve": 12}, }, - Empty1: 3, - Empty2: "empty2", - Empty3: []int{7, 8}, - Empty4: &U{"UinEmpty"}, - PI: newInt(23), - PSI: newIntSlice(21, 22, 23), - Tmpl: Must(New("x").Parse("test template")), // "x" is the value of .X + Empty1: 3, + Empty2: "empty2", + Empty3: []int{7, 8}, + Empty4: &U{"UinEmpty"}, + NonEmptyInterface: new(T), + PI: newInt(23), + PSI: newIntSlice(21, 22, 23), + Tmpl: Must(New("x").Parse("test template")), // "x" is the value of .X } // A non-empty interface. @@ -358,8 +361,12 @@ var execTests = []execTest{ // 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}, // 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. - {"bug1", "{{.Method0}}", "", &iVal, false}, + // The bug appears with *interface only; looped forever. + {"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 {