fix(types): ссылки специализированных членов приводятся к каноническим - #4368
Conversation
Шаблон вида «СправочникСсылка.<Имя справочника>» приходит из синтакс-помощника платформенным, а конфигурационные типы регистрируются своим видом. Структурная специализация сохраняла вид шаблона, поэтому за одним именем оказывались две ссылки, различающиеся только видом: объединение наборов их не схлопывало и тип показывался дважды. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MnWpDdNSEFohPZHG6voe6y
Сборка результата через «TypeSet.of» оставляла одни ссылки: поля объекта и типы элементов коллекции отбрасывались. Приведение переехало в «TypeSet.mapRefs», который переносит декорации на новую ссылку, а если несколько исходных ссылок сходятся в одну каноническую — сливает их. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MnWpDdNSEFohPZHG6voe6y
…еским Перегруз без приведения оставлял дверь, через которую в members попадали неканонические ссылки: подстановка сохраняет вид шаблона, поэтому платформенный шаблон давал платформенную ссылку и на конфигурационный тип. Приведение получили и остальные места специализации — разворачивание generic-членов, регистры и подстановка единственного плейсхолдера. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MnWpDdNSEFohPZHG6voe6y
Ленивая декорация оборачивается: преобразование применяется к тому, что её источник вернёт при чтении, форсить его для этого не нужно. Ключ у обёртки прежний, поэтому равенство и слияние ленивых ссылок не меняются. Новые места специализации из типов форм переведены на единственную сигнатуру с приведением — как и все остальные. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MnWpDdNSEFohPZHG6voe6y
|
Warning Review limit reached
Next review available in: 12 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthrough
ChangesType reference canonicalization
Estimated code review effort: 3 (Moderate) | ~25 minutes Sequence Diagram(s)sequenceDiagram
participant TypeRegistry
participant MemberDescriptor
participant TypeSet
TypeRegistry->>MemberDescriptor: specialize with canonicalRef
MemberDescriptor->>TypeSet: map parameter type references
TypeSet-->>MemberDescriptor: return mapped TypeSet
MemberDescriptor->>TypeRegistry: resolve specialized return references
TypeRegistry-->>MemberDescriptor: return canonical TypeRef
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 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 |
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/main/java/com/github/_1c_syntax/bsl/languageserver/types/registry/TypeRegistry.java (1)
986-1004: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick winCanonicalize extension-sourced member references before caching them.
registerExtensionpassesMap.of()tocopyMembers, soMemberDescriptor.specialize(Map, UnaryOperator)returns the descriptor unchanged andcanonicalRefdoes not run. Extension call sites include form command/base type, form type/basic type, form module/forms, form module/ordinary form object, data type/base type, item kind/base type, collection row/base type, generic row, and generic collection. Canonicalize these member references before indexing them to avoid duplicateTypeRefs for the same extension member.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/main/java/com/github/_1c_syntax/bsl/languageserver/types/registry/TypeRegistry.java` around lines 986 - 1004, Update copyMembers so every extension-sourced MemberDescriptor has its referenced TypeRefs canonicalized even when bindings is empty, before memberMetadataIndex.index caches it. Apply canonicalRef to the specialized descriptor or its member references while preserving generic-member filtering and existing registerMemberSource behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In
`@src/main/java/com/github/_1c_syntax/bsl/languageserver/types/registry/TypeRegistry.java`:
- Around line 986-1004: Update copyMembers so every extension-sourced
MemberDescriptor has its referenced TypeRefs canonicalized even when bindings is
empty, before memberMetadataIndex.index caches it. Apply canonicalRef to the
specialized descriptor or its member references while preserving generic-member
filtering and existing registerMemberSource behavior.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: f5b17fdf-347b-481f-b573-81fd7b7e6fba
📒 Files selected for processing (11)
src/main/java/com/github/_1c_syntax/bsl/languageserver/types/model/MemberDescriptor.javasrc/main/java/com/github/_1c_syntax/bsl/languageserver/types/model/TypeSet.javasrc/main/java/com/github/_1c_syntax/bsl/languageserver/types/registry/FormParametersRegistrar.javasrc/main/java/com/github/_1c_syntax/bsl/languageserver/types/registry/FormTypesProvider.javasrc/main/java/com/github/_1c_syntax/bsl/languageserver/types/registry/PlaceholderBinder.javasrc/main/java/com/github/_1c_syntax/bsl/languageserver/types/registry/RegisterTypesRegistrar.javasrc/main/java/com/github/_1c_syntax/bsl/languageserver/types/registry/TypeRegistry.javasrc/test/java/com/github/_1c_syntax/bsl/languageserver/types/model/MemberDescriptorFactoryTest.javasrc/test/java/com/github/_1c_syntax/bsl/languageserver/types/model/MemberDescriptorSpecializeTest.javasrc/test/java/com/github/_1c_syntax/bsl/languageserver/types/model/TypeSetTest.javasrc/test/java/com/github/_1c_syntax/bsl/languageserver/types/registry/TypeRegistrySpecializationTest.java
…ссылки Специализация возвращала дескриптор как есть при пустых подстановках, а расширение типа именно так и копирует члены — приведение ссылок к каноническим у них не выполнялось. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MnWpDdNSEFohPZHG6voe6y
| var newReturnTypes = TypeRef.specialize(returnTypes, bindings).mapRefs(canonicalizer); | ||
| var newSignatures = signatures; | ||
| boolean signaturesChanged = false; | ||
| if (!newSignatures.isEmpty()) { |
There was a problem hiding this comment.
По замечанию про члены расширения — верно. registerExtension копирует члены с пустыми подстановками, а specialize при пустых bindings возвращал дескриптор как есть, и приведение ссылок не выполнялось.
Ранний выход убрал: при пустых подстановках TypeRef.specialize и так ничего не меняет, а mapRefs теперь отрабатывает. Возврат this, когда ничего не изменилось, сохранился — он делается сравнением результата ниже.
Тест TypeRegistrySpecializationTest.extensionMemberTypesAreCanonicalRefsFromRegistry: тип-источник объявляет член с платформенной ссылкой, тип с тем же именем зарегистрирован конфигурационным, члены подмешиваются в цель — у подмешанного члена ожидается каноническая ссылка. Без правки тест красный, проверил.
|



Первый из трёх стека, выделенного из #4348.
Что было
Шаблон вида «СправочникСсылка.<Имя справочника>» приходит из синтакс-помощника платформенным, а конфигурационные типы регистрируются своим видом. Структурная специализация сохраняла вид шаблона, поэтому за одним именем оказывались две ссылки, различающиеся только видом: объединение наборов их не схлопывало и тип показывался дважды.
Что стало
Ссылки специализированных членов приводятся к зарегистрированным в реестре. Приведение:
TypeSet.mapRefs);registerSpecialization: перегрузка без приведения удалена, чтобы неканонические ссылки не попадали в members другими путями.Проверка
TypeRegistrySpecializationTest,TypeSetTest(перенос декораций, вложенные наборы, ленивые),MemberDescriptorSpecializeTest. Прогнан пакетtypes.🤖 Generated with Claude Code
https://claude.ai/code/session_01MnWpDdNSEFohPZHG6voe6y
Summary by CodeRabbit
Bug Fixes
Tests