Skip to content

text/template: panics encountered during function calls are hard to treat and debug #28242

Closed
@bep

Description

@bep
 go version && go env
go version go1.11 darwin/amd64
GOARCH="amd64"
GOBIN=""
GOCACHE="/Users/bep/Library/Caches/go-build"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="darwin"
GOOS="darwin"
GOPATH="/Users/bep/go"
GOPROXY=""
GORACE=""
GOROOT="/usr/local/Cellar/go/1.11/libexec"
GOTMPDIR=""
GOTOOLDIR="/usr/local/Cellar/go/1.11/libexec/pkg/tool/darwin_amd64"
GCCGO="gccgo"
CC="clang"
CXX="clang++"
CGO_ENABLED="1"
GOMOD="/Users/bep/dev/go/gohugoio/hugo/go.mod"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/n6/s_85mm8d31j6yctssnmn_g1r0000gn/T/go-build656061374=/tmp/go-build -gno-record-gcc-switches -fno-common"
package main

import (
	"bytes"
	"log"
	"strings"
	"text/template"
)

type F struct {
	name string
}

func (f *F) Name() string {
	return f.name
}

func (f *F) Other() *F {
	return nil
}

func main() {
	var buf bytes.Buffer
	tmpl, err := template.New("").Parse(`{{ .Other.Name }}`)
	if err != nil {
		log.Fatal(err)
	}

	data := &F{name: "foo"}

	if err := tmpl.Execute(&buf, data); err != nil {
		log.Fatal(err)
	}

	result := strings.TrimSpace(buf.String())

	if !strings.Contains(result, "foo") {
		log.Fatal(result)
	}

}

The above panics:

panic: runtime error: invalid memory address or nil pointer dereference [recovered]
	panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x8 pc=0x10ec8a5]

goroutine 1 [running]:
text/template.errRecover(0xc0000a9ec0)
	/usr/local/Cellar/go/1.11/libexec/src/text/template/exec.go:160 +0x1d0
panic(0x110e180, 0x121a910)
	/usr/local/Cellar/go/1.11/libexec/src/runtime/panic.go:513 +0x1b9
main.(*F).Name(0x0, 0x0, 0x0)
	/Users/bep/dev/go/bep/temp/main.go:15 +0x5
reflect.Value.call(0x110e5c0, 0xc000068338, 0x293, 0x112ebd0, 0x4, 0x123da30, 0x0, 0x0, 0x112d940, 0x1, ...)
	/usr/local/Cellar/go/1.11/libexec/src/reflect/value.go:447 +0x449
reflect.Value.Call(0x110e5c0, 0xc000068338, 0x293, 0x123da30, 0x0, 0x0, 0xc000072100, 0x1, 0x1)
	/usr/local/Cellar/go/1.11/libexec/src/reflect/value.go:308 +0xa4
text/template.(*state).evalCall(0xc0000a9e40, 0x110e5c0, 0xc000068290, 0x16, 0x110e5c0, 0xc000068338, 0x293, 0x114e920, 0xc00006a360, 0xc000082027, ...)
	/usr/local/Cellar/go/1.11/libexec/src/text/template/exec.go:695 +0x6bd
text/template.(*state).evalField(0xc0000a9e40, 0x110e5c0, 0xc000068290, 0x16, 0xc000082027, 0x4, 0x114e920, 0xc00006a360, 0xc000068270, 0x1, ...)
	/usr/local/Cellar/go/1.11/libexec/src/text/template/exec.go:585 +0xd78
text/template.(*state).evalFieldChain(0xc0000a9e40, 0x110e5c0, 0xc000068290, 0x16, 0x110e5c0, 0xc000068290, 0x16, 0x114e920, 0xc00006a360, 0xc000072060, ...)
	/usr/local/Cellar/go/1.11/libexec/src/text/template/exec.go:553 +0x217
text/template.(*state).evalFieldNode(0xc0000a9e40, 0x110e5c0, 0xc000068290, 0x16, 0xc00006a360, 0xc000068270, 0x1, 0x1, 0x110e420, 0x123da30, ...)
	/usr/local/Cellar/go/1.11/libexec/src/text/template/exec.go:517 +0x113
text/template.(*state).evalCommand(0xc0000a9e40, 0x110e5c0, 0xc000068290, 0x16, 0xc00006a300, 0x110e420, 0x123da30, 0x99, 0x100b738, 0xd0, ...)
	/usr/local/Cellar/go/1.11/libexec/src/text/template/exec.go:455 +0x725
text/template.(*state).evalPipeline(0xc0000a9e40, 0x110e5c0, 0xc000068290, 0x16, 0xc000092180, 0xc0000a9c01, 0xc000074750, 0xc0000a9d40)
	/usr/local/Cellar/go/1.11/libexec/src/text/template/exec.go:429 +0x126
text/template.(*state).walk(0xc0000a9e40, 0x110e5c0, 0xc000068290, 0x16, 0x114e7e0, 0xc00006a390)
	/usr/local/Cellar/go/1.11/libexec/src/text/template/exec.go:254 +0x49b
text/template.(*state).walk(0xc0000a9e40, 0x110e5c0, 0xc000068290, 0x16, 0x114e9e0, 0xc00006a2d0)
	/usr/local/Cellar/go/1.11/libexec/src/text/template/exec.go:262 +0x142
text/template.(*Template).execute(0xc000084080, 0x114e2e0, 0xc0000a0000, 0x110e5c0, 0xc000068290, 0x0, 0x0)
	/usr/local/Cellar/go/1.11/libexec/src/text/template/exec.go:217 +0x215
text/template.(*Template).Execute(0xc000084080, 0x114e2e0, 0xc0000a0000, 0x110e5c0, 0xc000068290, 0x0, 0xc00007ef38)
	/usr/local/Cellar/go/1.11/libexec/src/text/template/exec.go:200 +0x53
main.main()

I understand that the user should wrap the above in a with or something, but having maintained Hugo for some years I can say this:

  • This is a very common problem
  • And the error you get from this is extremely hard to debug

That, and if you follow the code in the Go source, there are nil checks for the other cases, but not for the "method case":

https://github.com/golang/go/blob/master/src/text/template/exec.go#L594

Metadata

Metadata

Assignees

No one assigned

    Labels

    FrozenDueToAgeNeedsDecisionFeedback is required from experts, contributors, and/or the community before a change can be made.

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions