Skip to content

jq: data imports (import "f" as $d;) are unimplemented — the $ is dropped at parse time, so they resolve as module imports and fail #2956

Description

@newhoggy

Severity: Low

Summary

Found by code review on PR #2954 (#2865). jq has two import forms:

  • import "m" as m; — a module import, binding a namespace of defs (m::f)
  • import "f" as $d; — a data import, reading f.json and binding its
    parsed contents to $d (jq wraps it in an array: [{"z":9}])

succinctly implements only the first. Parser::parse_import reads and then
discards the $, so Import carries no way to tell the two apart, and
ModuleLoader resolves every import as a module — a data import fails with
module not found.

Repro

Confirmed live against jq 1.7.1 and succinctly on main (pre-dates #2865).

$ cat data.json
{"z":9}

$ jq            -L . -nc 'import "data" as $d; $d'
[{"z":9}]

$ succinctly jq -L . -nc 'import "data" as $d; $d'
jq: error: module not found: data

Note jq resolves data.json, not data.jq — the .jq-suffix rule (#2702)
does not apply to a data import, and jq wraps the file's contents in an array.

Root cause

src/jq/parser.rs, parse_import:

let alias = if self.peek() == Some('$') {
    self.next();
    self.parse_ident()?
} else {
    self.parse_ident()?
};

Both arms produce the same bare identifier; Import { path, alias, metadata }
(src/jq/expr.rs) has no field recording which spelling it was. Downstream,
ModuleLoader::process_program calls load_module for every entry in
program.imports.

Suggested fix direction

  1. Add a flag to Import (e.g. data: bool) set by the $ branch of
    parse_import. One construction site, so the change is contained.
  2. In ModuleLoader, route a data import to a JSON read rather than a module
    load: resolve <path>.json against the same search path, parse it, wrap it
    in a one-element array, and bind it as the named $ variable alongside the
    --arg/--argjson bindings run_jq already assembles.
  3. Confirm the details against the oracle first — the .json suffix rule, the
    array wrapping, what happens when the file is missing or is not valid JSON,
    and whether $__loc__/metadata interact.

Why this is separate from #2865

#2865 is transitive include/import processing; it neither introduced nor
fixed this. It does make a module declaring a data import reachable, and
handles that by skipping unresolvable imports in the transitive path (a module
declaring one was harmless before, so making it fatal would have been a
regression) — ModuleLoader::module_resolves. That skip is a
regression-avoidance measure, not data-import support, and should be removed
once this lands.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    SonnetSuitable for a Sonnet-class model to implementbugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions