]> Cypherpunks repositories - gostls13.git/commitdiff
exp/template: find the String method by taking the address if we need to.
authorRob Pike <r@golang.org>
Thu, 11 Aug 2011 04:36:51 +0000 (14:36 +1000)
committerRob Pike <r@golang.org>
Thu, 11 Aug 2011 04:36:51 +0000 (14:36 +1000)
Also simplify nil handling in printing.

R=golang-dev, dsymonds
CC=golang-dev
https://golang.org/cl/4869042

src/pkg/exp/template/exec.go
src/pkg/exp/template/exec_test.go

index dafdaee914e09f696fb03a5f8b9c37298d1c93c4..2f3235303a2f6dc5ec6ff5a17192fdf490831ffd 100644 (file)
@@ -425,7 +425,8 @@ func methodByName(receiver reflect.Value, name string) (reflect.Value, bool) {
 }
 
 var (
-       osErrorType = reflect.TypeOf(new(os.Error)).Elem()
+       osErrorType     = reflect.TypeOf((*os.Error)(nil)).Elem()
+       fmtStringerType = reflect.TypeOf((*fmt.Stringer)(nil)).Elem()
 )
 
 // evalCall executes a function or method call. If it's a method, fun already has the receiver bound, so
@@ -625,13 +626,13 @@ func (s *state) printValue(n parse.Node, v reflect.Value) {
        }
        switch v.Kind() {
        case reflect.Ptr:
-               var isNil bool
-               if v, isNil = indirect(v); isNil {
-                       fmt.Fprint(s.wr, "<nil>")
-                       return
-               }
+               v, _ = indirect(v) // fmt.Fprint handles nil.
        case reflect.Chan, reflect.Func, reflect.Interface:
                s.errorf("can't print %s of type %s", n, v.Type())
        }
+       // If it's a value but the pointer implements Stringer, use the pointer.
+       if v.Kind() != reflect.Ptr && v.CanAddr() && reflect.PtrTo(v.Type()).Implements(fmtStringerType) {
+               v = v.Addr()
+       }
        fmt.Fprint(s.wr, v.Interface())
 }
index 4013f2ef49bd1910096332ee07c190a971e764c4..18dbcee3ea034b60a6c878fe2278ad341780376a 100644 (file)
@@ -28,6 +28,9 @@ type T struct {
        ComplexZero float64
        // Nested structs.
        U *U
+       // Struct with String method.
+       V0     V
+       V1, V2 *V
        // Slices
        SI      []int
        SIEmpty []int
@@ -57,12 +60,25 @@ 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)
+}
+
 var tVal = &T{
        True:   true,
        I:      17,
        U16:    16,
        X:      "x",
        U:      &U{"v"},
+       V0:     V{6666},
+       V1:     &V{7777}, // leave V2 as nil
        SI:     []int{3, 4, 5},
        SB:     []bool{true, false},
        MSI:    map[string]int{"one": 1, "two": 2, "three": 3},
@@ -212,6 +228,11 @@ var execTests = []execTest{
        {"$.U.V", "{{$.U.V}}", "v", tVal, true},
        {"declare in action", "{{$x := $.U.V}}{{$x}}", "v", tVal, true},
 
+       // Type with String method.
+       {"V{6666}.String()", "-{{.V0}}-", "-<6666>-", tVal, true},
+       {"&V{7777}.String()", "-{{.V1}}-", "-<7777>-", tVal, true},
+       {"(*V)(nil).String()", "-{{.V2}}-", "-nilV-", tVal, true},
+
        // Pointers.
        {"*int", "{{.PI}}", "23", tVal, true},
        {"*[]int", "{{.PSI}}", "[21 22 23]", tVal, true},