rsc / tmplfunc

Go templates invoked as functions

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

error when calling a template with same name as a predeclared function

dmitshur opened this issue · comments

tmplfunc makes it possible to shadow predeclared functions by defining a template with the same name. I'm not sure if that's intentional and meant to be supported, but there's no error returned if one tries it and no documentation saying it shouldn't be done, so the rest of this issue report assumes so (even though it's not a feature I'd want to use myself).

It seems to work in most cases that I've tried while debugging this, but there's one edge case (that came up in practice in x/website, see golang/go#51989) that is problematic. A minified snippet that triggers it:

package main

import (
	"fmt"
	"html/template"
	"os"

	"rsc.io/tmplfunc"
)

func main() {
	t := template.New("")
	tmplfunc.MustParse(t, `
{{html .}}

{{define "html"}}{{.Field}}{{end}}
`)
	err := t.Execute(os.Stdout, struct{ Field string }{Field: "a < b"})
	fmt.Println("err:", err)

	// Output:
	// err: template: :1:0: executing "" at <html>: error calling html: template: :4:19: executing "html" at <.Field>: can't evaluate field Field in type string
}

(playground link: https://go.dev/play/p/B4eC3urDPjz)

The error is confusing and incorrect. It should say "in type struct{Field string}" not "in type string", and it shouldn't be an error since that type does in fact have that field.

Notably, changing any one of the following conditions makes this template work without errors and render expected output:

  • calling the template with standard {{template "html" .}} syntax (rather than as a function as this package allows)
  • not shadowing a predeclared function "html", i.e. using "html2" or any other name
  • not involving a field access inside the template, e.g., factoring it out to the caller
  • using text/template instead of html/template (this is likely only relevant to the "html" predeclared function and not others)

There is possibly a real bug in an edge case in the standard library that tmplfunc happens to expose here, or maybe this problem is entirely in this package—I'm not familiar with tmplfunc enough look further. (I've tested with the latest version of this package using Go 1.18 and today's gotip.)