Skip to content

Недетерминированный резолв (mdoRef, moduleType) при расширениях: documentsByMDORef last-write-wins → мигающие FP UnusedLocalVariable/UnusedLocalMethod #4266

Description

@sfaqer

Описание

При наличии расширений в 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).

Механизм

  1. MDClasses.createSolution читает основную конфигурацию и расширения — у обоих модулей менеджера один mdoRef.
  2. Индекс ссылок заполняется корректно (строковые ключи (mdoRef, moduleType, scopeName, symbolName), без резолва документов).
  3. Ломается чтение: getReferencesTo(переменная) → вхождения найдены → buildReferencegetSourceDefinedSymbolserverContext.getDocument(mdoRef, moduleType) → возвращается не тот файл. Если для модуля расширения победил базовый — symbolTree.getMethodSymbol("МетодТолькоВРасширении") пуст, фолбэк на модульную переменную пуст → Optional.empty без исключения → все references переменной выпадают → «переменная не используется».
  4. Симптом: 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).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions