Skip to main content

plan_for_node

Function plan_for_node 

Source
pub fn plan_for_node<N>(
    node: &N,
    recursive: bool,
    source_id: u32,
    offset: u32,
) -> Result<Option<Rc<GroupPlan>>, NormalizeError>
where N: HasEntry,
Expand description

The plan for one binder node, computed ON DEMAND and memoized.

★ Replaces a parse-door walk that planned every binder in every parsed file. Laziness means most of those are never evaluated, so that work was mostly discarded — measured as a ~4% wall-clock tax on a real nixpkgs eval. Planning at first evaluation is identical in result (a group’s plan depends only on its own entries) and pays only for groups that are reached.

The memo is keyed exactly as before, so a plan computed for a node at offset o in one parse tree is never read for a different (imported) tree with a binder at the same offset.

A None return is a POSITIVE statement — the group has no duplicate static key and no dotted path, so the caller’s existing path is already correct. See the module docs on why that is not a fallback.

§Errors

NormalizeError for a group nix itself rejects — a duplicate attribute ({ a = 1; a = 2; }) or a duplicate formal.

★ This used to swallow that error with .ok().flatten(), which silently turned a rejection into “no plan” and sent the group down the entry loop. The result was accepting what nix refuses, and — worse — accepting it DIFFERENTLY from the bytecode VM: measured 2026-08-18, { a = 1; a = 2; } is { a = 2; } on the walker and { a = 1; } on the VM, both at exit 0 where nix exits 1. Neither answer is right, so no choice of winner reconciles the engines; only refusing does.