Draft
Conversation
Add WarningFunc to the check.Package API and a -w CLI flag that emits warnings when ExecuteTemplate is called with a non-static template name. Warnings are off by default to avoid noise in existing workflows. Also add tests for ./... with deeply nested packages and document how the CLI discovers templates (embed.FS requirement, supported patterns). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The CLI can now analyze templates loaded via template.ParseFiles() and
template.ParseGlob() with string literal arguments, in addition to the
existing embed.FS + ParseFS support.
Supported in all contexts: package-level calls (template.ParseFiles),
variable receiver calls (ts.ParseFiles), and chained calls
(template.New("x").ParseFiles).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
types.Eval required formatting AST back to source text and re-evaluating, which was fragile for method values and package-level variables. Using typesInfo.TypeOf directly resolves function signatures from the already- computed type information, handling all expression forms correctly. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add TemplateNames() to the Template interface to enumerate all defined
templates. After type-checking, compare referenced templates (from
ExecuteTemplate calls and {{template}} actions) against all available
templates and warn about unreferenced ones.
Templates with no content (e.g. the root container from template.New)
are excluded from the check.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When dot or a field is an interface type (e.g. any), field access like .Title cannot be statically verified. Instead of producing a hard error, emit a warning when -w is enabled and continue type-checking with the field treated as an empty interface. Adds WarningFunc to Global for template-level warnings, PackageWarningFunc for package-level warnings, and bridges them through checkCalls. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When a template accesses fields on a pointer type (e.g. *Page) without
a {{with}} or {{if}} guard, the access may panic at runtime if the
pointer is nil. With -w enabled, emit a warning suggesting a nil guard.
Adds dotGuarded tracking to scope: set to true inside {{with}} and
{{if}} blocks so that guarded pointer access does not trigger warnings.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Verifies that .Inner.Value warns when Inner is *Inner, catching pointer dereference at any depth in a field chain. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace the coarse dotGuarded bool with a per-field-path guarded set.
{{with .Foo}} guards only .Foo (and dot inside the block), not unrelated
fields like .Bar. {{if .Bar}} guards .Bar within its block.
Guarding .Foo does NOT guard .Foo.Baz — if Baz is a pointer type,
accessing fields through it still warns unless .Foo.Baz is separately
guarded.
Adds pipeFieldPath helper to extract the tested field path from
{{with}}/{{if}} pipe expressions.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
# Conflicts: # README.md
Add WarningCategory enum to PackageWarningFunc callback so library consumers can filter warnings by type. CLI continues to use single -w flag that enables all categories. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Document all four warning categories with Go code and template examples showing what triggers each warning and how to fix it. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Change from "warning: pos: message" to "pos: warning - message" to be consistent with Go tooling diagnostic conventions. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Warnings: file:line:col: message (W00N) Errors: file:line:col: executing "name" at <.Field>: message (E001) Remove the "type check failed:" prefix from errors and "warning -" prefix from warnings to match go vet / staticcheck conventions. Add diagnostic codes (W001-W004, E001) for machine-parseable output. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Use case-insensitive filepath comparison in check_test.go and stdlib_test.go to handle Windows drive letter casing differences between runtime.Caller and packages.Load. Also update convertTextExecError to match the new diagnostic format with category codes introduced in 22bf787. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add ResolveStringExpr to statically resolve template names from named constants, simple variable assignments, and function parameters by tracing through the intra-package and cross-package call graph. Tier 1: Named constants (const name = "page") Tier 2: Simple variables (name := "page"), with reassignment detection Tier 3: Function parameter tracing via call-site index Tier 4: Interface data type resolution (any → concrete type at call site) Tier 5: Multi-level call chains with fixed-point iteration (up to 5 levels) Tier 6: Cross-package tracing via PackageWithDeferred/DeferredCall API This replaces the single BasicLiteralString check in findExecuteCalls with a progressive resolution pipeline that handles the most common patterns for wrapping ExecuteTemplate in helper functions. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ection - checkPrintf in func.go validates format verb count and types (W-codes emitted via existing error path; arg-count mismatch and type mismatch both reported as hard errors consistent with other type checks) - package.go now recognises tpl.Execute(w, data) 2-arg calls and resolves the template name from the receiver's root template - check.go tracks variable declarations vs uses per scope and emits W005 (WarnUnusedVariable) for $x declared but never read - Script tests added for all three features (pass/err/warn variants) - PLAN.md added documenting the full 7-feature roadmap Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add WarnDeadBranch warning emitted when an {{if}} or {{with}} block
uses a literal true or false BoolNode as its condition, making one
branch statically unreachable. The nil literal case is excluded because
the template parser itself rejects {{if nil}} before analysis runs.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Track every (templateName, dataType) pair seen at {{template "name" .Data}}
call sites during Execute. After the walk, emit WarnInconsistentTemplateTypes
(W007) if the same sub-template is invoked with mutually non-assignable
types across different call sites. Untyped nil and empty interface are
excluded from the check as they carry no structural information.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
analyzer/analyzer.go wraps check.Package as a go/analysis.Analyzer. It constructs a packages.Package directly from the analysis.Pass fields (reusing the already type-checked AST) and expands //go:embed directives from AST comments to populate EmbedFiles without a second packages.Load. Diagnostics are reported at the ExecuteTemplate call site in Go source so gopls shows squiggles in .go files. The -w flag enables warnings. cmd/templatecheck/main.go is a singlechecker binary so the analyzer can be used as: go vet -vettool=$(which templatecheck) ./... Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
vscode-go-template-check/ is a VS Code extension that spawns check-templates on save and places error/warning squiggles inside .gohtml template files at the exact positions the tool reports. Key design points: - No LSP: lightweight CLI-spawn on save, stderr parsed into DiagnosticCollection entries per template file URI - New language ID `gohtml` for .gohtml files only — no conflict with vscode-go's `gotmpl` registration - TextMate grammar (text.html.gotemplate) embeds text.html.basic and injects Go template keyword/variable/field highlighting - Binary distribution follows gopls pattern: check on activation, prompt user to `go install` if not found on PATH - Build requires only `go install github.com/evanw/esbuild/cmd/esbuild@latest`; npm is only needed for `npx @vscode/vsce package` - esbuild listed as devDependency so vscode:prepublish works via vsce Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Detect go vet vettool probe flags (-V, -flags, -json) and delegate to singlechecker.Main, eliminating the separate cmd/templatecheck binary. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
TestFixtures discovers subdirs under testdata/fixtures/, reads fixture.json for flags/expectations, and runs check-templates against actual Go and .gotmpl files. Covers pass, error, and warning cases. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The go/analysis path only sees what go vet provides — no EmbedFiles, no cross-package deferred resolution — so it gives incomplete coverage and a false sense of security. The standalone binary is the right entry point. The analyzer package is kept for gopls/editor integration. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… Windows
filepath.Match("templates/*.gotmpl", "templates\page.gotmpl") returns false
on Windows because the embedded file path uses backslashes while the pattern
from the Go source literal uses forward slashes. Apply filepath.FromSlash to
the pattern before matching, consistent with embeddedFilesMatchingTemplateNameList.
Verified with new fixtures: cross_pkg_subdir_pass and cross_pkg_subdir_err,
both using templates in a pkg/templates/ subdirectory called from pkg/exec.go.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Builds check-templates for linux/darwin/windows (amd64+arm64) and packages the .vsix, uploading both as GitHub release assets. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add documentation for W005-W007 warnings, printf format validation, Execute support, gopls analyzer, and VS Code extension. Remove PLAN.md now that all planned features are implemented. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The go/analysis framework doesn't provide EmbedFiles or cross-package deferred resolution, so the analyzer gives incomplete coverage compared to the standalone CLI. The VS Code extension already calls the CLI directly, making this package unused dead code. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Go's template parser produces errors like "template: name:line: message" which the VS Code extension regex couldn't match. Rewrite them to "filepath:line:1: message (E001)" so parse errors show up as squiggles. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This branch adds five new static analysis checks, a VS Code extension for in-template diagnostics, and CI for automated releases.
New checks
{{printf "%d" .Name}}where.Nameis a string is now a type error. Format verbs are validated against argument types;%vis always accepted.template.Executesupport — 2-argExecutecalls are now discovered and checked, not justExecuteTemplate.{{$x := .Foo}}where$xis never referenced.{{if true}}...{{else}}unreachable{{end}}and similar literal-constant conditions.{{template "name"}}is called with incompatible data types from different call sites.Improved existing checks
ExecuteTemplatename resolution — call-graph tracing now resolves template names passed through function parameters, covering helper/wrapper patterns.ParseFilesandParseGlobsupport — template initialization viaParseFiles(...)andParseGlob(...)is now traced alongside the existingParseFSsupport.types.Evalapproach withtypesInfo.TypeOffor resolving FuncMap entries.VS Code extension
New
vscode-go-template-check/extension shows diagnostics inside template files (.gohtml,.tmpl,.gotmpl):{{.MissingField}}check-templatesif not foundCI / Release automation
.github/workflows/release.yml— on GitHub release creation, buildscheck-templatesbinaries for linux/darwin/windows (amd64 + arm64) and packages the.vsix, uploading all as release assets.What we didn't ship
go/analysisanalyzer / vettool integration — We built ago/analysis.Analyzerand wired it into the CLI as ago vet -vettool=backend. We reverted and then removed it entirely. Thego/analysisframework doesn't provide access toEmbedFilesor cross-package deferred resolution, so it gives incomplete coverage compared to the standalone CLI — a false sense of security. The VS Code extension calls the CLI directly, which is the correct entry point.Testing
Warning reference
ExecuteTemplatenameTest plan
go test ./...passesgo tool check-templates ./...runs cleanly on a sample project.gohtmlfiles