Убирает заплатку, сделанную для #1774#3502
Conversation
|
Note Other AI code review bot(s) detectedCodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review. WalkthroughДобавлены проверки и защита при соединении вершин CFG (включая запрет исходящих рёбер у ExitVertex), введено исключение FlowGraphLinkException, расширен API ControlFlowGraph (новые addEdge/edgePresentation), улучшена обработка верхнеуровневых препроцессоров в построителе CFG, скорректирован обход графа и обновлены тесты/диагностика. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor Builder as CFG Builder
participant CFG as ControlFlowGraph
participant Src as CfgVertex (source)
participant Super as GraphSuper
participant Ex as FlowGraphLinkException
Builder->>CFG: addEdge(source,target,edge)
CFG->>Src: onConnectOutgoing(graph,target,edge)
alt Первое соединение узла
Src-->>CFG: пометка connected, return
else Повторное соединение
Src->>Src: проверка существующих outgoing ребер
alt Найден дубликат типа
Src-->>CFG: throw FlowGraphLinkException
CFG-->>Builder: exception
else Нет дубликата
Src-->>CFG: return
end
end
CFG->>Super: super.addEdge(source,target,edge)
Super-->>CFG: результат
CFG-->>Builder: результат
sequenceDiagram
autonumber
participant Visitor as CfgBuildingParseTreeVisitor
participant Trees as Trees
participant Blocks as BlockStack
participant CFG as ControlFlowGraph
Visitor->>Trees: найти предыдущий узел (top-level preproc?)
alt Top-level preproc найден
Visitor->>Visitor: hasTopLevelPreprocessor = true
Visitor->>Visitor: visit preproc_if первым
Visitor->>Blocks: enterBlock() для truePart
Visitor->>CFG: addEdge(condition -> truePart.begin, TRUE_BRANCH)
Visitor->>Visitor: пометить conditionVertex в truePart.buildParts
Visitor->>Visitor: при endif — склеить хвосты, при необходимости добавить FALSE_BRANCH
else Не найден
Visitor->>Visitor: обычная обработка тела модуля
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Suggested reviewers
Poem
Tip 🔌 Remote MCP (Model Context Protocol) integration is now available!Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats. 📜 Recent review detailsConfiguration used: CodeRabbit UI 💡 Knowledge Base configuration:
You can enable these sources in your CodeRabbit configuration. 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (12)
✨ Finishing Touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (13)
src/main/java/com/github/_1c_syntax/bsl/languageserver/cfg/FlowGraphLinkException.java (1)
24-28: Добавьте serialVersionUID и конструкторы с cause для улучшения диагностикиМногие анализаторы (SpotBugs/IDEA) предупреждают об отсутствующем serialVersionUID у Serializable-исключений; дополнительные конструкторы упростят проброс первопричины.
public class FlowGraphLinkException extends RuntimeException { + private static final long serialVersionUID = 1L; public FlowGraphLinkException(String message) { super(message); } + public FlowGraphLinkException(String message, Throwable cause) { + super(message, cause); + } + public FlowGraphLinkException(Throwable cause) { + super(cause); + } }src/main/java/com/github/_1c_syntax/bsl/languageserver/cfg/BasicBlockVertex.java (1)
51-57: Nit: обрамите if фигурными скобками для единообразия и безопасности будущих правокСтиль кода в файле преимущественно со скобками; так меньше шансов на ошибку при добавлении строк.
@Override public String toString() { - if (statements.isEmpty()) - return "<empty block>"; + if (statements.isEmpty()) { + return "<empty block>"; + } return super.toString(); }src/main/java/com/github/_1c_syntax/bsl/languageserver/cfg/ConditionalVertex.java (1)
57-65: Проверяйте тип ребра до вызова super.onConnectOutgoing — fail-fast и отсутствие побочных эффектовТак мы раньше отфильтруем недопустимые типы и избежим лишней работы/побочных эффектов базовой реализации.
@Override protected void onConnectOutgoing(ControlFlowGraph graph, CfgVertex target, CfgEdge edge) { - super.onConnectOutgoing(graph, target, edge); - - if (edge.getType() != CfgEdgeType.TRUE_BRANCH && edge.getType() != CfgEdgeType.FALSE_BRANCH) { + if (edge.getType() != CfgEdgeType.TRUE_BRANCH && edge.getType() != CfgEdgeType.FALSE_BRANCH) { throw new FlowGraphLinkException("Can't add edge " + this + "->"+target + "\n" +"Edge type " + edge.getType() + " is forbidden here."); } + super.onConnectOutgoing(graph, target, edge); }src/main/java/com/github/_1c_syntax/bsl/languageserver/cfg/ExitVertex.java (1)
25-28: Сделайте сообщение исключения более информативным (тип ребра и целевая вершина)Это облегчит диагностику при нарушении инварианта.
@Override protected void onConnectOutgoing(ControlFlowGraph graph, CfgVertex target, CfgEdge edge) { - throw new FlowGraphLinkException("ExitNode can't have outgoing edges"); + throw new FlowGraphLinkException( + "ExitVertex can't have outgoing edges: attempted " + edge.getType() + " to " + target + ); }src/main/java/com/github/_1c_syntax/bsl/languageserver/cfg/ControlFlowGraphWalker.java (1)
48-50: Унифицировать проверку уникальности с walkNext(edgeType)Сейчас множественность проверяется только для DIRECT; имеет смысл так же контролировать уникальность для произвольного типа в
walkNext(CfgEdgeType), либо вынести общую логику в приватный хелпер.Набросок приватного хелпера и использование в обоих методах:
private Optional<CfgEdge> uniqueEdgeByType(CfgEdgeType edgeType) { return availableRoutes().stream() .filter(x -> x.getType() == edgeType) .reduce((a, b) -> { throw new IllegalStateException("Multiple " + edgeType + " outgoing edges in " + currentNode); }); } public CfgEdge walkNext() { var edgeOrNot = uniqueEdgeByType(CfgEdgeType.DIRECT); if (edgeOrNot.isPresent()) { currentNode = graph.getEdgeTarget(edgeOrNot.get()); return edgeOrNot.get(); } throw new IllegalStateException("DIRECT edge is not found for node " + currentNode); } public CfgEdge walkNext(CfgEdgeType edgeType) { var edgeOrNot = uniqueEdgeByType(edgeType); if (edgeOrNot.isPresent()) { currentNode = graph.getEdgeTarget(edgeOrNot.get()); return edgeOrNot.get(); } throw new IllegalStateException("Edge is not found for node " + currentNode); }src/main/java/com/github/_1c_syntax/bsl/languageserver/diagnostics/AllFunctionPathMustHaveReturnDiagnostic.java (1)
120-125: Избегайте Collector с поставщиком уже существующего спискаПередача поставщика, возвращающего уже созданный список, нарушает контракт Collector и может повести себя неожиданно при изменениях режима выполнения стрима. Проще и безопаснее использовать forEach/addAll.
Предлагаю заменить на прямое добавление:
- incomingVertices.stream() - .map(vertex -> RelatedInformation.create(documentContext.getUri(), - Ranges.create(vertex), - info.getMessage())) - .collect(Collectors.toCollection(() -> listOfMessages)); + incomingVertices.forEach(vertex -> + listOfMessages.add( + RelatedInformation.create(documentContext.getUri(), Ranges.create(vertex), info.getMessage()) + ) + );src/main/java/com/github/_1c_syntax/bsl/languageserver/cfg/CfgVertex.java (2)
30-31: Состояние isConnected — ок как микро-оптимизация, но проверьте жизненный цикл вершинФлаг позволяет избежать первого обращения к графу, но делает поведение зависящим от жизненного цикла вершины (повторное использование объекта вершины в другом графе теоретически может повлиять). Если такие переиспользования исключены конвенцией — ок; иначе стоит пересмотреть на graph.outgoingEdgesOf(this).isEmpty().
Хотите, подготовлю проверку по коду на предмет переиспользования экземпляров CfgVertex между графами?
59-62: Нит: метод duplicateLinkError бросает исключение сам и при этом используется в выражении throwСейчас duplicateLinkError сам делает throw, а снаружи используется как throw duplicateLinkError(...). Это нетипично и сбивает с толку. Лучше пусть метод возвращает исключение, а бросание остается в месте вызова.
- private FlowGraphLinkException duplicateLinkError(ControlFlowGraph graph, CfgVertex target, CfgEdge edge) { - throw new FlowGraphLinkException("Can't add edge " + this + "->"+target + "\n" - +"Source vertex " + this + " already has "+edge.getType()+" edge " + graph.edgePresentation(edge)); - } + private FlowGraphLinkException duplicateLinkError(ControlFlowGraph graph, CfgVertex target, CfgEdge edge) { + return new FlowGraphLinkException( + "Can't add edge " + this + "->" + target + "\n" + + "Source vertex " + this + " already has " + edge.getType() + " edge " + graph.edgePresentation(edge) + ); + }src/main/java/com/github/_1c_syntax/bsl/languageserver/cfg/ControlFlowGraph.java (1)
42-44: Нит: несоответствие возвращаемых типов addEdge-обертокСейчас есть void-обертка addEdge(source, target, CfgEdgeType), которая игнорирует boolean-результат базового addEdge. Это консистентно со старым API, но в перспективе можно вернуть boolean для унификации (осторожно: возможен breaking change).
src/main/java/com/github/_1c_syntax/bsl/languageserver/cfg/CfgBuildingParseTreeVisitor.java (4)
70-81: Верхнеуровневый препроцессор: сейчас обрабатывается только ближайший блок; стоит поддержать несколько подрядЕсли перед телом модуля идут несколько верхнеуровневых препроцессорных конструкций подряд, текущая логика посетит только ближайшую к FileCodeBlock. Предлагаю пройтись по всем предыдущим узлам RULE_preprocessor и вызвать accept в порядке исходника.
Пример правки:
- if (producePreprocessorConditionsEnabled) { - // Если это тело модуля, то самую первую инструкцию препроцессора сожрет грамматика file - // надо ее тоже посетить принудительно. - var parent = block.getParent(); - if (parent instanceof BSLParser.FileCodeBlockContext fileBlock) { - var probablyPreprocessor = Trees.getPreviousNode(fileBlock.getParent(), fileBlock , BSLParser.RULE_preprocessor); - if (probablyPreprocessor != fileBlock) { - hasTopLevelPreprocessor = true; - probablyPreprocessor.accept(this); - } - } - } + if (producePreprocessorConditionsEnabled) { + // Если это тело модуля, грамматика file «съедает» верхнеуровневые препроцессоры. + // Пройдем все предыдущие препроцессоры и посетим их в порядке исходника. + var parent = block.getParent(); + if (parent instanceof BSLParser.FileCodeBlockContext fileBlock) { + var container = fileBlock.getParent(); + var node = Trees.getPreviousNode(container, fileBlock, BSLParser.RULE_preprocessor); + java.util.Deque<org.antlr.v4.runtime.tree.ParseTree> stack = new java.util.ArrayDeque<>(); + while (node != fileBlock) { + stack.push(node); + node = Trees.getPreviousNode(container, node, BSLParser.RULE_preprocessor); + } + while (!stack.isEmpty()) { + hasTopLevelPreprocessor = true; + stack.pop().accept(this); + } + } + }
402-405: Защититься от NPE в проверке уровня препроцессораВ вызове isStatementLevelPreproc(ctx) возможен NPE при нетипичном дереве (если у узла не окажется одного из родителей). Редкий случай, но лучше сделать метод null-safe.
Предлагаемая правка метода (вне текущего диапазона строк):
private static boolean isStatementLevelPreproc(BSLParserRuleContext ctx) { var parent = ctx.getParent(); if (parent == null) { return false; } var grandparent = parent.getParent(); return grandparent != null && grandparent.getRuleIndex() == BSLParser.RULE_statement; }
492-526: preproc_endif: корректная логика FALSE_BRANCH; возможно избыточный addVertex для хвостовЛогика mustAddFalseBranch устраняет необходимость принудительно добавлять FALSE_BRANCH, когда альтернативы уже заданы ранее — это правильно. Небольшая придирка: вызов graph.addVertex(blockTail) перед добавлением ребра, скорее всего, избыточен — хвост, как правило, уже присутствует в графе. Если ControlFlowGraph не гарантирует идемпотентность addVertex, это может быть источником ошибок; если гарантирует — всё ок.
Мини-правка:
- graph.addVertex(blockTail); graph.addEdge(blockTail, upperBlock.end());Дополнительно: стоит убедиться, что тесты покрывают оба случая — без else/elsif (добавляется FALSE_BRANCH) и с альтернативами (ветка не добавляется).
589-599: Перекладка входящих ребер: устранен CME; сохранить тип ребра до удаления и проверить базовую JDKТекущий подход с предварительным копированием списка ребер предотвращает ConcurrentModificationException — это плюс. Для наглядности и на всякий случай лучше сохранить тип ребра в локальную переменную до удаления ребра из графа. Также используется Stream.toList(), требующий JDK 16+; проверьте, соответствует ли это целевой версии проекта.
Предлагаемая правка:
- var incoming = graph.incomingEdgesOf(currentTail).stream().toList(); + var incoming = graph.incomingEdgesOf(currentTail).stream().toList(); for (var edge : incoming) { // ребра смежности не переключаем, т.к. текущий блок удаляется if (edge.getType() == CfgEdgeType.ADJACENT_CODE) { continue; } - var source = graph.getEdgeSource(edge); - graph.removeEdge(edge); - graph.addEdge(source, vertex, edge.getType()); + var source = graph.getEdgeSource(edge); + var type = edge.getType(); + graph.removeEdge(edge); + graph.addEdge(source, vertex, type); }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (11)
src/main/java/com/github/_1c_syntax/bsl/languageserver/cfg/BasicBlockVertex.java(1 hunks)src/main/java/com/github/_1c_syntax/bsl/languageserver/cfg/CfgBuildingParseTreeVisitor.java(9 hunks)src/main/java/com/github/_1c_syntax/bsl/languageserver/cfg/CfgVertex.java(1 hunks)src/main/java/com/github/_1c_syntax/bsl/languageserver/cfg/ConditionalVertex.java(1 hunks)src/main/java/com/github/_1c_syntax/bsl/languageserver/cfg/ControlFlowGraph.java(2 hunks)src/main/java/com/github/_1c_syntax/bsl/languageserver/cfg/ControlFlowGraphWalker.java(1 hunks)src/main/java/com/github/_1c_syntax/bsl/languageserver/cfg/ExitVertex.java(1 hunks)src/main/java/com/github/_1c_syntax/bsl/languageserver/cfg/FlowGraphLinkException.java(1 hunks)src/main/java/com/github/_1c_syntax/bsl/languageserver/diagnostics/AllFunctionPathMustHaveReturnDiagnostic.java(1 hunks)src/test/java/com/github/_1c_syntax/bsl/languageserver/cfg/ControlFlowGraphBuilderTest.java(7 hunks)src/test/java/com/github/_1c_syntax/bsl/languageserver/diagnostics/AllFunctionPathMustHaveReturnDiagnosticTest.java(1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-04-18T22:46:43.245Z
Learnt from: nixel2007
PR: 1c-syntax/bsl-language-server#3449
File: src/main/java/com/github/_1c_syntax/bsl/languageserver/utils/expressiontree/ExpressionTreeBuildingVisitor.java:192-203
Timestamp: 2025-04-18T22:46:43.245Z
Learning: В проекте bsl-language-server класс BSLParser.ExpressionContext наследуется от BSLParserRuleContext, а не напрямую от ParserRuleContext. При работе с ним нужно учитывать специфичные методы BSLParserRuleContext.
Applied to files:
src/main/java/com/github/_1c_syntax/bsl/languageserver/cfg/CfgBuildingParseTreeVisitor.java
🧬 Code Graph Analysis (1)
src/test/java/com/github/_1c_syntax/bsl/languageserver/cfg/ControlFlowGraphBuilderTest.java (1)
src/test/java/com/github/_1c_syntax/bsl/languageserver/util/TestUtils.java (1)
TestUtils(37-80)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (12)
- GitHub Check: build (23, ubuntu-latest)
- GitHub Check: build (21, ubuntu-latest)
- GitHub Check: build (17, windows-latest)
- GitHub Check: build (23, windows-latest)
- GitHub Check: build (21, macOS-latest)
- GitHub Check: build (21, windows-latest)
- GitHub Check: build (17, macOS-latest)
- GitHub Check: build (23, macOS-latest)
- GitHub Check: build (17, ubuntu-latest)
- GitHub Check: Analyse
- GitHub Check: build
- GitHub Check: Benchmark
🔇 Additional comments (21)
src/main/java/com/github/_1c_syntax/bsl/languageserver/cfg/ControlFlowGraphWalker.java (1)
48-50: Хорошее ужесточение: теперь гарантируется единственность DIRECT-ребраПроверка через reduce с выбросом исключения при >1 ребре делает инвариант очевидным и помогает раннему выявлению ошибок построения графа.
src/test/java/com/github/_1c_syntax/bsl/languageserver/diagnostics/AllFunctionPathMustHaveReturnDiagnosticTest.java (1)
105-118: Тест кейс с препроцессором корректно отражает целевую логику выхода из функцииЗамена условного блока на директиву препроцессора (#Если/#Иначе) в примере делает тест релевантным новой модели CFG и корректной обработке исключений/возвратов. Ожидание отсутствия диагностик выглядит верным.
src/main/java/com/github/_1c_syntax/bsl/languageserver/diagnostics/AllFunctionPathMustHaveReturnDiagnostic.java (2)
100-102: Включение условий препроцессора при построении CFG — правильное решениеbuilder.producePreprocessorConditions(true) синхронизирует диагностику с новой поддержкой препроцессора в CFG. Это предотвращает ложно-положительные срабатывания на путях, отрезанных препроцессором.
103-110: Переход на graph.getExitPoint() и упрощение входящих ребер — окОпора на неизменяемую exit-точку графа и явный сбор источников входящих ребер к ней упрощает логику и устраняет прежние Optional-проверки. Выбор .toList() заранее стабилизирует набор.
src/main/java/com/github/_1c_syntax/bsl/languageserver/cfg/CfgVertex.java (2)
36-41: toString() с диапазонами строк — полезно для диагностикиПредставление вида Class{start:stop} улучшает трассировку ошибок связности графа. Выгодно в связке с edgePresentation.
43-57: Проверка дублирования исходящих ребер по типу — уточнить инвариантыБазовая проверка запрещает более одного исходящего ребра одного типа от любой вершины. Это хорошо соответствует условным вершинам (TRUE/FALSE), но требует подтверждения, что для всех прочих вершин (например, базовых блоков при сложных конструкциях) действительно недопустимы два DIRECT-ребра к разным целям.
Если есть допустимые случаи нескольких DIRECT-ребер, логику стоит перенести в специализированные подклассы (ConditionalVertex, ExitVertex и т.п.) или сужать проверку в базовом классе.
Для подстраховки предлагаю проверить, нет ли обходов базовой проверки через двухаргументный addEdge (см. комментарий в ControlFlowGraph.java ниже).
src/test/java/com/github/_1c_syntax/bsl/languageserver/cfg/ControlFlowGraphBuilderTest.java (8)
51-57: Уточненные проверки размеров и связности — читабельно и по делуПереход на hasSize/isEmpty и явная проверка одного исходящего ребра делают тест прозрачнее.
79-89: Корректная проверка ветвления и маршрутов в простом условииОжидание двух маршрутов после ветвления и пустого набора маршрутов в Exit согласуется с новой логикой ControlFlowGraphWalker.
268-282: Проверка ребер при break/мертвом коде — хорошее покрытие углаАссерты по количеству входящих в конец второго цикла, наличию DIRECT-ребра “Прервать” и сохранности ребер из “мертвого” участка помогают зафиксировать инварианты билдера CFG.
381-385: Переход на .toList() и метод-референс — аккуратная модернизацияМелкое улучшение читаемости без изменения семантики.
509-526: Верхнеуровневый препроцессор ведет к ветвлению — тест отражает требованиеproducePreprocessorConditions(true) ожидаемо приводит к ветвлению на старте. Тест фиксирует инвариант.
528-548: Игнор препроцессора в секции переменных — верное поведениеОтсутствие ветвления при верхнеуровневом препроцессоре в секции переменных подтверждено тестом. Это важно для стабильности CFG.
550-573: Препроцессор в теле процедуры первым узлом — корректно обрабатываетсяТест полезен: ветвление есть, true-ветка ведет к первому оператору.
575-625: Сложный сценарий с возвратами/исключением под препроцессором покрыт хорошоАссерты на достижение exit-точки из обеих веток и на отсутствие лишних связей у узла препроцессора надежно закрепляют поведение билдера CFG.
src/main/java/com/github/_1c_syntax/bsl/languageserver/cfg/ControlFlowGraph.java (3)
34-40: Неизменяемая exit-точка, регистрируемая в конструкторе — плюс к устойчивости графаИнициализация exitPoint и добавление вершины в граф в конструкторе устраняют неопределенность и упрощают потребителям доступ к выходу.
46-52: Перехват добавления ребра через onConnectOutgoing — правильное место для инвариантовПереопределение addEdge(CfgVertex, CfgVertex, CfgEdge) с вызовом source.onConnectOutgoing закрепляет единый вход для проверок связности и типов ребер.
46-52: Проверка двухаргументных вызовов addEdgeНи одного двухаргументного вызова addEdge(V, V) вне класса ControlFlowGraph не найдено — все вызовы используют трёхаргументную версию и корректно проходят через onConnectOutgoing. Инварианты сохраняются.
src/main/java/com/github/_1c_syntax/bsl/languageserver/cfg/CfgBuildingParseTreeVisitor.java (4)
394-402: Топ‑уровневый препроцессор: корректная инициализация начала блокаСоздание вершины для begin текущего блока при обработке верхнеуровневого препроцессора решает проблему отсутствия явного CodeBlock-входа. Флаг сбрасывается своевременно.
406-418: Перестройка препроцессорного if: выделение true-блока и маркер — окСоздание отдельного блока для true-ветки и помещение маркерного узла (PreprocessorConditionVertex) в BuildParts обеспечивает корректную связку с последующими elseif/else/endif.
128-156: visitIfStatement: использование enterBlock() для текущего уровня — верное упрощениеПолучение currentLevelBlock напрямую из enterBlock() упрощает логику и делает дальнейшие операции с BuildParts более очевидными.
170-173: Удаление пустых хвостов в if: корректно и безопасноПроверка на отсутствие значимых ребер и пустой список statements c pattern matching — хороший способ не засорять граф лишними узлами.
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (5)
src/main/java/com/github/_1c_syntax/bsl/languageserver/cfg/ControlFlowGraphWalker.java (2)
44-58: Добавить precondition: walker должен быть запущен (start())Сейчас при вызове walkNext() без start() будет NPE внутри outgoingEdgesOf. Предлагаю явную проверку, чтобы сообщение об ошибке было предсказуемым.
Применить в этом методе:
public CfgEdge walkNext() { + ensureStarted(); var edges = availableRoutes();И аналогично в availableRoutes() и walkNext(CfgEdgeType) (см. ниже).
Добавить вспомогательный метод в класс:
// вне выбранного диапазона, вставить в класс private void ensureStarted() { if (currentNode == null) { throw new IllegalStateException("Walker is not started. Call start() first."); } }
60-71: Симметрия и явная диагностика: проверять уникальность и для типизированных рёберГраф теперь сам запрещает дубликаты, но для симметрии с walkNext() и более явной диагностики багов графа лучше применить тот же подход в walkNext(CfgEdgeType) и уточнить текст ошибки.
- public CfgEdge walkNext(CfgEdgeType edgeType) { - var edgeOrNot = availableRoutes().stream() - .filter(x -> x.getType() == edgeType) - .findAny(); - - if (edgeOrNot.isPresent()) { - currentNode = graph.getEdgeTarget(edgeOrNot.get()); - return edgeOrNot.get(); - } - - throw new IllegalStateException("Edge is not found for node " + currentNode); - } + public CfgEdge walkNext(CfgEdgeType edgeType) { + ensureStarted(); + var edgeOrNot = availableRoutes().stream() + .filter(x -> x.getType() == edgeType) + .reduce((CfgEdge a, CfgEdge b) -> { + throw new IllegalStateException("Multiple " + edgeType + " outgoing edges in " + currentNode); + }); + + if (edgeOrNot.isPresent()) { + currentNode = graph.getEdgeTarget(edgeOrNot.get()); + return edgeOrNot.get(); + } + + throw new IllegalStateException("Edge of type " + edgeType + " is not found for node " + currentNode); + }Дополнительно предложено уточнение сообщения об отсутствии ребра: включить тип ребра в текст.
src/test/java/com/github/_1c_syntax/bsl/languageserver/cfg/ControlFlowGraphBuilderTest.java (3)
513-531: Усилить проверку топового препроцессораСейчас проверяем только факт ветвления. Рекомендую дополнить проверкой точного числа исходящих рёбер и их типов — это повысит надежность теста.
walker.start(); - assertThat(walker.isOnBranch()).isTrue(); + assertThat(walker.isOnBranch()).isTrue(); + assertThat(walker.availableRoutes()).hasSize(2); + assertThat(walker.availableRoutes()) + .extracting(CfgEdge::getType) + .containsExactlyInAnyOrder(CfgEdgeType.TRUE_BRANCH, CfgEdgeType.FALSE_BRANCH);
532-553: Уточнить ожидаемое стартовое положениеЧтобы зафиксировать, что препроцессор в разделе переменных игнорируется, можно явно проверить первую операцию.
walker.start(); - assertThat(walker.isOnBranch()).isFalse(); + assertThat(walker.isOnBranch()).isFalse(); + assertThat(textOfCurrentNode(walker)).isEqualTo("А=8");
554-578: Топовый препроцессор в теле модуля — добавить проверку численности исходящихДля полноты картины полезно проверить, что у узла-препроцессора два исходящих ребра.
walker.start(); - assertThat(walker.isOnBranch()).isTrue(); + assertThat(walker.isOnBranch()).isTrue(); + assertThat(walker.availableRoutes()).hasSize(2); walker.walkNext(CfgEdgeType.TRUE_BRANCH);
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (5)
src/main/java/com/github/_1c_syntax/bsl/languageserver/cfg/CfgBuildingParseTreeVisitor.java(9 hunks)src/main/java/com/github/_1c_syntax/bsl/languageserver/cfg/CfgVertex.java(1 hunks)src/main/java/com/github/_1c_syntax/bsl/languageserver/cfg/ControlFlowGraph.java(2 hunks)src/main/java/com/github/_1c_syntax/bsl/languageserver/cfg/ControlFlowGraphWalker.java(2 hunks)src/test/java/com/github/_1c_syntax/bsl/languageserver/cfg/ControlFlowGraphBuilderTest.java(8 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
- src/main/java/com/github/_1c_syntax/bsl/languageserver/cfg/CfgBuildingParseTreeVisitor.java
- src/main/java/com/github/_1c_syntax/bsl/languageserver/cfg/CfgVertex.java
- src/main/java/com/github/_1c_syntax/bsl/languageserver/cfg/ControlFlowGraph.java
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/test/java/com/github/_1c_syntax/bsl/languageserver/cfg/ControlFlowGraphBuilderTest.java (1)
src/test/java/com/github/_1c_syntax/bsl/languageserver/util/TestUtils.java (1)
TestUtils(37-80)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (12)
- GitHub Check: build (21, ubuntu-latest)
- GitHub Check: build (23, macOS-latest)
- GitHub Check: build (23, ubuntu-latest)
- GitHub Check: build (21, macOS-latest)
- GitHub Check: build (17, ubuntu-latest)
- GitHub Check: build (21, windows-latest)
- GitHub Check: build (23, windows-latest)
- GitHub Check: build (17, macOS-latest)
- GitHub Check: build (17, windows-latest)
- GitHub Check: build
- GitHub Check: Analyse
- GitHub Check: Benchmark
🔇 Additional comments (9)
src/main/java/com/github/_1c_syntax/bsl/languageserver/cfg/ControlFlowGraphWalker.java (1)
48-50: Защита от дублирующих DIRECT-рёбер в walkNext — хорошоЯвная проверка через reduce с выбросом ISE при >1 DIRECT-ребре добавляет полезную гарантию поверх инвариантов графа.
src/test/java/com/github/_1c_syntax/bsl/languageserver/cfg/ControlFlowGraphBuilderTest.java (8)
27-41: Импорты для Mockito/AssertJ — корректноДобавленные CommonToken, Assertions и статические mock/when используются по делу.
55-61: Переход на fluent-ассерты AssertJ — окhasSize вместо size-проверок повышает читабельность.
83-99: Явные проверки числа путей и входящих рёбер — окПокрывают инварианты развилки и выходной вершины.
273-286: Уточнение проверок входящих/исходящих рёбер во вложенных циклах — окХорошо фиксируют корректность «break/continue» трассировки.
386-389: Лаконичнее со Stream APIИспользование BasicBlockVertex.class::isInstance и Stream.toList() уместно.
579-629: Покрытие выхода через Возврат/Исключение — отличноПроверки корректно привязаны к graph.getExitPoint() и фиксируют отсутствие лишних связей у входного узла препроцессора.
631-643: Дубликаты рёбер запрещены — тест валидирует контрактЧетко покрывает новый guard в графе.
645-661: Запрет DIRECT-ребра из ConditionalVertex — тест корректныйМок контекста и ожидание FlowGraphLinkException соответствуют новым правилам.
There was a problem hiding this comment.
Pull Request Overview
This PR removes a workaround patch that was implemented for issue #1774 and fixes the underlying Control Flow Graph (CFG) bug that caused the original problem. The changes enable proper handling of preprocessor conditions in the CFG builder and improve graph validation.
- Fixes CFG bug that led to issue #1774 by properly handling preprocessor conditions
- Removes workaround code and enables preprocessor conditions processing
- Adds comprehensive validation and exception handling for CFG edge connections
Reviewed Changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| AllFunctionPathMustHaveReturnDiagnostic.java | Enables preprocessor conditions and simplifies exit node handling |
| AllFunctionPathMustHaveReturnDiagnosticTest.java | Updates test case to use preprocessor directive instead of regular if statement |
| CfgBuildingParseTreeVisitor.java | Major refactoring to properly handle top-level preprocessor conditions and fix graph building logic |
| ControlFlowGraph.java | Adds edge validation hooks and utility methods |
| CfgVertex.java | Adds connection validation and duplicate edge detection |
| ConditionalVertex.java | Adds validation for proper branch edge types |
| ExitVertex.java | Prevents outgoing edges from exit vertex |
| FlowGraphLinkException.java | New exception class for CFG validation errors |
| ControlFlowGraphWalker.java | Improves error handling for multiple direct edges |
| BasicBlockVertex.java | Improves toString representation for empty blocks |
| ControlFlowGraphBuilderTest.java | Extensive test additions and improvements for CFG functionality |
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
| if (hasNoSignificantEdges(blockTail) | ||
| && blockTail instanceof BasicBlockVertex basicBlock | ||
| && basicBlock.statements().isEmpty()) { | ||
| graph.removeVertex(basicBlock); |
There was a problem hiding this comment.
Removing a vertex from the graph while iterating over its edges or vertices can cause concurrent modification issues. The vertex should be marked for removal and cleaned up after the iteration completes.
Co-authored-by: Copilot <[email protected]>
|



Описание
Чеклист
Общие
gradlew precommit)