Skip to content

[BUG][auto] synthetic-main-line-zero: pyscn emits __main__ records with line range 0-0 for module-level scripts #396

@DaisukeYoda

Description

@DaisukeYoda

Summary

pyscn analyze (and the underlying complexity analyzer) emits a function record named __main__ with start_line=0 and end_line=0 for Python files containing module-level executable code (no def main(): and no if __name__ == "__main__": block). Line range 0-0 is structurally impossible — every actual line of source is 1-indexed — so any consumer that opens the file at the reported range gets nothing.

This is mostly hidden because the synthetic __main__ typically lands in the low-risk bucket, but it shows up in the per-function list of every audit that includes scripts with module-level code, and in the juftin/browsr audit it surfaced at high enough complexity to land in the medium-risk list (where the impossible line range would actively mislead a user trying to navigate to it).

Repro (minimal synthetic)

# /tmp/scriptlike.py
import sys

count = 0
for arg in sys.argv[1:]:
    if arg.startswith("--"):
        count += 1
    else:
        count -= 1
print(count)
pyscn analyze --json --no-open /tmp/scriptlike.py

Expected behavior

Either:

  • Don't emit a __main__ record at all for module-level code (the file's "function" is the module itself; alternative: emit it with the actual source span, e.g. start_line=4, end_line=10 covering the executable statements), or
  • Emit it with a structurally valid range matching the spanned source lines.

Actual output (excerpt from rainbow audit)

{
  "Name": "__main__",
  "FilePath": ".pyscn/audit/repos/ledger-donjon-rainbow/examples/HW_analysis/pin_fault.py",
  "StartLine": 0,
  "StartColumn": 0,
  "EndLine": 0,
  "Metrics": {
    "Complexity": 5,
    "CognitiveComplexity": 0,
    "Nodes": ...
  },
  "RiskLevel": "low"
}

The actual file has 99 lines of source (functions setup_emulator at line 19, result at line 33, plus module-level loop at lines 38-99). pyscn correctly emits records for the two real functions; the __main__ entry is the synthesized one and is the only function with a 0-0 range.

Why this is a bug (not just imprecise)

The line range 0-0 is structurally impossible — a start_line == 0 && end_line == 0 record for a non-empty file violates the implicit contract of every other record pyscn emits (all of which use 1-indexed real lines). Tools consuming the JSON (IDEs, code-frame extractors, git blame integrations) will fail or produce empty output when they try to open the cited range. This makes the synthetic record indistinguishable from a real-but-malformed one.

Cross-repo evidence

This pattern was previously observed in:

  • juftin/browsr (audited 2026-04-25) — docs/gen_ref_pages.py:0-0, surfaced in the complexity findings list at medium risk
  • Ledger-Donjon/rainbow (audited 2026-04-27, this issue) — examples/HW_analysis/pin_fault.py:0-0, low risk

In both cases the __main__ record corresponds to module-level code, not a real def __main__ (which doesn't exist as a Python convention anyway — module entry points use if __name__ == "__main__": blocks, not a function literally named __main__).

Suggested fixes

  1. Skip the synthesis: the simplest fix is to not emit a __main__ record at all when the only "function" is module-level code. Cyclomatic complexity at module scope is a known concept but isn't usually called __main__.
  2. Emit a real range: if the metric is genuinely useful (it can be), give it a span that covers the non-function statements (e.g., the smallest enclosing range of all top-level statements outside def/class). That keeps the metric while making the record well-formed.
  3. Rename: if there's some internal reason to keep this record, rename it to something that makes its synthetic origin obvious (<module>, __module__, <top-level>) so consumers can easily filter it.

pyscn version

v1.16.0-14-gf38cdbc

Found via the /pyscn-fp-audit skill in repo Ledger-Donjon/rainbow@f72101a (and previously in juftin/browsr).

Metadata

Metadata

Assignees

No one assigned

    Labels

    auto-filedFiled automatically by the pyscn-fp-audit skill (review before acting)bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions