fix(diagnostics): не подменять тип ресивера типом охватывающего выражения в UnknownMember#4254
Conversation
…UnknownMember unknownMemberReceiverAt: когда dereference-инференс ресивера пуст, добирали тип через expressionTypesAt(receiverEnd) — тип ВСЕГО охватывающего выражения (для Ресивер.Член = ... это Булево у сравнения), из-за чего член проверялся у неверного типа. Это давало ложные срабатывания на валидных членах (Строка.Роль, Соответствие.Получить, ТЗ.Найти и т.п.). Теперь ресивер добирается по завершающему идентификатору ресивера перед точкой (предыдущий accessProperty комплексного идентификатора / головной IDENTIFIER), резолвленному через индекс ссылок терминально — без спуска по AST и без подмены типа охватывающим выражением. Co-Authored-By: Claude Opus 4.8 <[email protected]> Claude-Session: https://claude.ai/code/session_01BSiRGLm633B4EmvG3vkk4V
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 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 |
|



Описание
UnknownMemberDiagnosticдляРесивер.Членвыводит тип ресивера черезTypeService.unknownMemberReceiverAt. Когда dereference-инференс ресивера пуст, тип добирался черезexpressionTypesAt(receiverEnd)— тип всего охватывающего выражения. ДляРесивер.Член <оператор> …(сравнение, логика) это тип операции, а не ресивера:expressionTypesAtвозвращает типСтрока.Роль = "…"—Булево. РесиверСтрокаошибочно типизируется какБулево, членРольне находится уБулево→ ложное срабатывание на валидном члене.Что стало: когда dereference-ресивер пуст, ресивер добирается по завершающему идентификатору ресивера перед точкой (
receiverEndIdentifier) — предыдущийaccessPropertyтого жеcomplexIdentifier(дляА.Б.В|→Б) или головнойIDENTIFIER(А.Б|→А), резолвленный через индекс ссылок терминально. Тип охватывающего выражения больше не используется. Fallback поexpressionTypesAtуместен лишь для висячей точки (Ресивер.|) в completion — там завершённого члена нет; для диагностики с завершённым членом он давал только ложные срабатывания.Грамматика сверена по
BSLParser.g4(complexIdentifier: (IDENTIFIER|newExpression|ternaryOperator|globalMethodCall) modifier*;modifier: accessProperty|accessIndex|accessCall|incompleteAccess;accessProperty: DOT IDENTIFIER): для head-не-идентификатора (Новый …, тернарник, вызов) и предыдущего-не-accessProperty(вызов/индекс)receiverEndIdentifierвозвращает пусто — ресивер не именованный, по индексу не резолвится, что корректно.Зачем
Двойной эффект — корректность и производительность:
expressionTypesAt(receiverEnd)структурно неверен как «тип ресивера» — он всегда накрывает выражение шире ресивера (включая сам член и оператор). На реальном модуле это давало ~2363 ложных срабатывания на валидных членах (Строка.Роль,Соответствие.Получить,ТЗ.Найти, …).expressionTypesAt/findReference(receiverEnd)со спуском от корня на каждое обращение к члену. Терминальный резолв завершающего идентификатора убирает и спуск, и лишнюю ветку.Замеры
Нагрузка —
UnknownMemberпо общему модулюУправлениеДоступомСлужебныйиз SSL (~48k строк); best-итерация в одной JVM. База — все смердженные PR серии + terminal-aware резолюция (#4253):Разбор корректности (замер с категоризацией восстановлений fallback'а): 2363 «восстановления» шли через
expressionTypesAt(receiverEnd)и все были ложными (тип операции, обычноБулево); 101 легитимное — через индекс ссылок — сохранены (резолв завершающего идентификатора).Связанные задачи
Closes
Чеклист
Общие
claude/perf-terminal-reference-finder— см. «Дополнительно»)UnknownMemberDiagnosticTest+ фикстура: негатив — нетипизированный ресивер в сравнении (Параметр.НекийМетод() = Истина,Параметр.НекоеСвойство > 0) не фонит; позитив — несуществующий член конкретного ресивера в том же контексте сравнения (Массив.НетТакогоМетода() > 0) ловится)gradlew precommit) — прогнаны затронутые тесты диагностик/типов; полныйprecommitза мейнтейнерамиДополнительно
Зависит от #4253 (использует его terminal-aware
ReferenceResolver.findReference(uri, TerminalNode)), поэтому PR стекнут на веткуclaude/perf-terminal-reference-finder; после мерджа #4253 базу нужно переключить наdevelop. Продолжение серии перф-правок: вскрыто при профилировании остаточного пути резолюции ресивера — узкое место по CPU совпало с источником ложных срабатыванийUnknownMember.🤖 Generated with Claude Code
https://claude.ai/code/session_01BSiRGLm633B4EmvG3vkk4V
Generated by Claude Code