Описание
При наличии расширений в workspace резолв определения символа через documentsByMDORef недетерминирован: у заимствованного объекта расширения mdoRef и moduleType совпадают с базовым («Справочник.Х» + ManagerModule), а documentsByMDORef — карта «(mdoRef, moduleType) → документ» с last-write-wins. Базовый модуль и его копия из расширения борются за один слот; победитель определяется порядком параллельного populateContext (files.parallelStream()), т.е. меняется от запуска к запуску.
Похоже, объясняет #3948 дословно (FP UnusedLocalVariable в модуле менеджера расширения, ругается пачкой по методу; плагин 1.18.0 / LS 0.29.0, один sonar.sources).
Механизм
MDClasses.createSolution читает основную конфигурацию и расширения — у обоих модулей менеджера один mdoRef.
- Индекс ссылок заполняется корректно (строковые ключи
(mdoRef, moduleType, scopeName, symbolName), без резолва документов).
- Ломается чтение:
getReferencesTo(переменная) → вхождения найдены → buildReference → getSourceDefinedSymbol → serverContext.getDocument(mdoRef, moduleType) → возвращается не тот файл. Если для модуля расширения победил базовый — symbolTree.getMethodSymbol("МетодТолькоВРасширении") пуст, фолбэк на модульную переменную пуст → Optional.empty без исключения → все references переменной выпадают → «переменная не используется».
- Симптом: FP сразу по всем переменным методов, которых нет в модуле-победителе; следующий запуск — другой победитель — диагностика исчезла. Мигает при единственном source-каталоге и единственном контексте сервера. Симметрично страдает базовый модуль, когда побеждает расширение, и
UnusedLocalMethod-семейство (резолв определения идёт тем же путём).
В LSP/MCP механизм живёт так же: populate параллельный на каждом старте сервера, расширения в workspace — норма. Складывается с гонками, которые чинит #4261, но им не устраняется (это не локи и не заморозка).
Предлагаемое исправление
Для Variable/Method-скоупов документ определения известен точно — это документ самого вхождения (локальная переменная/неэкспортный метод не видны из другого файла): резолвить определение по URI вхождения, а не через глобальную карту documentsByMDORef. Дополнительно/альтернативно — учитывать принадлежность расширению в ключе индекса: сейчас вхождения базы и расширения смешаны в одном bucket'е, что даёт ещё и взаимную маскировку (references одного файла «засчитываются» другому).
Контекст
Разбор в обсуждении PR #4261: #4261 (comment)
Связано: #3948. Проверено по v0.29.0 и текущему develop; mdoRef заимствованных объектов сверен по mdclasses (createSolution, SolutionTest).
Описание
При наличии расширений в workspace резолв определения символа через
documentsByMDORefнедетерминирован: у заимствованного объекта расширения mdoRef и moduleType совпадают с базовым («Справочник.Х» +ManagerModule), аdocumentsByMDORef— карта «(mdoRef, moduleType) → документ» с last-write-wins. Базовый модуль и его копия из расширения борются за один слот; победитель определяется порядком параллельногоpopulateContext(files.parallelStream()), т.е. меняется от запуска к запуску.Похоже, объясняет #3948 дословно (FP
UnusedLocalVariableв модуле менеджера расширения, ругается пачкой по методу; плагин 1.18.0 / LS 0.29.0, одинsonar.sources).Механизм
MDClasses.createSolutionчитает основную конфигурацию и расширения — у обоих модулей менеджера один mdoRef.(mdoRef, moduleType, scopeName, symbolName), без резолва документов).getReferencesTo(переменная)→ вхождения найдены →buildReference→getSourceDefinedSymbol→serverContext.getDocument(mdoRef, moduleType)→ возвращается не тот файл. Если для модуля расширения победил базовый —symbolTree.getMethodSymbol("МетодТолькоВРасширении")пуст, фолбэк на модульную переменную пуст →Optional.emptyбез исключения → все references переменной выпадают → «переменная не используется».UnusedLocalMethod-семейство (резолв определения идёт тем же путём).В LSP/MCP механизм живёт так же: populate параллельный на каждом старте сервера, расширения в workspace — норма. Складывается с гонками, которые чинит #4261, но им не устраняется (это не локи и не заморозка).
Предлагаемое исправление
Для Variable/Method-скоупов документ определения известен точно — это документ самого вхождения (локальная переменная/неэкспортный метод не видны из другого файла): резолвить определение по URI вхождения, а не через глобальную карту
documentsByMDORef. Дополнительно/альтернативно — учитывать принадлежность расширению в ключе индекса: сейчас вхождения базы и расширения смешаны в одном bucket'е, что даёт ещё и взаимную маскировку (references одного файла «засчитываются» другому).Контекст
Разбор в обсуждении PR #4261: #4261 (comment)
Связано: #3948. Проверено по v0.29.0 и текущему develop; mdoRef заимствованных объектов сверен по mdclasses (
createSolution,SolutionTest).