Warn when a cleanly parsed code file yields no symbols

A code file that parses with zero errors but still produces nothing
beyond its own bare file node is usually a plain data literal the AST
extractor has nothing to model (a const array, a module export of
plain data), not a code file that legitimately has no symbols. Those
two cases looked identical in the output: the file silently entered
the graph as an isolated, zero edge node, and nothing reported it. A
real corpus measured in the issue had 88 such nodes covering roughly
40 percent of the corpus, with not one edge among them.

Add a complementary check next to the existing syntax error warning,
gated on the opposite condition (no parse_errors and no extractor
error) so the two can never double report the same file: a file
already explained by a genuine parse error or a missing optional
dependency is excluded here.

Fixes #3946.

(cherry picked from commit 3b3ee82b1a)
This commit is contained in:
ayushcodes10 authored and safishamsi committed 2026-10-04 02:09:42 +01:00
1 parent e8b5d276a4
commit f81c4e5ec6
1 file changed
+33
+33
View File
@@ -7653,6 +7653,39 @@ def extract(
file=sys.stderr, flush=True,
)
# #3946: a code file that parses CLEANLY (no parse_errors — distinct from
# the #2551/#2599 case just above, where _syntax_error_files already
# explains the loss) but still yields only its own bare file node is
# usually a plain data literal the AST extractor has nothing to model
# (`const BANK = [...]`), not a code file with no symbols. Nothing
# reported this before; the file silently entered the graph as a bare,
# zero-edge node, indistinguishable from a legitimately symbol-free file.
_symbolless_files: list[tuple[str, int]] = []
for i, _p in enumerate(paths):
_res = per_file[i] or {}
if _res.get("parse_errors") or _res.get("error"):
continue
if len(_res.get("nodes", [])) <= 1:
try:
_size = _p.stat().st_size
except OSError:
_size = 0
_symbolless_files.append((os.path.relpath(str(_p), str(root)).replace("\\", "/"), _size))
if _symbolless_files:
_total_bytes = sum(size for _, size in _symbolless_files)
_total_mb = _total_bytes / (1024 * 1024)
_shown_sl = ", ".join(rel for rel, _ in _symbolless_files[:5])
_more_sl = (
f" (+{len(_symbolless_files) - 5} more)"
if len(_symbolless_files) > 5 else ""
)
print(
f" warning: {len(_symbolless_files)} code file(s) yielded no symbols "
f"({_total_mb:.2f} MB); they may be data rather than code: "
f"{_shown_sl}{_more_sl}",
file=sys.stderr, flush=True,
)
for path, result in zip(paths, per_file):
if path.suffix in (".tf", ".tfvars", ".hcl"):
prepare_terraform(result, path, root)