Repository navigation
fix(roslyn): a deep if/else-if chain no longer aborts the whole extraction (#401) - #403
Merged
Merged
Conversation
…ction (#401) The flow pass lowers each `if` to {op, then[], else[]}, so every `else if` costs two JSON levels, and the facts document was serialised with System.Text.Json's default MaxDepth of 64. One method with a tracked disposable local and 31 conditions threw at the final serialise: no facts, no SARIF, every other method of the repository unanalysed. - Serialise facts and heap effects with MaxDepth 128, the cap spec/OwnIR.md section 4.2 is measured against, so every document within the spec writes. - Skip a method whose lowered body nests deeper than the section 4.2 limit (32) as unmodelled and count it: both doors reject such a document outright, so raising MaxDepth alone would only move the failure to the core. tests/test_extractor_deep_flow.py (wired into the wpf-extractor CI job): chains of 31/32 stay analysed with exactly n levels, 33/200 are skipped and counted, the core accepts every document, and a sibling leak is still reported. Red on the old extractor for all four lengths; mutants with a limit of 30 or no skip are caught. Facts are byte-identical to the previous build on the repository samples and two large real trees. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…#401) Review of #403 asked whether the new recursive depth walk could overflow. It never runs that deep, but the existing LowerFlowStmt recursion does: somewhere between 300 and 600 `else if` it overflowed the stack, an uncatchable crash that lost the whole run on both the old and the new extractor. LowerFlowStmt now checks RuntimeHelpers.TryEnsureSufficientExecutionStack and bails the method as unmodelled; such a body is far deeper than the section 4.2 limit and was going to be skipped anyway. The regression adds n = 1000 (red on a75a024: stack overflow, rc 0xC00000FD). Facts stay byte-identical to e7d33e3 on the samples and three large real trees. Chains of a few thousand conditions are rejected by Roslyn's own nesting guard before the extractor sees them (no crash, but the file yields no methods); that coverage loss is not addressed here. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The wpf-extractor job runs tests/test_extractor_deep_flow.py on Linux only, and the stack guard it pins (RuntimeHelpers.TryEnsureSufficientExecutionStack) depends on the platform thread stack. Add one step, Windows-only, to the existing stage1-engine matrix job, which already has .NET 8 and Python. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 18, 2026
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.
Что и зачем
Один метод с отслеживаемым disposable-локалом и цепочкой из 31 и более условий
if / else ifпри--flow-localsронял весь экстрактор:JsonException, MaxDepth 64 наProgram.cs:7340. Без фактов оставался весь репозиторий (#401: couchbase-net-client, dnSpy).Исправление делает две вещи:
MaxDepth = 128— это предел, от которого отсчитан spec/OwnIR.md §4.2;methods_skipped_unmodelled. Оба ядра отклоняют такой документ целиком, поэтому одного подъёма MaxDepth было бы мало: падение переехало бы из экстрактора в ядро.Покрытие в пределах спецификации не теряется: методы с вложенностью до 32 включительно по-прежнему анализируются.
Тип изменения
Как проверено
python tests/run_tests.py— локально полностью не проходит из-за окружения (Windows:test_checkpoint_status→FileNotFoundError; WSL на/mnt/c: нет git-checkout иcargo). Изменение Python-набор не затрагивает.ruff check .— чисто;mypy— 11 ошибок вscripts/perf_baseline.pyиscripts/step7/envcapture.py, те же наe7d33e33.OWN_TIERB_REQUIRED=1 python tests/test_extractor_deep_flow.py:own-cli ownir, release) принимает документы для 31/32/33/200, соседняя утечка находится. Отрицательный контроль — тело, вручную углублённое до 33, — отклоняется: «function body nested deeper than 32 levels».e7d33e33наfrontend/roslyn/samples,protocol-samples,heap-effects-samplesи двух больших реальных деревьях, в режимах--flow-localsи plain.OWN_TIERB_REQUIRED=1:test_extractor_columns,test_guarded_consume,test_certify,test_ownir,test_p037_sidecar,test_p037_evidence(на чистом дереве),test_step7_mergegate_ci,test_stage3_surfaces,test_training_prereg.check_fix_candidates_factsиcheck_weak_subscribe_factsдают rc=2 одинаково на базе и на ветке.Связанные issue
Closes #401
Чеклист
feat:,fix:,docs:…)Границы исправления
TryEnsureSufficientExecutionStackсрабатывает в зависимости от размера стека.InsufficientExecutionStackExceptionна ~2300–2900 условиях и тихая потеря файла на 3000+. Они вынесены в Очень глубокая цепочка if/else if: Roslyn роняет прогон (InsufficientExecutionStackException, ~2300–2900) или тихо теряет файл (3000+) #404 и этим PR не исправляются.wpf-extractor) и на Windows (stage1-engine, шаг только для Windows).Вне скоупа: деревья событий protocol-регионов ограничены тем же лимитом 32, но здесь не пропускаются. Сверх лимита там остаётся отказ — это стоит отдельной задачи.
🤖 Generated with Claude Code