Severity: Low
Summary
Found while investigating #2774 (module-sourced $__loc__ provenance). A
module loaded via include can itself contain its own include directive,
naming a third module. Real jq processes that nested include transitively,
making the third module's defs available to the second module's own body.
succinctly's ModuleLoader does not: it only calls extract_func_defs on a
loaded module's parsed Program::expr, silently dropping that Program's own
includes/imports lists.
Repro
Confirmed live against jq 1.7.1:
$ cat inner.jq
def g: 42;
$ cat outer.jq
include "inner";
def h: g;
$ jq -L . -nc 'include "outer"; h' # 42
$ succinctly jq -L . -nc 'include "outer"; h' # jq: error: g/0 is not defined at <top-level>
Root cause
ModuleLoader::ensure_module_loaded (src/bin/succinctly/jq_runner.rs) does:
let program = jq::parse_program(&contents)...?;
Ok(entry.insert(extract_func_defs(&program.expr)))
jq::parse_program returns a full Program (with its own includes,
imports, and module fields, exactly like the top-level parse), but only
program.expr is inspected. A module's own include/import directives are
parsed successfully and then discarded -- h's reference to g is therefore
genuinely undefined by the time succinctly's resolve pass sees it, since g
was never inlined into outer.jq's own body at all.
Why this is separate from #2774
#2774 is about what path a module-sourced $__loc__ reports once a module
is loaded; this is about a module failing to load its own transitive
dependencies at all, a functional gap rather than a diagnostic-wording one.
Suggested fix direction
ensure_module_loaded/load_module need to recurse: after parsing a
module's Program, resolve and inline that module's own includes (bare
names, merged into its scope) and imports (namespaced) before extracting
its function defs -- essentially the same logic ModuleLoader::process_program
already does for the top-level program, applied recursively to each loaded
module. Watch for cycles (a.jq includes b.jq includes a.jq) -- confirm
what jq itself does there (a compile error, most likely) before choosing a
behavior, and consider loaded_modules' existing per-path cache as a natural
place to detect an in-progress load.
Found via adversarial review of #2774 (PR pending).
Severity: Low
Summary
Found while investigating #2774 (module-sourced
$__loc__provenance). Amodule loaded via
includecan itself contain its ownincludedirective,naming a third module. Real jq processes that nested
includetransitively,making the third module's defs available to the second module's own body.
succinctly's
ModuleLoaderdoes not: it only callsextract_func_defson aloaded module's parsed
Program::expr, silently dropping thatProgram's ownincludes/importslists.Repro
Confirmed live against jq 1.7.1:
Root cause
ModuleLoader::ensure_module_loaded(src/bin/succinctly/jq_runner.rs) does:jq::parse_programreturns a fullProgram(with its ownincludes,imports, andmodulefields, exactly like the top-level parse), but onlyprogram.expris inspected. A module's owninclude/importdirectives areparsed successfully and then discarded --
h's reference togis thereforegenuinely undefined by the time succinctly's resolve pass sees it, since
gwas never inlined into
outer.jq's own body at all.Why this is separate from #2774
#2774 is about what path a module-sourced
$__loc__reports once a moduleis loaded; this is about a module failing to load its own transitive
dependencies at all, a functional gap rather than a diagnostic-wording one.
Suggested fix direction
ensure_module_loaded/load_moduleneed to recurse: after parsing amodule's
Program, resolve and inline that module's ownincludes(barenames, merged into its scope) and
imports(namespaced) before extractingits function defs -- essentially the same logic
ModuleLoader::process_programalready does for the top-level program, applied recursively to each loaded
module. Watch for cycles (
a.jqincludesb.jqincludesa.jq) -- confirmwhat jq itself does there (a compile error, most likely) before choosing a
behavior, and consider
loaded_modules' existing per-path cache as a naturalplace to detect an in-progress load.
Found via adversarial review of #2774 (PR pending).