Skip to content

Rust: module-level static and const declarations produce no nodes — 0 of 783 names in a jdx/mise snapshot #3471

Description

@sortakool

Version / environment

  • graphifyy==0.9.57 (PyPI wheel, clean venv)
  • macOS arm64, Python 3.14
  • Also present at the v0.9.57 tag commit 3f82bf7f837a07fb0f7668fbdbd5662801906942

Summary

The Rust extractor emits no graph node for module-level static or const declarations.
Functions, structs, enums, traits and impl methods from the same file are extracted normally,
so the declaration is silently absent rather than obviously broken.

In a mixed-language repository this omits the declaration's provenance and skews query
vocabulary: a constant's name can end up in the graph only from shell scripts or test
fixtures that reference it, never from the Rust that defines it.

Minimal reproduction

tmp="$(mktemp -d)" && mkdir -p "$tmp/repro/src"

cat >"$tmp/repro/Cargo.toml" <<'EOF'
[package]
name = "graphify-rust-bindings-repro"
version = "0.1.0"
edition = "2021"
EOF

cat >"$tmp/repro/src/lib.rs" <<'EOF'
pub static RETRY_LIMIT: usize = 3;
pub const DEFAULT_MODE: &str = "fast";

pub struct Settings { pub mode: String }
pub enum Mode { Fast, Slow }

pub fn retry_limit() -> usize { RETRY_LIMIT }
EOF

python3 -m venv "$tmp/venv"
"$tmp/venv/bin/pip" install -q "graphifyy==0.9.57"
"$tmp/venv/bin/graphify" extract "$tmp/repro" --code-only --force

REPRO_DIR="$tmp/repro" "$tmp/venv/bin/python" <<'PY'
import json, os
from pathlib import Path
g = json.loads((Path(os.environ["REPRO_DIR"])/"graphify-out"/"graph.json").read_text())
labels = {n.get("label") for n in g["nodes"] if (n.get("source_file") or "").endswith(".rs")}
for want in ("RETRY_LIMIT", "DEFAULT_MODE", "Settings", "Mode", "retry_limit()"):
    print(f"{want:16s} {'PRESENT' if want in labels else 'ABSENT'}")
PY

Actual

RETRY_LIMIT      ABSENT
DEFAULT_MODE     ABSENT
Settings         PRESENT
Mode             PRESENT
retry_limit()    PRESENT

Settings, Mode and retry_limit() are the positive controls: the file is parsed and the
other declaration kinds in it are extracted in the same run.

Expected

RETRY_LIMIT and DEFAULT_MODE should each get a node sourced from src/lib.rs, with a
file-level defines (or contains) edge, alongside the struct, enum and function.

Cause

graphify/extractors/rust.py:206
gates every non-function item on:

if t in ("struct_item", "enum_item", "trait_item"):

There is no branch for tree-sitter-rust's static_item or const_item. Grepping the
installed package for either name returns 0 hits (control: struct_item 2, function_item 3,
enum_item 2). extract_rust's own docstring (rust.py:62) states the contract as
"functions, structs, enums, traits, impl methods, and use declarations".

Quantified impact

Measured against a jdx/mise snapshot (tag v2026.9.4, commit 794948606e02cde091653a165291b06cae43c352),
extracted with graphify extract <dir> --code-only --force:

  • 617 Rust files represented in the graph, contributing 17,159 nodes.
  • 879 module-level static/const declaration sites → 783 distinct names.
  • 0 of those 783 names appear as an exact-label node sourced from any Rust file.
  • src/env.rs alone contributed 101 nodes — 99 functions, 1 type, 1 file node — and no
    static/const node, despite 105 static and 14 const declaration lines in it.

The 29 of those names that do appear anywhere in the graph are sourced from shell, Ruby and
fixture files that reference them (e2e/**, benchmarks/**/*.sh, src/assets/bash/activate.sh),
because the Bash extractor does node top-level exported variables
(extractors/bash.py:501-513).
So a query for e.g. MISE_SHIMS_DIR returns test fixtures and not the declaration:

pub(crate) static MISE_LOG_FILE: Lazy<Option<PathBuf>> =
    Lazy::new(|| var_path("MISE_LOG_FILE"));

Prior art

Suggested fix scope

  1. Handle static_item and const_item in graphify/extractors/rust.py, emitting a node
    per declared name plus a file-level edge.
  2. Add Rust extractor tests for module-level pub static and pub const.
  3. Leave initializer call-edges out of scope — Lazy::new(|| var_path("X")) producing no
    calls edge follows from the call harvest walking only function bodies
    (rust.py:431, for caller_nid, body_node in function_bodies:) and belongs to Calls and constant reads in top-level / script context never get a caller node, so they emit no edges (uniform across languages) #1972.

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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions