fix(parsers): replace silent skip with regex fallback for large files (closes #163)#168
Closed
Wolfvin wants to merge 1 commit into
Closed
fix(parsers): replace silent skip with regex fallback for large files (closes #163)#168Wolfvin wants to merge 1 commit into
Wolfvin wants to merge 1 commit into
Conversation
…closes #163) Issue #163: files above hardcoded line thresholds (JS > 100, PY > 200) were silently skipped to avoid tree-sitter 0.26 binding SIGSEGV (issue #116). This caused the most complex files in a codebase — the ones most worth analyzing — to be invisible to all downstream engines (complexity, dead-code, smell, entrypoints). On Wolfvin/Regrets ~40% of the codebase was silently dropped. Root cause investigation: tree-sitter 0.26 Python binding has a nondeterministic SIGSEGV on large files. Existing mitigations (BaseParser._last_tree, parse_tree(), _gc.disable()) reduce crash frequency but do not eliminate it. Verified by stress-testing synthetic and real-world files: crashes begin at ~250 lines for JS and ~500 lines for Python. Bug requires binding upgrade (#116). Fix: instead of silently skipping, large files now use the REGEX FALLBACK parser, which gives partial coverage (function declarations + direct calls) instead of zero coverage. The result includes a skipped_from_tree_sitter field so callers know tree-sitter was not used and why. The scan command aggregates all such entries into a top-level skipped_from_tree_sitter list in its JSON output. Additional hardening to JSBackendParser: - Iterative DFS walk replaces recursive _walk (prevents Python stack frames from holding stale Node references across function boundaries). - Iterative DFS in _find_calls_in_scope for the same reason. outline_engine.py: no longer preemptively falls back to regex for JS > 100 lines or Python > 200 lines — attempts tree-sitter first, falls back only on actual parse exception. Thresholds raised: JS 100 -> 250, PY 200 -> 500 (largest values that passed 5 consecutive stress-test runs without SIGSEGV). Tests: tests/test_large_file_fallback.py verifies large files return non-empty nodes + skipped_from_tree_sitter field, small files use tree-sitter without skipped field. All 1380 existing tests still pass (1 pre-existing failure in test_codelensignore unrelated).
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
7 tasks
|
6 tasks
Owner
Author
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Closes #163
Problem
CodeLens silently skipped files above hardcoded line thresholds to avoid a tree-sitter 0.26 binding SIGSEGV (tracked in #116):
{"nodes": [], "edges": []})This caused the most complex files in a codebase — the ones most worth analyzing — to be invisible to all downstream engines (
complexity,dead-code,smell,entrypoints). On Wolfvin/Regrets ~40% of the codebase was silently dropped, including the worst hotspots:scripts/validate.js(2730 lines) → silently returned 0 nodesscripts/validate.py(2361 lines) → silently returned 0 nodesRoot Cause Investigation
The tree-sitter 0.26 Python binding has a nondeterministic SIGSEGV on large files. Existing mitigations (
BaseParser._last_tree,parse_tree(),_gc.disable()) reduce crash frequency but do not eliminate it. Verified by stress-testing:scripts/validate.js(2730 lines) → SIGSEGV on every attemptscripts/validate.py(2361 lines) → SIGSEGV on every attemptThe bug cannot be fixed from Python — it requires a binding upgrade (tracked in #116).
Fix
Instead of silently skipping, large files now use the REGEX FALLBACK parser (
parse_js_backend_fallback/parse_python_fallback), which gives partial coverage (function declarations + direct calls) instead of zero coverage. The result includes askipped_from_tree_sitterfield so callers know tree-sitter was not used and why.The scan command aggregates all such entries into a top-level
skipped_from_tree_sitterlist in its JSON output.Additional hardening to
JSBackendParser_walk(prevents Python stack frames from holding stale Node references across function boundaries — reduces crash frequency on smaller files)._find_calls_in_scopefor the same reason.outline_engine.pyNo longer preemptively falls back to regex for JS > 100 lines or Python > 200 lines — attempts tree-sitter first, falls back only on actual parse exception.
Thresholds raised
Definition of Done Status
_last_treefix applied uniformly across all parser classes (verified — already inbase_parser.py)skipped_from_tree_sitterlist in JSON outputcomplexityon Wolfvin/Regrets now detectsvalidate.jsandvalidate.py(via regex fallback — partial coverage instead of zero)test_codelensignore::TestBackwardCompat::test_actual_target_dir_is_ignoredunrelated to this change)Files Changed
scripts/parsers/js_backend_parser.py— threshold + regex fallback + iterative walkscripts/parsers/python_parser.py— threshold + regex fallbackscripts/outline_engine.py— remove preemptive threshold; fall back only on actual exceptionscripts/commands/scan.py— aggregateskipped_from_tree_sitterinto top-level output fieldtests/test_large_file_fallback.py— new tests verifying fallback behaviorCHANGELOG.md— entry documenting the fixTests
Findings
The tree-sitter 0.26 Python binding has a fundamental SIGSEGV bug on large files that cannot be fully mitigated from Python. The original
MAX_SAFE_JS_LINES = 100/MAX_SAFE_PY_LINES = 200thresholds were conservative but necessary. This PR raises them as far as safely possible and replaces silent skip with explicit fallback + reporting. A permanent fix requires upgrading the tree-sitter binding (issue #116).