Skip to content

perf(references): terminal-aware резолюция ссылок вместо повторного спуска по AST#4253

Merged
nixel2007 merged 2 commits into
developfrom
claude/perf-terminal-reference-finder
Jul 10, 2026
Merged

perf(references): terminal-aware резолюция ссылок вместо повторного спуска по AST#4253
nixel2007 merged 2 commits into
developfrom
claude/perf-terminal-reference-finder

Conversation

@nixel2007

Copy link
Copy Markdown
Member

Описание

Резолюция ссылок под курсором (ReferenceResolver → цепочка ReferenceFinder'ов) принимала только Position, поэтому вызывающие, у которых TerminalNode идентификатора уже под рукой (инференсер типов, диагностики), теряли его — и каждый finder заново спускался от корня AST через findTerminalNodeContainsPosition/аналоги (O(размер дерева)).

Что стало — terminal-aware overload в SPI:

  • ReferenceFinder.findReference(uri, TerminalNode)default делегирует в findReference(uri, Position) по стартовой позиции терминала (полная обратная совместимость: finder'ы, которым спуск не нужен, ничего не переопределяют);
  • ReferenceResolver.findReference(uri, TerminalNode) — тот же @Order и семантика первого совпадения, что и у позиционного варианта.

Три finder'а с дорогим спуском переопределяют терминальный метод на подъём от терминала:

  • KeywordReferenceFinder — строит keyword-символ прямо от терминала;
  • NewExpressionReferenceFinder — подъём до объемлющего newExpression (ancestorOrSelf → typeName → newExpression) вместо полного findInnermostNewExpression-спуска;
  • PlatformMemberReferenceFinderTypeService.memberAt(document, terminal).

Горячие вызывающие переведены на терминал: ExpressionTypeInferencer.inferMethodCall/inferIdentifier (терминал имени вызова / идентификатора уже есть) и TypeService.isUnknownGlobalAt(terminal).

Зачем

По CPU-профилю на большом реальном модуле (после line-индексов серии) остаток резолюции — это findTerminalNodeContainsPosition и однотипные спуски от корня, ~65% inclusive времени UnknownMember, разложенные по finder'ам (NewExpression ~22%, Keyword ~17%, PlatformMember ~16%). Все они запускаются из ReferenceResolver.findReference(uri, position) при инференсе ресивера — на каждый резолв идентификатора, хотя терминал у инференсера уже есть. Цепочка findFirst вдобавок прогоняет спуск каждого finder'а до первого совпадения (напр. NewExpression@150 спускается и возвращает пусто на не-new позициях — чистые накладные расходы).

Терминальный путь заменяет спуск от корня на подъём O(глубина); негативные ветки (терминал не является new-выражением/keyword'ом) отвечают мгновенно, не обходя дерево.

Замеры

Нагрузка — UnknownMember по общему модулю УправлениеДоступомСлужебный из SSL (~48k строк); A/B по best-итерации в одной JVM. База — все PR серии (терминальный резолв типов + мемоизация инференса + оба line-индекса), т.е. срез уже полностью оптимизирован — измеряется чистый вклад этой правки:

  • best-итерация: 3309 мс → 2459 мс = ×1.35 (−25.7%), число срабатываний диагностики неизменно (6540 = 6540 — парити).

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

Closes

Чеклист

Общие

  • Ветка PR обновлена из develop (ветка стекнута на claude/perf-terminal-based-type-resolution — см. «Дополнительно»)
  • Отладочные, закомментированные и прочие, не имеющие смысла участки кода удалены
  • Изменения покрыты тестами (ReferenceResolverTest: +2 теста терминальной диспетчеризации — default-делегирование в позиционный и приоритет override; парити UnknownMember/Deprecated/AssignToReadOnly/Hover/Definition/ExpressionTypeInferencer)
  • Обязательные действия перед коммитом выполнены (gradlew precommit) — прогнаны затронутые тесты references/инференса/провайдеров; полный precommit за мейнтейнерами

Дополнительно

Зависит от #4250 (использует терминальные overload'ы TypeService.memberAt(terminal)/isUnknownGlobalAt(terminal) и ExpressionAtPosition). Поэтому PR стекнут на ветку claude/perf-terminal-based-type-resolution; после мерджа #4250 базу нужно переключить на develop (или ребейзнуть). Финальный шаг серии перф-правок по цепочке CPU-профилей одной диагностики: терминальный резолв типов (#4250) → мемоизация инференса (#4251) → line-индекс объявлений (#4252) → line-индекс вхождений (#4249) → terminal-aware резолюция ссылок (этот PR).

🤖 Generated with Claude Code

https://claude.ai/code/session_01BSiRGLm633B4EmvG3vkk4V


Generated by Claude Code

Добавлен terminal-aware overload в SPI резолюции ссылок:
ReferenceFinder.findReference(uri, TerminalNode) (default делегирует в
позиционный — обратная совместимость) и ReferenceResolver.findReference(
uri, TerminalNode) с тем же @order и семантикой первого совпадения.

New/Keyword/PlatformMember finder'ы переопределяют его на подъём от
терминала вместо спуска findTerminalNodeContainsPosition от корня AST.
Горячие вызывающие переведены на терминал: ExpressionTypeInferencer
(inferMethodCall, inferIdentifier) и TypeService.isUnknownGlobalAt.

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: f8c8c1c7-57ce-4b1f-9a9c-eba3c3200832

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-terminal-reference-finder

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.

- убрать неиспользуемый resolveReferenceAt(Position) в ExpressionTypeInferencer
  (единственный вызывающий переведён на терминальный overload) — S1144;
- NewExpression terminal-override: single-expression lambda вместо блочной,
  short-circuit до fetch документа — S2211;
- KeywordReferenceFinder: позиционный путь делегирует в терминальный, без
  DocumentContext-типизированного хелпера (и импорта) — coupling 21→20, S1200.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Claude-Session: https://claude.ai/code/session_01BSiRGLm633B4EmvG3vkk4V
@sonarqubecloud

Copy link
Copy Markdown

Base automatically changed from claude/perf-terminal-based-type-resolution to develop July 10, 2026 07:29
@github-actions

Copy link
Copy Markdown
Contributor

Test Results

 3 588 files  ± 0   3 588 suites  ±0   1h 42m 23s ⏱️ - 2m 47s
 3 559 tests + 2   3 541 ✅ + 2   18 💤 ±0  0 ❌ ±0 
21 354 runs  +12  21 242 ✅ +12  112 💤 ±0  0 ❌ ±0 

Results for commit 391ef14. ± Comparison against base commit e1bb80c.

@nixel2007
nixel2007 merged commit 23ec6c1 into develop Jul 10, 2026
33 checks passed
@nixel2007
nixel2007 deleted the claude/perf-terminal-reference-finder branch July 10, 2026 07:40
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