Skip to content

Add Template::FindUndeclaredVariables (0236) - #603

Merged
rmorozov merged 2 commits into
masterfrom
claude/project-thread-tff1um
Oct 19, 2026
Merged

rmorozov merged 2 commits into
masterfrom
claude/project-thread-tff1um

Conversation

@rmorozov

@rmorozov rmorozov commented Oct 19, 2026 •

Copy link
Copy Markdown
Member

Requested by Ruslan · project thread

Before: a host could not ask a loaded template which variables it needs; jinja2.meta.find_undeclared_variables had no counterpart (parity.md ❌), and GetMetadata only reads the C++-specific {% meta %} block. Trans blocks' message names ({% trans %}{{ user }}{% endtrans %}) and bare trans variables ({% trans user %}) were also invisible to the name resolver, so they could render from the wrong scope when the same name was a loop target or macro argument elsewhere.

After: Template::FindUndeclaredVariables() / TemplateW::... returns Result<std::vector<std::string>, CharT> with the sorted names Jinja2's meta.find_undeclared_variables reports, without the built-in and environment globals; TemplateNotParsed before a successful Load. Trans names render from the right scope (the verifier's differential fuzz against Python: 0 differences on 12k trans-heavy templates, where master differed on ~110).

How: the call parses the kept source again into an arena of its own and asks that parse's NameResolver (new UndeclaredNames/IsDeclared/DeclaresAtEntry over a DeclarationIndex of each name's first store per frame and the frames that read a macro's special names, so the check is linear), whose frames already mirror Jinja2's idtracking (0117, 0168), so Load pays nothing (instruction gate: max +1.3%, Load only). A name counts when it is read, or stored in an if branch, with no earlier store in its frame, no store in an enclosing frame (a block sees only its own), and no binder (loop targets and loop, macro/call-block arguments with caller/varargs/kwargs, a default's own macro arguments, and the specials only when the body reads them, self, a block's super). Parser changes: trans message names and bare trans variables are AddUse reads; a set block in a loop's else stores like a plain set there; a set block's filters read in the block's frame, as in Jinja2; imported names besides their alias use a new StoreKind::Hidden (render-neutral). Includes, imports and extends parents are not followed, as in Jinja2.

Divergences (in docs/tasks/0236): gettext/ngettext are never reported (they are globals whenever the i18n extension is on, as with Jinja2's installed callables); {% autoescape e %} reads e in the enclosing frame; Jinja2's synthetic _trans is not reported; {% set x | f(a) %} with no later store of a renders and reports a, where Jinja2 3.1's compiler raises an AssertionError. Template.module is filed as task 0284.

Tests: test/undeclared_variables_test.cpp, 81 templates checked against Jinja2 3.1 (do, loopcontrols, i18n with null translations), narrow and wide, plus globals, not-loaded, trans and set-block-filter render rows.

Roles: main session; verifier (3 rounds: round 1 found a trans render regression for bare variables and a loop-else set-block false positive, round 2 a wrong rule for special names in macro defaults, round 3 one block-scoping edge; all fixed, with differential fuzz against Python and asan+ubsan clean). Codex: 2 findings, 2 fixed (the filter-block half of one declined: Jinja2 reads filter-block arguments in the enclosing frame). Red pushes: none so far.


🤖 Generated with Claude Code

https://claude.ai/code/session_01XALEgQvXdN6pfE79XkgggV

jinja2.meta.find_undeclared_variables for Jinja2C++: the source is
parsed again and the parse's NameResolver reports the names read or
stored in a branch that no store, frame around them or binder declares,
without the environment's globals. Trans variables and message names
are now reads of the resolver too, a set block in a loop's else stores
in the else's frame, and imported names besides their alias get a store
kind of their own. Template.module is filed as 0284.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XALEgQvXdN6pfE79XkgggV
@rmorozov rmorozov self-assigned this Oct 19, 2026

Copy link
Copy Markdown
Member Author

@codex review
@codex security review


Generated by Claude Code

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 19, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-19T01:13:14.421140Z a54c8a4 Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5a0c9e095c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/template_impl.h
Comment thread src/name_resolver.cpp Outdated
Codex review of #603: replace per-read scans of a frame's stores with an
index of each name's first store per frame and of the frames that read a
macro's special names, so the check is linear (5,000 sets: 0.5 s -> 29 ms
in Debug). A set block's filters now read in the block's frame, so a later
store in it hides the outer name as in Jinja2.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XALEgQvXdN6pfE79XkgggV

Copy link
Copy Markdown
Member Author

@codex review

@codex security review

a54c8a4 addresses both earlier findings: declaration lookup is now indexed (linear), and set-block filters read in the block's frame. The diff touches the template parser.


Generated by Claude Code

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. You're on a roll.

Reviewed commit: a54c8a49ac

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Copy link
Copy Markdown
Member Author

@codex security review


Generated by Claude Code

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. You're on a roll.

Reviewed commit: a54c8a49ac

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@rmorozov
rmorozov marked this pull request as ready for review October 19, 2026 02:32
@rmorozov rmorozov mentioned this pull request Oct 19, 2026
@rmorozov
rmorozov merged commit 8632497 into master Oct 19, 2026
41 checks passed
rmorozov added a commit that referenced this pull request Oct 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants