fix(types): не расширять тип строки-аккумулятора до «Строка, Число» (#4205)#4239
Conversation
При выводе типа переменной вида `Строка = Строка + "..."` рекурсивная ссылка на саму переменную упиралась в guard циклов и резолвилась в пустой набор. Ветка `+` в inferBinary трактовала пустой левый операнд как Число (default), и это ложное Число подмешивалось в union — тип переменной расширялся до "Строка, Число". В обратном порядке (`"..." + Строка`) левый операнд — литерал, поэтому баг не проявлялся. Причина глубже, чем ветка `+`: self-reference терял уже известный тип из предыдущих присваиваний. Теперь inferVariable регистрирует переменную в InferenceContext.inProgress и публикует растущий accumulator по мере объединения присваиваний; при повторном входе (self-reference) возвращается накопленный к этому моменту тип вместо EMPTY — one-pass фикс-точка. Как следствие: - выражение `Строка + "..."` корректно выводится в Строку (а не теряет тип); - числовой аккумулятор `Число = Число + 1` остаётся Число; - общая ветка `+` не изменена. Closes #4205
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughModifies ChangesSelf-reference type stabilization
Estimated code review effort: 3 (Moderate) | ~25 minutes Sequence Diagram(s)sequenceDiagram
participant inferVariable
participant InferenceContext
participant resolveReference
inferVariable->>InferenceContext: check inProgress for symbol X
InferenceContext-->>inferVariable: no entry found
inferVariable->>InferenceContext: seed inProgress with acc
inferVariable->>resolveReference: resolve reference to X
resolveReference->>InferenceContext: read inProgress on cycle detection
InferenceContext-->>resolveReference: partial TypeSet
resolveReference-->>inferVariable: partial TypeSet
inferVariable->>InferenceContext: update inProgress with accumulated result
inferVariable->>InferenceContext: remove in-progress entry
Related issues: Suggested labels: bug, type-inference Suggested reviewers: maintainers 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
selfConcatenationExpressionIsString указывал курсор на пробел после оператора `+` (helper прибавляет +1 к колонке), где нет ни одного терминала — findTerminalNodeContainsPosition возвращал пусто, и тип получался EMPTY. Ставим курсор внутрь правого операнда: findExpressionContext поднимается до ближайшего expression-узла (всего бинарного выражения), и тип корректно выводится в Строку. Сам фикс инференса не менялся: selfConcatenationVariableStaysString и numericSelfAccumulatorStaysNumber в CI прошли — падал только этот тест из-за позиции курсора.
|



Проблема
#4205: при выводе типа переменной, собираемой конкатенацией с самой собой, тип расширялся до
Строка, Число:В обратном порядке (
"..." + ПолноеИмя) баг не проявлялся — что и было отмечено в issue.Причина
При инференсе типа переменной
ExpressionTypeInferencerобъединяет типы по всем её присваиваниям. ДляПолноеИмя = ПолноеИмя + "..."правая часть ссылается на самуПолноеИмя; эта рекурсивная ссылка упиралась в guard циклов (ctx.visited) и резолвилась вTypeSet.EMPTY. Ветка+вinferBinaryопределяет результат по левому операнду, а для пустого (неизвестного) левого операнда попадала вdefault→Число. Это ложноеЧислои подмешивалось в union. При"..." + ПолноеИмялевый операнд — строковый литерал, рекурсии нет, поэтому проблемы не было.Решение
Причина глубже, чем ветка
+: self-reference терял уже известный тип из предыдущих присваиваний. Исправление — на уровне резолва самоссылки, а не подгонки ветки+:inferVariableрегистрирует переменную в новомInferenceContext.inProgressи по мере объединения присваиваний публикует туда растущийaccumulator;EMPTY— one-pass фикс-точка по присваиваниям.Благодаря этому:
Строка + "..."корректно выводится вСтрока(а не теряет тип);Число = Число + 1остаётсяЧисло;+не изменена — универсальная семантика сложения не затронута.Тесты
В
BinaryOperatorInferenceTest(+ фикстураBinaryOperatorInference.bsl):selfConcatenationVariableStaysString— тип переменной ровноСтрока(регресс на симптом [BUG] Конкатенация строки с собой расширяет выводимый тип до "Строка, Число" #4205);selfConcatenationExpressionIsString— само выражениеПолноеИмя + "..."выводится вСтрока, а не в пустой набор;numericSelfAccumulatorStaysNumber— числовой аккумулятор остаётсяЧисло.Closes #4205
🤖 Generated with Claude Code
Generated by Claude Code
Summary by CodeRabbit
+operator inference in self-reference scenarios, keepingСтрокаandЧислоresults consistent and preventing incorrect union growth or type widening.+inference to prevent regressions.