From 11dba2ec2d8fcedc1da0103925c254586ef51120 Mon Sep 17 00:00:00 2001 From: Rob Pike Date: Fri, 20 Mar 2015 10:47:52 -0700 Subject: [PATCH] html/template: fix crash when escaping incomplete template text/template turned this into an error but html/template crashed. Refactor text/template.Execute to export a new function, text/template.DefinedTemplates, so html/template can get the same helpful error message in this case, and invoke it when there is no definition for a template being escaped. Fixes #10204. Change-Id: I1d04e9e7ebca829bc08509caeb65e75da969711f Reviewed-on: https://go-review.googlesource.com/7855 Reviewed-by: Russ Cox --- src/html/template/escape_test.go | 15 ++++++++++++ src/html/template/template.go | 3 +++ src/text/template/exec.go | 39 ++++++++++++++++++++------------ 3 files changed, 43 insertions(+), 14 deletions(-) diff --git a/src/html/template/escape_test.go b/src/html/template/escape_test.go index ef7b877484..9c9502a617 100644 --- a/src/html/template/escape_test.go +++ b/src/html/template/escape_test.go @@ -1686,6 +1686,21 @@ func TestPipeToMethodIsEscaped(t *testing.T) { } } +// Unlike text/template, html/template crashed if given an incomplete +// template, that is, a template that had been named but not given any content. +// This is issue #10204. +func TestErrorOnUndefined(t *testing.T) { + tmpl := New("undefined") + + err := tmpl.Execute(nil, nil) + if err == nil { + t.Error("expected error") + } + if !strings.Contains(err.Error(), "incomplete") { + t.Errorf("expected error about incomplete template; got %s", err) + } +} + func BenchmarkEscapedExecute(b *testing.B) { tmpl := Must(New("t").Parse(`{{.}}`)) var buf bytes.Buffer diff --git a/src/html/template/template.go b/src/html/template/template.go index ce6170105c..64c0041c9c 100644 --- a/src/html/template/template.go +++ b/src/html/template/template.go @@ -56,6 +56,9 @@ func (t *Template) escape() error { t.nameSpace.mu.Lock() defer t.nameSpace.mu.Unlock() if t.escapeErr == nil { + if t.Tree == nil { + return fmt.Errorf("template: %q is an incomplete or empty template%s", t.Name(), t.text.DefinedTemplates()) + } if err := escapeTemplate(t, t.text.Root, t.Name()); err != nil { return err } diff --git a/src/text/template/exec.go b/src/text/template/exec.go index faf31e3ede..613a778188 100644 --- a/src/text/template/exec.go +++ b/src/text/template/exec.go @@ -136,24 +136,35 @@ func (t *Template) Execute(wr io.Writer, data interface{}) (err error) { } t.init() if t.Tree == nil || t.Root == nil { - var b bytes.Buffer - for name, tmpl := range t.tmpl { - if tmpl.Tree == nil || tmpl.Root == nil { - continue - } - if b.Len() > 0 { - b.WriteString(", ") - } - fmt.Fprintf(&b, "%q", name) + state.errorf("%q is an incomplete or empty template%s", t.Name(), t.DefinedTemplates()) + } + state.walk(value, t.Root) + return +} + +// DefinedTemplates returns a string listing the defined templates, +// prefixed by the string "defined templates are: ". If there are none, +// it returns the empty string. For generating an error message here +// and in html/template. +func (t *Template) DefinedTemplates() string { + if t.common == nil { + return "" + } + var b bytes.Buffer + for name, tmpl := range t.tmpl { + if tmpl.Tree == nil || tmpl.Root == nil { + continue } - var s string if b.Len() > 0 { - s = "; defined templates are: " + b.String() + b.WriteString(", ") } - state.errorf("%q is an incomplete or empty template%s", t.Name(), s) + fmt.Fprintf(&b, "%q", name) } - state.walk(value, t.Root) - return + var s string + if b.Len() > 0 { + s = "; defined templates are: " + b.String() + } + return s } // Walk functions step through the major pieces of the template structure, -- 2.48.1