check

package module
v0.1.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 13, 2026 License: MIT Imports: 14 Imported by: 0

README

Check Go Reference

Check is a Go library for statically type-checking text/template and html/template. It catches template/type mismatches early, making refactoring safer when changing types or templates.

check-templates CLI

If all your ExecuteTemplate calls use a string literal for the template name and a static type for the data argument, you can use the CLI directly:

go get -tool github.com/typelate/check/cmd/check-templates
go tool check-templates ./...

Flags:

  • -v — list each call with position, template name, and data type
  • -C dir — change working directory before loading packages
  • -o format — output format: tsv (default) or jsonl

Library usage

Call Execute with a types.Type for the template's data (.) and the template's parse.Tree. See example_test.go for a working example.

  • muxt — builds on this library to type-check templates wired to HTTP handlers. If you only need command-line checks, muxt check works too.
  • jba/templatecheck — a more mature alternative for template type-checking.

Limitations

  1. You must provide a types.Type for the template's root context (.).
  2. No support for third-party template packages (e.g. safehtml).
  3. Cannot detect runtime conditions such as out-of-range indexes or errors from boxed types.

Documentation

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func Execute added in v0.0.2

func Execute(global *Global, tree *parse.Tree, data types.Type) error
Example
package main

import (
	"fmt"
	"go/token"
	"log"
	"slices"
	"text/template"
	"text/template/parse"

	"golang.org/x/tools/go/packages"

	"github.com/typelate/check"
)

type Person struct {
	Name string
}

func main() {
	// 1. Load Go packages with type info.
	fset := token.NewFileSet()
	pkgs, err := packages.Load(&packages.Config{
		Fset:  fset,
		Tests: true,
		Mode: packages.NeedTypes |
			packages.NeedTypesInfo |
			packages.NeedSyntax |
			packages.NeedFiles |
			packages.NeedName |
			packages.NeedModule,
		Dir: ".",
	}, ".")
	if err != nil {
		log.Fatal(err)
	}
	const testPackageName = "check_test"
	packageIndex := slices.IndexFunc(pkgs, func(p *packages.Package) bool {
		return p.Name == testPackageName
	})
	if packageIndex < 0 {
		log.Fatalf("%s package not found", testPackageName)
	}
	testPackage := pkgs[packageIndex]

	// 2. Parse a template.
	tmpl, err := template.New("example").Parse(
		/* language=gotemplate */ `
{{define "unknown field" -}}
	{{.UnknownField}}
{{- end}}
{{define "known field" -}}
	Hello, {{.Name}}!
{{- end}}"
`)
	if err != nil {
		log.Fatalf("parse error: %v", err)
	}

	// 3. Create a TreeFinder (wraps Template.Lookup).
	treeFinder := check.FindTreeFunc(func(name string) (*parse.Tree, bool) {
		if named := tmpl.Lookup(name); named != nil {
			return named.Tree, true
		}
		return nil, false
	})

	// 4. Build a function checker.
	functions := check.DefaultFunctions(testPackage.Types)

	// 5. Initialize a Global.
	global := check.NewGlobal(testPackage.Types, fset, treeFinder, functions)

	// 6. Look up a type used by the template.
	personObj := testPackage.Types.Scope().Lookup("Person")
	if personObj == nil {
		log.Fatalf("type Person not found in %s", testPackage.PkgPath)
	}

	// 7. Type-check the template.
	{
		const templateName = "unknown field"
		if err := check.Execute(global, tmpl.Lookup("unknown field").Tree, personObj.Type()); err != nil {
			fmt.Println(err.Error())
		} else {
			fmt.Printf("template %q type-check passed\n", templateName)
		}
	}
	{
		const templateName = "known field"
		if err := check.Execute(global, tmpl.Lookup("known field").Tree, personObj.Type()); err != nil {
			fmt.Println(err.Error())
		} else {
			fmt.Printf("template %q type-check passed\n", templateName)
		}
	}
}
Output:

type check failed: example:3:3: executing "unknown field" at <.UnknownField>: UnknownField not found on github.com/typelate/check_test.Person
template "known field" type-check passed

func Package added in v0.1.2

func Package(pkg *packages.Package, inspectCall ExecuteTemplateNodeInspectorFunc, inspectTemplate TemplateNodeInspectorFunc) error

Package discovers all .ExecuteTemplate calls in the given package, resolves receiver variables to their template construction chains, and type-checks each call.

ExecuteTemplate must be called with a string literal for the second parameter.

Types

type CallChecker

type CallChecker interface {
	CheckCall(*Global, string, []parse.Node, []types.Type) (types.Type, error)
}

type Error added in v0.0.5

type Error struct {
	Tree *parse.Tree
	Node parse.Node
	// contains filtered or unexported fields
}

func (*Error) Error added in v0.0.5

func (e *Error) Error() string

func (*Error) Unwrap added in v0.0.5

func (e *Error) Unwrap() error

type ExecuteTemplateNodeInspectorFunc added in v0.1.2

type ExecuteTemplateNodeInspectorFunc func(node *ast.CallExpr, t *parse.Tree, tp types.Type)

type FindTreeFunc

type FindTreeFunc func(name string) (*parse.Tree, bool)

func (FindTreeFunc) FindTree

func (fn FindTreeFunc) FindTree(name string) (*parse.Tree, bool)

type Functions

type Functions map[string]*types.Signature

func DefaultFunctions

func DefaultFunctions(pkg *types.Package) Functions

DefaultFunctions returns the standard functions defined in html/template and text/template. It looks up escape functions (js, html, urlquery) from whichever template package is imported.

func (Functions) Add

func (functions Functions) Add(m Functions) Functions

func (Functions) CheckCall

func (functions Functions) CheckCall(global *Global, funcIdent string, argNodes []parse.Node, argTypes []types.Type) (types.Type, error)

type Global

type Global struct {
	InspectTemplateNode TemplateNodeInspectorFunc
	InspectCallNode     ExecuteTemplateNodeInspectorFunc

	// Qualifier controls how types are printed in error messages.
	// If nil, types are printed with their full package path.
	// See types.WriteType for details.
	Qualifier types.Qualifier
	// contains filtered or unexported fields
}

func NewGlobal

func NewGlobal(pkg *types.Package, fileSet *token.FileSet, trees TreeFinder, fnChecker CallChecker) *Global

func (*Global) TypeString added in v0.1.0

func (g *Global) TypeString(typ types.Type) string

TypeString returns the string representation of typ using the configured Qualifier.

type TemplateNodeInspectorFunc added in v0.0.5

type TemplateNodeInspectorFunc func(node *parse.TemplateNode, t *parse.Tree, tp types.Type)

type TreeFinder

type TreeFinder interface {
	FindTree(name string) (*parse.Tree, bool)
}

TreeFinder should wrap https://pkg.go.dev/html/template#Template.Lookup and return the Tree field from the Template If you are using text/template the lookup function from that package should also work.

type TypeNodeMapping

type TypeNodeMapping map[types.Type][]parse.Node

Directories

Path Synopsis
cmd
check-templates command
internal

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL