perf(types): резолв членов по терминалу вместо повторного спуска по AST#4250
Conversation
DeprecatedMethodCall собирал платформенные члены модуля дважды за один check() — независимо в checkPlatformMembers и checkDeletedPrefixMembers, удваивая обход AST и резолв членов. Теперь список собирается один раз в check() и передаётся обеим проверкам. Дополнительно PlatformMemberCalls передаёт уже известный из обхода AST TerminalNode в новую перегрузку TypeService.membersAt(doc, terminal, position), минуя повторный полный спуск по дереву (findTerminalNodeContainsPosition) ради поиска терминала по позиции. CPU-профиль (JFR) на модуле SSL УправлениеДоступомСлужебный (~48k строк): 4285 мс → 2017 мс на check() (×2.12). Поведение не меняется — существующие тесты диагностик и TypeService зелёные. Co-Authored-By: Claude Opus 4.8 <[email protected]> Claude-Session: https://claude.ai/code/session_01BSiRGLm633B4EmvG3vkk4V
…scent matchAt/receiverTypesAt принимали позицию и заново спускались по AST от корня, чтобы найти терминал, который вызывающий уже держал на руках, — до четырёх полных обходов дерева на один идентификатор (по обходу на каждую продукцию-кандидат внутри findExpressionTree). Проводим известный TerminalNode по цепочке matchAt -> findDereferenceTree -> ExpressionAtPosition и поднимаемся вверх от терминала вместо спуска. findExpressionTree(doc, position) теперь тоже резолвит терминал по позиции один раз и переиспользует его для всех четырёх продукций — выигрыш достаётся и hover/completion/signature help. CPU-профиль (JFR) на модуле SSL УправлениеДоступомСлужебный (~48k строк): спуск identifierTerminalAt 88.7% -> 0%, matchAt 83.8% -> 8.6%; суммарно с предыдущей правкой 4285 мс -> 578 мс на check() (×7.4 к baseline, ×3.5 к прошлому коммиту). Поведение не меняется — 1465 тестов types/провайдеров/диагностик зелёные. Co-Authored-By: Claude Opus 4.8 <[email protected]> Claude-Session: https://claude.ai/code/session_01BSiRGLm633B4EmvG3vkk4V
PlatformMemberCalls.collect делал три отдельных findAllRuleNodes (globalMethodCall, methodCall, accessProperty) — три полных обхода AST на модуль. Объединяем в один обход по трём rule-index с диспетчеризацией по виду продукции (collectSite). Множество узлов и порядок резолва в пределах сайта не меняются. CPU-профиль (JFR) на модуле SSL УправлениеДоступомСлужебный (~48k строк): findAllRuleNodes 57% -> 30%; суммарно с предыдущими правками 4285 мс -> 363 мс на check() (×11.8 к baseline, ×1.6 к прошлому коммиту). Поведение не меняется — тесты диагностик и PlatformMemberCalls зелёные. Co-Authored-By: Claude Opus 4.8 <[email protected]> Claude-Session: https://claude.ai/code/session_01BSiRGLm633B4EmvG3vkk4V
…r once UnknownMember и AssignToReadOnlyProperty держат терминал-идентификатор на руках из обхода AST, но звали позиционные точки входа TypeService (unknownMemberReceiverAt/isUnknownGlobalAt/memberAt), которые заново спускались по дереву от корня к позиции. Добавлены терминальные перегрузки (позиция для остаточных позиционных API — findReference, fallback висячей точки — берётся из терминала через Ranges.create). Дополнительно unknownMemberReceiverAt инферил тип ресивера дважды (membersAt матчит члены, потом receiverTypesAt инферит тот же left повторно). Введён DereferenceMemberMatcher.matchWithReceiverAt — один инференс отдаёт и члены, и типы ресивера. Замер (последовательный прогон всех диагностик по SSL УправлениеДоступомСлужебный, ~48k строк): UnknownMember 66.7 -> ~50 c (×1.34; это правило инференс-bound и activatedByDefault=false), AssignToReadOnlyProperty 1.61 -> ~1.3 c. Поведение не меняется — тесты types/провайдеров/диагностик зелёные. Co-Authored-By: Claude Opus 4.8 <[email protected]> Claude-Session: https://claude.ai/code/session_01BSiRGLm633B4EmvG3vkk4V
|
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 (8)
📝 WalkthroughWalkthroughThe change refactors member and expression resolution to use Antlr terminal nodes directly, adds terminal-based ChangesTerminal-based member resolution
Estimated code review effort: 3 (Moderate) | ~25 minutes Sequence Diagram(s)sequenceDiagram
participant Diagnostic
participant TypeService
participant PlatformMemberCalls
participant DereferenceMemberMatcher
participant ExpressionAtPosition
Diagnostic->>PlatformMemberCalls: collect call sites
PlatformMemberCalls->>TypeService: resolve identifier TerminalNode
TypeService->>DereferenceMemberMatcher: match terminal
DereferenceMemberMatcher->>ExpressionAtPosition: find expression tree
ExpressionAtPosition-->>DereferenceMemberMatcher: expression tree
DereferenceMemberMatcher-->>TypeService: matched members
TypeService-->>PlatformMemberCalls: typed members
PlatformMemberCalls-->>Diagnostic: collected platform members
Possibly related PRs
🚥 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 |
Test Results 3 588 files 3 588 suites 1h 45m 10s ⏱️ Results for commit e1bb80c. ♻️ This comment has been updated with latest results. |
|



Описание
Резолюция «член в позиции» (
TypeService.membersAt/memberAt/unknownMemberReceiverAt/isUnknownGlobalAt,DereferenceMemberMatcher,ExpressionAtPosition) принималаPositionи заново спускалась от корня AST черезTrees.findTerminalNodeContainsPosition(ast, position)— это O(размер дерева) на каждый вызов. Но все вызывающие (диагностики, инференсер) уже держат под рукойTerminalNodeтого самого идентификатора.Что стало — добавлены overload-сигнатуры по
TerminalNode:TypeService:membersAt(doc, TerminalNode),memberAt(doc, TerminalNode),unknownMemberReceiverAt(doc, TerminalNode),isUnknownGlobalAt(doc, TerminalNode);DereferenceMemberMatcher:matchAt(terminal, doc),receiverTypesAt(doc, terminal),findDereferenceTree(terminal)иmatchWithReceiverAt(инферит ресивер один раз, отдаётMemberMatch(members, receiverTypes));ExpressionAtPosition:enclosingRule(TerminalNode, ruleIndex)/findExpressionTree(TerminalNode)— подъём вверх от известного терминала (O(глубина)) вместо спуска от корня.Вызывающие переведены на терминал:
UnknownMemberDiagnostic(methodName.IDENTIFIER()/accessProperty.IDENTIFIER()),AssignToReadOnlyPropertyDiagnostic,DeprecatedMethodCall/PlatformMemberCalls. Позиционные сигнатуры сохранены и делегируют через терминал (обратная совместимость).Дополнительно две смежные правки в горячем пути платформенных диагностик:
DeprecatedMethodCallDiagnostic— дедуп:PlatformMemberCalls.collect()вызывается один раз,List<TypedMember>переиспользуется вcheckPlatformMembers/checkDeletedPrefixMembers(раньше сбор шёл дважды).PlatformMemberCalls— три проходаfindAllRuleNodes(globalMethodCall / methodCall / accessProperty) слиты в одинfindAllRuleNodes(ast, RULE_globalMethodCall, RULE_methodCall, RULE_accessProperty)с диспетчеромcollectSite.Зачем
findTerminalNodeContainsPosition(ast, position)— спуск от корня стоимостью в размер дерева; на большом модуле это доминирующая линейная стоимость каждого резолва члена. Терминал у вызывающего уже есть — спуск лишний.Замеры
Нагрузка —
DeprecatedMethodCallпо общему модулюУправлениеДоступомСлужебныйиз SSL (~48k строк); чистый A/B в одной JVM:DeprecatedMethodCall: 4285 мс → 363 мс = ×11.8 (эффект дедупа сбора + единого прохода + резолва по терминалу), число срабатываний неизменно.Терминальные overload'ы — фундамент для остальных PR серии (инференс-кэш и line-индексы снимаются уже поверх них).
Связанные задачи
Closes
Чеклист
Общие
develop)DeprecatedMethodCall/UnknownMember/AssignToReadOnlyProperty/inlay-hint тесты — парити результатов;PlatformMethodCallInlayHintCollectorUnitTestпоправлен под новую сигнатуру)gradlew precommit) — прогнаны затронутые тесты; полныйprecommitза мейнтейнерамиДополнительно
Часть серии независимых перф-правок, снятых по цепочке CPU-профилей одной диагностики на большом файле. Другие PR серии: мемоизация инференса выражений, индекс объявлений символов, индекс вхождений по строке.
🤖 Generated with Claude Code
https://claude.ai/code/session_01BSiRGLm633B4EmvG3vkk4V
Generated by Claude Code
Summary by CodeRabbit