Skip to content

fix(diagnostics): не подменять тип ресивера типом охватывающего выражения в UnknownMember#4254

Merged
nixel2007 merged 1 commit into
developfrom
claude/perf-fix-unknown-member-receiver
Jul 10, 2026
Merged

fix(diagnostics): не подменять тип ресивера типом охватывающего выражения в UnknownMember#4254
nixel2007 merged 1 commit into
developfrom
claude/perf-fix-unknown-member-receiver

Conversation

@nixel2007

Copy link
Copy Markdown
Member

Описание

UnknownMemberDiagnostic для Ресивер.Член выводит тип ресивера через TypeService.unknownMemberReceiverAt. Когда dereference-инференс ресивера пуст, тип добирался через expressionTypesAt(receiverEnd) — тип всего охватывающего выражения. Для Ресивер.Член <оператор> … (сравнение, логика) это тип операции, а не ресивера:

Если Строка.Роль = "АдминистраторСистемы" Тогда   // 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 ложных срабатывания на валидных членах (Строка.Роль, Соответствие.Получить, ТЗ.Найти, …).
  • Производительность: этот путь был доминантой CPU — expressionTypesAt/findReference(receiverEnd) со спуском от корня на каждое обращение к члену. Терминальный резолв завершающего идентификатора убирает и спуск, и лишнюю ветку.

Замеры

Нагрузка — UnknownMember по общему модулю УправлениеДоступомСлужебный из SSL (~48k строк); best-итерация в одной JVM. База — все смердженные PR серии + terminal-aware резолюция (#4253):

было стало
время (best-iter) 2409 мс 307 мс (×7.8)
срабатывания 6540 (из них 2363 ложных) 4177

Разбор корректности (замер с категоризацией восстановлений fallback'а): 2363 «восстановления» шли через expressionTypesAt(receiverEnd) и все были ложными (тип операции, обычно Булево); 101 легитимное — через индекс ссылок — сохранены (резолв завершающего идентификатора).

Связанные задачи

Closes

Чеклист

Общие

  • Ветка PR обновлена из develop (стекнута на 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

…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
@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: c22057b1-723d-43fe-ab80-27e3d01ef703

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/perf-fix-unknown-member-receiver

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Base automatically changed from claude/perf-terminal-reference-finder to develop July 10, 2026 07:40
@nixel2007
nixel2007 merged commit ccd5156 into develop Jul 10, 2026
35 checks passed
@nixel2007
nixel2007 deleted the claude/perf-fix-unknown-member-receiver branch July 10, 2026 07:54
@sonarqubecloud

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants