Fix IllegalArgumentException in CFG builder on dangling top-level preprocessor before a loop - #3981
Conversation
|
@claude review pls |
|
Claude finished @nixel2007's task in 1m 34s —— View job Code Review
|
There was a problem hiding this comment.
Pull request overview
This PR fixes a CFG builder crash caused by treating dangling top-level preprocessor directives as module-opening #Если directives, which could corrupt the block stack when later malformed nested preprocessors appeared inside loops.
Changes:
- Restricts
hasTopLevelPreprocessoractivation to leadingpreproc_if()only. - Adds a regression test for dangling top-level
#Иначеbefore a loop with an unterminated nested#Если.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
src/main/java/com/github/_1c_syntax/bsl/languageserver/cfg/CfgBuildingParseTreeVisitor.java |
Updates top-level preprocessor handling to avoid stale flag leakage from dangling non-#Если directives. |
src/test/java/com/github/_1c_syntax/bsl/languageserver/cfg/ControlFlowGraphBuilderTest.java |
Adds regression coverage ensuring the reported malformed preprocessor/loop case no longer crashes CFG construction. |
A module starting with a dangling preprocessor directive followed by a loop whose body contains an unterminated
#Еслиcrashed CFG construction withIllegalArgumentException: no such vertex in graph: <empty block>(thrown frombuildLoopSubgraph).Root cause
buildGraphsethasTopLevelPreprocessor = truefor any leading module preprocessor directive that the grammar consumes. OnlyvisitPreproc_ifclears that flag, so a dangling#Иначе/#ИначеЕсли/#КонецЕслиleft it set. The stale flag then leaked into a later nested#Если(inside the loop body) that had no matching#КонецЕсли, forcing an unbalanced preprocessor subgraph. This corrupted the block stack and removed the loop-break vertex, so the subsequentFALSE_BRANCHedge referenced a vertex no longer in the graph.Minimal reproducer:
Changes
CfgBuildingParseTreeVisitor.buildGraph: sethasTopLevelPreprocessoronly when the leading preprocessor is an actual#Если(preproc_if() != null); otherwise leave it false. Other directives are still visited (harmless no-ops).ControlFlowGraphBuilderTest: addedtest_danglingTopLevelPreprocessorBeforeLoopShouldNotCrashregression test.