fix(manifest): treat virtual-workspace-root Cargo.toml as skipped by design (#3910)

This commit is contained in:
Aditya kumar singh committed 2026-09-30 00:34:18 +05:30
1 parent 9fd5aadfd8
commit 89b4e4e147
3 files changed
+26 -5

No files matched your search

+6 -3
View File
@@ -6950,7 +6950,9 @@ def _extract_single_file(args: tuple) -> tuple[int, dict]:
# (e.g. a transient batch/parallel hiccup). Caching it makes the empty
# byte-stable across runs and silently blinds affected/explain to and
# through the file (#1666); skipping the write lets a rerun self-heal.
if not bypass_cache and "error" not in result and result.get("nodes"):
# An intentional decline or virtual workspace root (result.get("skipped"))
# is cached so repeat runs do not re-extract and re-report it (#3910).
if not bypass_cache and "error" not in result and (result.get("nodes") or result.get("skipped")):
save_cached(path, result, root, cache_root=cache_location)
return idx, result
@@ -7152,8 +7154,9 @@ def _extract_sequential(
bypass_cache = path.suffix in _JS_CACHE_BYPASS_SUFFIXES
# XAML boundary anchors on `root` (the corpus), not the cache location.
result = _safe_extract_with_xaml_root(extractor, path, root)
# See _extract_single_file: don't cache an anomalous zero-node result (#1666).
if not bypass_cache and "error" not in result and result.get("nodes"):
# See _extract_single_file: don't cache an anomalous zero-node result (#1666),
# but cache intentional declines/virtual manifests carrying `skipped` (#3910).
if not bypass_cache and "error" not in result and (result.get("nodes") or result.get("skipped")):
save_cached(path, result, root, cache_root=cache_location)
per_file[idx] = result
if total_files >= _PROGRESS_INTERVAL:
+4
View File
@@ -91,6 +91,8 @@ def extract_package_manifest(path: Path) -> dict[str, Any]:
info = _PARSERS[eco](text)
except Exception as exc: # noqa: BLE001 — a malformed manifest must not abort extraction
return {"nodes": [], "edges": [], "error": f"manifest parse error: {exc}"}
if isinstance(info, dict) and info.get("skipped"):
return {"nodes": [], "edges": [], "skipped": info["skipped"]}
if not info or not info.get("name"):
return {"nodes": [], "edges": []}
@@ -237,6 +239,8 @@ def _parse_cargo(text: str) -> dict | None:
# package of its own — emit nothing rather than a fabricated node. ``name`` is
# never workspace-inheritable in Cargo, but guard on the type anyway.
if not isinstance(name, str) or not name:
if isinstance(data.get("workspace"), dict):
return {"name": None, "skipped": "virtual workspace root Cargo.toml"}
return None
# ``version`` may be workspace-inherited (``version.workspace = true``), which
# parses to a table; keep only a concrete string version.
+16 -2
View File
@@ -136,12 +136,26 @@ def test_cargo_parses_name_version_and_deps(tmp_path):
assert {"pkg_serde", "pkg_tokio"} <= deps # inline-table dep keyed by name
def test_cargo_virtual_workspace_manifest_emits_no_package(tmp_path):
def test_cargo_virtual_workspace_manifest_emits_no_package(tmp_path, capsys):
# A virtual workspace root has no [package] table, so it declares no package
# of its own — it must not fabricate a node.
# of its own — it must not fabricate a node, and is skipped by design (#3910).
p = _write(tmp_path / "Cargo.toml", '[workspace]\nmembers = ["a", "b"]\n')
r = extract_package_manifest(p)
assert _pkg_nodes(r) == []
assert r.get("skipped") == "virtual workspace root Cargo.toml"
# In extract(): must not emit zero-node warning, must not be marked failed, and must be cached
result = extract([p], cache_root=tmp_path)
err = capsys.readouterr().err
assert "zero nodes" not in err
assert result.get("failed_sources") == []
# Caching verification: load_cached returns the skipped result
from graphify.cache import load_cached
cached = load_cached(p, tmp_path, cache_root=tmp_path)
assert cached is not None
assert cached.get("skipped") == "virtual workspace root Cargo.toml"
assert cached.get("nodes") == []
def test_cargo_target_conditional_deps_are_collected(tmp_path):