Commit 33ce9c19 authored by Russ Cox's avatar Russ Cox

text/template: make zero Template work again

Fixes #11379.

Change-Id: Idbb5c3faad472b77e9867dd2d4551fef5e4ac5f1
Reviewed-on: https://go-review.googlesource.com/11421Reviewed-by: 's avatarRob Pike <r@golang.org>
parent 66130907
...@@ -113,7 +113,10 @@ func errRecover(errp *error) { ...@@ -113,7 +113,10 @@ func errRecover(errp *error) {
// the output writer. // the output writer.
// A template may be executed safely in parallel. // A template may be executed safely in parallel.
func (t *Template) ExecuteTemplate(wr io.Writer, name string, data interface{}) error { func (t *Template) ExecuteTemplate(wr io.Writer, name string, data interface{}) error {
tmpl := t.tmpl[name] var tmpl *Template
if t.common != nil {
tmpl = t.tmpl[name]
}
if tmpl == nil { if tmpl == nil {
return fmt.Errorf("template: no template %q associated with template %q", name, t.name) return fmt.Errorf("template: no template %q associated with template %q", name, t.name)
} }
...@@ -146,6 +149,9 @@ func (t *Template) Execute(wr io.Writer, data interface{}) (err error) { ...@@ -146,6 +149,9 @@ func (t *Template) Execute(wr io.Writer, data interface{}) (err error) {
// it returns the empty string. For generating an error message here // it returns the empty string. For generating an error message here
// and in html/template. // and in html/template.
func (t *Template) DefinedTemplates() string { func (t *Template) DefinedTemplates() string {
if t.common == nil {
return ""
}
var b bytes.Buffer var b bytes.Buffer
for name, tmpl := range t.tmpl { for name, tmpl := range t.tmpl {
if tmpl.Tree == nil || tmpl.Root == nil { if tmpl.Tree == nil || tmpl.Root == nil {
......
...@@ -882,7 +882,13 @@ func TestTree(t *testing.T) { ...@@ -882,7 +882,13 @@ func TestTree(t *testing.T) {
func TestExecuteOnNewTemplate(t *testing.T) { func TestExecuteOnNewTemplate(t *testing.T) {
// This is issue 3872. // This is issue 3872.
_ = New("Name").Templates() New("Name").Templates()
// This is issue 11379.
new(Template).Templates()
new(Template).Parse("")
new(Template).New("abc").Parse("")
new(Template).Execute(nil, nil) // returns an error (but does not crash)
new(Template).ExecuteTemplate(nil, "XXX", nil) // returns an error (but does not crash)
} }
const testTemplates = `{{define "one"}}one{{end}}{{define "two"}}two{{end}}` const testTemplates = `{{define "one"}}one{{end}}{{define "two"}}two{{end}}`
......
...@@ -42,6 +42,7 @@ func ParseFiles(filenames ...string) (*Template, error) { ...@@ -42,6 +42,7 @@ func ParseFiles(filenames ...string) (*Template, error) {
// contents before calling ParseFiles, t.Execute may fail. In that // contents before calling ParseFiles, t.Execute may fail. In that
// case use t.ExecuteTemplate to execute a valid template. // case use t.ExecuteTemplate to execute a valid template.
func (t *Template) ParseFiles(filenames ...string) (*Template, error) { func (t *Template) ParseFiles(filenames ...string) (*Template, error) {
t.init()
return parseFiles(t, filenames...) return parseFiles(t, filenames...)
} }
...@@ -97,6 +98,7 @@ func ParseGlob(pattern string) (*Template, error) { ...@@ -97,6 +98,7 @@ func ParseGlob(pattern string) (*Template, error) {
// equivalent to calling t.ParseFiles with the list of files matched by the // equivalent to calling t.ParseFiles with the list of files matched by the
// pattern. // pattern.
func (t *Template) ParseGlob(pattern string) (*Template, error) { func (t *Template) ParseGlob(pattern string) (*Template, error) {
t.init()
return parseGlob(t, pattern) return parseGlob(t, pattern)
} }
......
...@@ -40,6 +40,7 @@ type option struct { ...@@ -40,6 +40,7 @@ type option struct {
// Execution stops immediately with an error. // Execution stops immediately with an error.
// //
func (t *Template) Option(opt ...string) *Template { func (t *Template) Option(opt ...string) *Template {
t.init()
for _, s := range opt { for _, s := range opt {
t.setOption(s) t.setOption(s)
} }
......
...@@ -52,6 +52,7 @@ func (t *Template) Name() string { ...@@ -52,6 +52,7 @@ func (t *Template) Name() string {
// delimiters. The association, which is transitive, allows one template to // delimiters. The association, which is transitive, allows one template to
// invoke another with a {{template}} action. // invoke another with a {{template}} action.
func (t *Template) New(name string) *Template { func (t *Template) New(name string) *Template {
t.init()
nt := &Template{ nt := &Template{
name: name, name: name,
common: t.common, common: t.common,
...@@ -81,6 +82,9 @@ func (t *Template) init() { ...@@ -81,6 +82,9 @@ func (t *Template) init() {
func (t *Template) Clone() (*Template, error) { func (t *Template) Clone() (*Template, error) {
nt := t.copy(nil) nt := t.copy(nil)
nt.init() nt.init()
if t.common == nil {
return nt, nil
}
for k, v := range t.tmpl { for k, v := range t.tmpl {
if k == t.name { if k == t.name {
nt.tmpl[t.name] = nt nt.tmpl[t.name] = nt
...@@ -115,6 +119,7 @@ func (t *Template) copy(c *common) *Template { ...@@ -115,6 +119,7 @@ func (t *Template) copy(c *common) *Template {
// If the template does not already exist, it will create a new one. // If the template does not already exist, it will create a new one.
// It is an error to reuse a name except to overwrite an empty template. // It is an error to reuse a name except to overwrite an empty template.
func (t *Template) AddParseTree(name string, tree *parse.Tree) (*Template, error) { func (t *Template) AddParseTree(name string, tree *parse.Tree) (*Template, error) {
t.init()
// If the name is the name of this template, overwrite this template. // If the name is the name of this template, overwrite this template.
// The associate method checks it's not a redefinition. // The associate method checks it's not a redefinition.
nt := t nt := t
...@@ -132,6 +137,9 @@ func (t *Template) AddParseTree(name string, tree *parse.Tree) (*Template, error ...@@ -132,6 +137,9 @@ func (t *Template) AddParseTree(name string, tree *parse.Tree) (*Template, error
// Templates returns a slice of defined templates associated with t. // Templates returns a slice of defined templates associated with t.
func (t *Template) Templates() []*Template { func (t *Template) Templates() []*Template {
if t.common == nil {
return nil
}
// Return a slice so we don't expose the map. // Return a slice so we don't expose the map.
m := make([]*Template, 0, len(t.tmpl)) m := make([]*Template, 0, len(t.tmpl))
for _, v := range t.tmpl { for _, v := range t.tmpl {
...@@ -146,6 +154,7 @@ func (t *Template) Templates() []*Template { ...@@ -146,6 +154,7 @@ func (t *Template) Templates() []*Template {
// corresponding default: {{ or }}. // corresponding default: {{ or }}.
// The return value is the template, so calls can be chained. // The return value is the template, so calls can be chained.
func (t *Template) Delims(left, right string) *Template { func (t *Template) Delims(left, right string) *Template {
t.init()
t.leftDelim = left t.leftDelim = left
t.rightDelim = right t.rightDelim = right
return t return t
...@@ -156,6 +165,7 @@ func (t *Template) Delims(left, right string) *Template { ...@@ -156,6 +165,7 @@ func (t *Template) Delims(left, right string) *Template {
// type. However, it is legal to overwrite elements of the map. The return // type. However, it is legal to overwrite elements of the map. The return
// value is the template, so calls can be chained. // value is the template, so calls can be chained.
func (t *Template) Funcs(funcMap FuncMap) *Template { func (t *Template) Funcs(funcMap FuncMap) *Template {
t.init()
t.muFuncs.Lock() t.muFuncs.Lock()
defer t.muFuncs.Unlock() defer t.muFuncs.Unlock()
addValueFuncs(t.execFuncs, funcMap) addValueFuncs(t.execFuncs, funcMap)
...@@ -166,6 +176,9 @@ func (t *Template) Funcs(funcMap FuncMap) *Template { ...@@ -166,6 +176,9 @@ func (t *Template) Funcs(funcMap FuncMap) *Template {
// Lookup returns the template with the given name that is associated with t. // Lookup returns the template with the given name that is associated with t.
// It returns nil if there is no such template or the template has no definition. // It returns nil if there is no such template or the template has no definition.
func (t *Template) Lookup(name string) *Template { func (t *Template) Lookup(name string) *Template {
if t.common == nil {
return nil
}
return t.tmpl[name] return t.tmpl[name]
} }
...@@ -177,6 +190,7 @@ func (t *Template) Lookup(name string) *Template { ...@@ -177,6 +190,7 @@ func (t *Template) Lookup(name string) *Template {
// (In multiple calls to Parse with the same receiver template, only one call // (In multiple calls to Parse with the same receiver template, only one call
// can contain text other than space, comments, and template definitions.) // can contain text other than space, comments, and template definitions.)
func (t *Template) Parse(text string) (*Template, error) { func (t *Template) Parse(text string) (*Template, error) {
t.init()
t.muFuncs.RLock() t.muFuncs.RLock()
trees, err := parse.Parse(t.name, text, t.leftDelim, t.rightDelim, t.parseFuncs, builtins) trees, err := parse.Parse(t.name, text, t.leftDelim, t.rightDelim, t.parseFuncs, builtins)
t.muFuncs.RUnlock() t.muFuncs.RUnlock()
......
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