Commit d3d08e1e authored by Rob Pike's avatar Rob Pike

exp/template: forgot to allow . as a function argument

R=golang-dev, adg
CC=golang-dev
https://golang.org/cl/4671053
parent b8c66429
...@@ -284,7 +284,10 @@ isFirst, canBeMethod bool) reflect.Value { ...@@ -284,7 +284,10 @@ isFirst, canBeMethod bool) reflect.Value {
if canBeMethod { if canBeMethod {
// Need to get to a value of type *T to guarantee we see all // Need to get to a value of type *T to guarantee we see all
// methods of T and *T. // methods of T and *T.
ptr := data.Addr() ptr := data
if ptr.CanAddr() {
ptr = ptr.Addr()
}
if method, ok := methodByName(ptr.Type(), fieldName); ok { if method, ok := methodByName(ptr.Type(), fieldName); ok {
return s.evalCall(ptr, method.Func, fieldName, true, args, final) return s.evalCall(ptr, method.Func, fieldName, true, args, final)
} }
...@@ -382,8 +385,14 @@ func (s *state) evalCall(v, fun reflect.Value, name string, isMethod bool, args ...@@ -382,8 +385,14 @@ func (s *state) evalCall(v, fun reflect.Value, name string, isMethod bool, args
} }
func (s *state) evalArg(data reflect.Value, typ reflect.Type, n node) reflect.Value { func (s *state) evalArg(data reflect.Value, typ reflect.Type, n node) reflect.Value {
if field, ok := n.(*fieldNode); ok { switch arg := n.(type) {
value := s.evalFieldNode(data, field, []node{n}, zero) case *dotNode:
if !data.Type().AssignableTo(typ) {
s.errorf("wrong type for value; expected %s; got %s", typ, data.Type())
}
return data
case *fieldNode:
value := s.evalFieldNode(data, arg, []node{n}, zero)
if !value.Type().AssignableTo(typ) { if !value.Type().AssignableTo(typ) {
s.errorf("wrong type for value; expected %s; got %s", typ, value.Type()) s.errorf("wrong type for value; expected %s; got %s", typ, value.Type())
} }
......
...@@ -161,7 +161,7 @@ var execTests = []execTest{ ...@@ -161,7 +161,7 @@ var execTests = []execTest{
{"*[]int[1]", "{{index .PSI 1}}", "22", tVal, true}, {"*[]int[1]", "{{index .PSI 1}}", "22", tVal, true},
{"NIL", "{{.NIL}}", "<nil>", tVal, true}, {"NIL", "{{.NIL}}", "<nil>", tVal, true},
// Emtpy interfaces holding values. // Empty interfaces holding values.
{"empty nil", "{{.Empty0}}", "<no value>", tVal, true}, {"empty nil", "{{.Empty0}}", "<no value>", tVal, true},
{"empty with int", "{{.Empty1}}", "3", tVal, true}, {"empty with int", "{{.Empty1}}", "3", tVal, true},
{"empty with string", "{{.Empty2}}", "empty2", tVal, true}, {"empty with string", "{{.Empty2}}", "empty2", tVal, true},
...@@ -200,7 +200,7 @@ var execTests = []execTest{ ...@@ -200,7 +200,7 @@ var execTests = []execTest{
{"printf float", `{{printf "%g" 3.5}}`, "3.5", tVal, true}, {"printf float", `{{printf "%g" 3.5}}`, "3.5", tVal, true},
{"printf complex", `{{printf "%g" 1+7i}}`, "(1+7i)", tVal, true}, {"printf complex", `{{printf "%g" 1+7i}}`, "(1+7i)", tVal, true},
{"printf string", `{{printf "%s" "hello"}}`, "hello", tVal, true}, {"printf string", `{{printf "%s" "hello"}}`, "hello", tVal, true},
{"printf function", `{{printf "%#q" gopher}}`, "`gopher`", tVal, true}, {"printf function", `{{printf "%#q" zeroArgs}}`, "`zeroArgs`", tVal, true},
{"printf field", `{{printf "%s" .U.V}}`, "v", tVal, true}, {"printf field", `{{printf "%s" .U.V}}`, "v", tVal, true},
{"printf method", `{{printf "%s" .Method0}}`, "M0", tVal, true}, {"printf method", `{{printf "%s" .Method0}}`, "M0", tVal, true},
{"printf lots", `{{printf "%d %s %g %s" 127 "hello" 7-3i .Method0}}`, "127 hello (7-3i) M0", tVal, true}, {"printf lots", `{{printf "%d %s %g %s" 127 "hello" 7-3i .Method0}}`, "127 hello (7-3i) M0", tVal, true},
...@@ -273,13 +273,17 @@ var execTests = []execTest{ ...@@ -273,13 +273,17 @@ var execTests = []execTest{
{"error method, no error", "{{.EPERM false}}", "false", tVal, true}, {"error method, no error", "{{.EPERM false}}", "false", tVal, true},
} }
func gopher() string { func zeroArgs() string {
return "gopher" return "zeroArgs"
}
func oneArg(a string) string {
return "oneArg=" + a
} }
func testExecute(execTests []execTest, set *Set, t *testing.T) { func testExecute(execTests []execTest, set *Set, t *testing.T) {
b := new(bytes.Buffer) b := new(bytes.Buffer)
funcs := FuncMap{"gopher": gopher} funcs := FuncMap{"zeroArgs": zeroArgs, "oneArg": oneArg}
for _, test := range execTests { for _, test := range execTests {
tmpl := New(test.name).Funcs(funcs) tmpl := New(test.name).Funcs(funcs)
err := tmpl.Parse(test.input) err := tmpl.Parse(test.input)
......
...@@ -81,6 +81,10 @@ var setExecTests = []execTest{ ...@@ -81,6 +81,10 @@ var setExecTests = []execTest{
{"invoke dot []int", `{{template "dot" .SI}}`, "[3 4 5]", tVal, true}, {"invoke dot []int", `{{template "dot" .SI}}`, "[3 4 5]", tVal, true},
{"invoke dotV", `{{template "dotV" .U}}`, "v", tVal, true}, {"invoke dotV", `{{template "dotV" .U}}`, "v", tVal, true},
{"invoke nested int", `{{template "nested" .I}}`, "17", tVal, true}, {"invoke nested int", `{{template "nested" .I}}`, "17", tVal, true},
// User-defined function: test argument evaluator.
{"testFunc literal", `{{oneArg "joe"}}`, "oneArg=joe", tVal, true},
{"testFunc .", `{{oneArg .}}`, "oneArg=joe", "joe", true},
} }
const setText = ` const setText = `
......
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