From 753ff6339f2197742b802e5962523b9828a81327 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 23 Oct 2025 14:51:03 +0000 Subject: [PATCH 1/6] Initial plan From 3f392459198a29b1fc6d034671569f18d897972f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 23 Oct 2025 15:06:46 +0000 Subject: [PATCH 2/6] Add test to reproduce CFG building error with empty string followed by dot Co-authored-by: nixel2007 <1132840+nixel2007@users.noreply.github.com> --- .../cfg/CfgEmptyStringDotTest.java | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 src/test/java/com/github/_1c_syntax/bsl/languageserver/cfg/CfgEmptyStringDotTest.java diff --git a/src/test/java/com/github/_1c_syntax/bsl/languageserver/cfg/CfgEmptyStringDotTest.java b/src/test/java/com/github/_1c_syntax/bsl/languageserver/cfg/CfgEmptyStringDotTest.java new file mode 100644 index 00000000000..ff770c31892 --- /dev/null +++ b/src/test/java/com/github/_1c_syntax/bsl/languageserver/cfg/CfgEmptyStringDotTest.java @@ -0,0 +1,40 @@ +package com.github._1c_syntax.bsl.languageserver.cfg; + +import com.github._1c_syntax.bsl.languageserver.util.TestUtils; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +import static org.assertj.core.api.Assertions.assertThat; + +@SpringBootTest +class CfgEmptyStringDotTest { + + @Test + void testEmptyStringWithDotShouldNotCrash() { + var code = """ + Процедура РаспределитьНаТовары(Команда) + + Если Товар.ТаможеннаяСтоимость = "". Тогда + + КонецЕсли; + + КонецПроцедуры + """; + + var dContext = TestUtils.getDocumentContext(code); + var parseTree = dContext.getAst().subs().sub(0).procedure().subCodeBlock().codeBlock(); + + var builder = new CfgBuildingParseTreeVisitor(); + builder.producePreprocessorConditions(true); + builder.produceLoopIterations(false); + builder.determineAdjacentDeadCode(false); + + // This should not crash + var graph = builder.buildGraph(parseTree); + + // Basic validation that the graph was created + assertThat(graph).isNotNull(); + assertThat(graph.getEntryPoint()).isNotNull(); + assertThat(graph.getExitPoint()).isNotNull(); + } +} From dabe0f23b60e3cf2100c58e13d0173af4bdc73d1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 23 Oct 2025 15:08:53 +0000 Subject: [PATCH 3/6] Fix CFG building error when codeBlock is null in if/elsif/else branches Co-authored-by: nixel2007 <1132840+nixel2007@users.noreply.github.com> --- .../cfg/CfgBuildingParseTreeVisitor.java | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/github/_1c_syntax/bsl/languageserver/cfg/CfgBuildingParseTreeVisitor.java b/src/main/java/com/github/_1c_syntax/bsl/languageserver/cfg/CfgBuildingParseTreeVisitor.java index e4f4e2110c7..ee5dee64821 100644 --- a/src/main/java/com/github/_1c_syntax/bsl/languageserver/cfg/CfgBuildingParseTreeVisitor.java +++ b/src/main/java/com/github/_1c_syntax/bsl/languageserver/cfg/CfgBuildingParseTreeVisitor.java @@ -136,6 +136,7 @@ public ParseTree visitIfStatement(BSLParser.IfStatementContext ctx) { } var truePart = blocks.leaveBlock(); + graph.addVertex(truePart.begin()); graph.addEdge(conditionStatement, truePart.begin(), CfgEdgeType.TRUE_BRANCH); currentLevelBlock.getBuildParts().push(truePart.end()); currentLevelBlock.getBuildParts().push(conditionStatement); @@ -198,9 +199,12 @@ public ParseTree visitElsifBranch(BSLParser.ElsifBranchContext ctx) { // тело true blocks.enterBlock(); - ctx.codeBlock().accept(this); + if (ctx.codeBlock() != null) { + ctx.codeBlock().accept(this); + } var truePart = blocks.leaveBlock(); + graph.addVertex(truePart.begin()); graph.addEdge(condition, truePart.begin(), CfgEdgeType.TRUE_BRANCH); blocks.getCurrentBlock().getBuildParts().push(truePart.end()); blocks.getCurrentBlock().getBuildParts().push(condition); @@ -218,11 +222,14 @@ public ParseTree visitCodeBlock(BSLParser.CodeBlockContext ctx) { @Override public ParseTree visitElseBranch(BSLParser.ElseBranchContext ctx) { blocks.enterBlock(); - ctx.codeBlock().accept(this); + if (ctx.codeBlock() != null) { + ctx.codeBlock().accept(this); + } var block = blocks.leaveBlock(); // на стеке находится условие var condition = blocks.getCurrentBlock().getBuildParts().pop(); + graph.addVertex(block.begin()); graph.addEdge(condition, block.begin(), CfgEdgeType.FALSE_BRANCH); blocks.getCurrentBlock().getBuildParts().push(block.end()); From 8fdede25f0353aa36213e5218b7633b627720a84 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 23 Oct 2025 15:17:57 +0000 Subject: [PATCH 4/6] Add comprehensive tests for empty code blocks in if/elsif/else branches Co-authored-by: nixel2007 <1132840+nixel2007@users.noreply.github.com> --- .../cfg/CfgEmptyStringDotTest.java | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/src/test/java/com/github/_1c_syntax/bsl/languageserver/cfg/CfgEmptyStringDotTest.java b/src/test/java/com/github/_1c_syntax/bsl/languageserver/cfg/CfgEmptyStringDotTest.java index ff770c31892..b621dee1d01 100644 --- a/src/test/java/com/github/_1c_syntax/bsl/languageserver/cfg/CfgEmptyStringDotTest.java +++ b/src/test/java/com/github/_1c_syntax/bsl/languageserver/cfg/CfgEmptyStringDotTest.java @@ -37,4 +37,66 @@ void testEmptyStringWithDotShouldNotCrash() { assertThat(graph.getEntryPoint()).isNotNull(); assertThat(graph.getExitPoint()).isNotNull(); } + + @Test + void testEmptyElsifBranchShouldNotCrash() { + var code = """ + Процедура Тест() + + Если Условие1 Тогда + Действие1(); + ИначеЕсли Условие2 = "". Тогда + + КонецЕсли; + + КонецПроцедуры + """; + + var dContext = TestUtils.getDocumentContext(code); + var parseTree = dContext.getAst().subs().sub(0).procedure().subCodeBlock().codeBlock(); + + var builder = new CfgBuildingParseTreeVisitor(); + builder.producePreprocessorConditions(true); + builder.produceLoopIterations(false); + builder.determineAdjacentDeadCode(false); + + // This should not crash + var graph = builder.buildGraph(parseTree); + + // Basic validation that the graph was created + assertThat(graph).isNotNull(); + assertThat(graph.getEntryPoint()).isNotNull(); + assertThat(graph.getExitPoint()).isNotNull(); + } + + @Test + void testEmptyElseBranchShouldNotCrash() { + var code = """ + Процедура Тест() + + Если Условие1 Тогда + Действие1(); + Иначе + + КонецЕсли; + + КонецПроцедуры + """; + + var dContext = TestUtils.getDocumentContext(code); + var parseTree = dContext.getAst().subs().sub(0).procedure().subCodeBlock().codeBlock(); + + var builder = new CfgBuildingParseTreeVisitor(); + builder.producePreprocessorConditions(true); + builder.produceLoopIterations(false); + builder.determineAdjacentDeadCode(false); + + // This should not crash + var graph = builder.buildGraph(parseTree); + + // Basic validation that the graph was created + assertThat(graph).isNotNull(); + assertThat(graph.getEntryPoint()).isNotNull(); + assertThat(graph.getExitPoint()).isNotNull(); + } } From 8142edded8b5dfb65f86bd299d8315b8fb5af9a1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 23 Oct 2025 15:20:07 +0000 Subject: [PATCH 5/6] Add test for exact issue scenario with empty string followed by dot Co-authored-by: nixel2007 <1132840+nixel2007@users.noreply.github.com> --- .../cfg/CfgEmptyStringDotTest.java | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/test/java/com/github/_1c_syntax/bsl/languageserver/cfg/CfgEmptyStringDotTest.java b/src/test/java/com/github/_1c_syntax/bsl/languageserver/cfg/CfgEmptyStringDotTest.java index b621dee1d01..4d9642de37b 100644 --- a/src/test/java/com/github/_1c_syntax/bsl/languageserver/cfg/CfgEmptyStringDotTest.java +++ b/src/test/java/com/github/_1c_syntax/bsl/languageserver/cfg/CfgEmptyStringDotTest.java @@ -99,4 +99,34 @@ void testEmptyElseBranchShouldNotCrash() { assertThat(graph.getEntryPoint()).isNotNull(); assertThat(graph.getExitPoint()).isNotNull(); } + + @Test + void testOriginalIssueCase() { + // This is the exact code from the issue report + var code = """ + Процедура Тест() + + Если Товар.ТаможеннаяСтоимость = "". Тогда + + КонецЕсли; + + КонецПроцедуры + """; + + var dContext = TestUtils.getDocumentContext(code); + var parseTree = dContext.getAst().subs().sub(0).procedure().subCodeBlock().codeBlock(); + + var builder = new CfgBuildingParseTreeVisitor(); + builder.producePreprocessorConditions(true); + builder.produceLoopIterations(false); + builder.determineAdjacentDeadCode(false); + + // This should not crash (was crashing before the fix) + var graph = builder.buildGraph(parseTree); + + // Basic validation that the graph was created + assertThat(graph).isNotNull(); + assertThat(graph.getEntryPoint()).isNotNull(); + assertThat(graph.getExitPoint()).isNotNull(); + } } From 7075fd26736374f96d65e7c12887c5a7d5ade012 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 25 Oct 2025 08:08:29 +0000 Subject: [PATCH 6/6] Use Trees.nodeContainsErrors() to check for parse errors in conditional branches Instead of just checking for null codeBlock, now also check if the branch node contains parse errors using Trees.nodeContainsErrors(). This is more robust and semantically correct - explicitly checking for parse errors rather than relying on null checks alone. Co-authored-by: nixel2007 <1132840+nixel2007@users.noreply.github.com> --- .../bsl/languageserver/cfg/CfgBuildingParseTreeVisitor.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/github/_1c_syntax/bsl/languageserver/cfg/CfgBuildingParseTreeVisitor.java b/src/main/java/com/github/_1c_syntax/bsl/languageserver/cfg/CfgBuildingParseTreeVisitor.java index ee5dee64821..e27581ab510 100644 --- a/src/main/java/com/github/_1c_syntax/bsl/languageserver/cfg/CfgBuildingParseTreeVisitor.java +++ b/src/main/java/com/github/_1c_syntax/bsl/languageserver/cfg/CfgBuildingParseTreeVisitor.java @@ -131,7 +131,7 @@ public ParseTree visitIfStatement(BSLParser.IfStatementContext ctx) { // тело true blocks.enterBlock(); - if (ctx.ifBranch().codeBlock() != null) { + if (ctx.ifBranch().codeBlock() != null && !Trees.nodeContainsErrors(ctx.ifBranch())) { ctx.ifBranch().codeBlock().accept(this); } var truePart = blocks.leaveBlock(); @@ -199,7 +199,7 @@ public ParseTree visitElsifBranch(BSLParser.ElsifBranchContext ctx) { // тело true blocks.enterBlock(); - if (ctx.codeBlock() != null) { + if (ctx.codeBlock() != null && !Trees.nodeContainsErrors(ctx)) { ctx.codeBlock().accept(this); } var truePart = blocks.leaveBlock(); @@ -222,7 +222,7 @@ public ParseTree visitCodeBlock(BSLParser.CodeBlockContext ctx) { @Override public ParseTree visitElseBranch(BSLParser.ElseBranchContext ctx) { blocks.enterBlock(); - if (ctx.codeBlock() != null) { + if (ctx.codeBlock() != null && !Trees.nodeContainsErrors(ctx)) { ctx.codeBlock().accept(this); } var block = blocks.leaveBlock();