Как получить IDeclaredType из IClrDeclaredElement с помощью Resharper SDK

Я пишу навигационный плагин для Resharper, и моя ситуация такова, что у меня есть список IDeclaredElement что я получил от выполнения

var declaredElements = context.GetData(DataConstants.DECLARED_ELEMENTS)

Этот элемент является элементом, на котором пользователь имеет курсор мыши.

Что я хотел бы сделать, это получить IDeclaredType объявленного элемента, включая любые параметры типа, которые он может иметь, в случае, если это универсальный тип.

Документация по reharper SDK довольно проста, когда речь идет о системе типов, и на самом деле вообще не объясняет взаимосвязь между различными типами.

Я искал другие плагины, чтобы попытаться найти примеры этого, но они оказались пустыми. Я проверил все классы Util и Extension, чтобы узнать, есть ли где-нибудь метод, который дает мне то, что я хочу, но ничего нет.

О единственном, что я нашел, было это:

declaredElements.First().GetSuperTypes()

который возвращает иерархию типов, исключая текущий тип. Полезно, но не то, что я ищу.

Кто-нибудь имеет опыт работы с этим API или понимает, как он работает? Я хотел бы получить ответ, который немного объяснит отношения между типами.

Мое понимание этого, вкратце:

  • Типы с объявленным в названии (IDeclaredElement, IDeclaredType) появляются для обозначения физических элементов кода.
  • IType представляется интерфейсом верхнего уровня для всех типов, который не соответствует физическим элементам кода
  • Мне неясно, что означает тип с элементом в имени (ITypeElement, IDeclaredElement) Возможно, это относится к элементам AST.

Я хотел бы получить разъяснения по этому вопросу.

2 ответа

Решение

Я добавил проблему, чтобы обновить ее в документации: https://github.com/JetBrains/resharper-devguide/issues/4

Я постараюсь дать объяснение в горшке здесь.

ITreeNode Иерархия типов определяет абстрактное синтаксическое дерево вашего кода. Это предоставляет много информации, но очень низкоуровневое - оно отображается непосредственно на необработанный текст кода. Это также пропускает некоторую информацию более высокого уровня. Например, если я хочу получить все члены типа для объявления класса, я могу пройти AST для класса и собрать все соответствующие узлы дерева, но тогда мне также придется обрабатывать частичные классы, а AST не предоставляет никакой информации для поиска других частей класса. Точно так же, если я вижу объявление класса public class Foo : BarЯ должен был бы сделать ручное разрешение Bar базовый тип.

IDeclaredElement иерархия типов - это, по сути, семантическое представление синтаксического дерева. На самом простом уровне объявленный элемент - это "то, что имеет объявления". Это может быть объявление класса, или объявление метода, или что-то, даже не связанное с кодом - элементы HTML, классы CSS и даже цвета и пути файловой системы (вот почему он называется "элемент" - ему нужно имя, которое может применяться к много разных вещей).

Например, типы CLR представлены ITypeElement интерфейс, который происходит от IDeclaredElement, Он предоставляет методы и свойства для получения объявленных элементов для методов, свойств, конструкторов и т. Д. Целевого типа. Таким образом, (почти) возможно обеспечить семантическое представление исходного проекта CLR исключительно в терминах объявленных элементов. Почти, но не совсем.

Заявленные элементы имеют GetDeclarations метод, который обеспечивает IDeclaration узлы синтаксического дерева, которые являются объявлениями для объявленного элемента. Точно так же IDeclaration узел обеспечивает DeclaredElement свойство, чтобы иметь возможность получить объявленный элемент из узла.

Кроме того, ReSharper имеет очень мощный механизм, называемый ссылками, который позволяет узлу дерева иметь исходящую ссылку, которая разрешает объявленный элемент (также может не разрешиться, что является ошибкой, такой как использование метода, которого еще нет записано, или он может разрешить более одного элемента, например, используя метод без указания, какая это перегрузка). Эти ссылки могут быть применены к любому узлу, такому как имя переменной, ссылающейся на объявление переменной, или Bar в public class Foo : Bar имея ссылку на объявленный элемент Bar (из которого можно получить IDeclaration и исходный код Bar).

Это обеспечивает впечатляющий набор функций - синтаксическое представление файла кода, семантическое представление объявлений кода и ссылки, объединяющие все вместе, но это еще не все. Объявленный элемент обеспечивает семантическое представление объявленной вещи, но не предназначен для представления всех сценариев использования.

В частности (с учетом типов CLR), он не может представлять использование типа в виде массива, указателя или закрытого универсального типа. ITypeElement может обеспечить семантическое представление о классе Foo или же Bar<T>, но это не может представлять Foo[], или же Bar<Quux>,

Объявленные элементы должны иметь возможность моделировать эти сценарии использования в качестве базовых классов, сигнатур методов и т. Д. Для этого производные объявленные элементы (такие как ITypeElement) использовать дополнительную интерфейсную иерархию для представления этой информации "системы типов". Эта иерархия зависит от анализируемого языка. Для типов CLR это IType иерархия, для JavaScript это IJavaScriptType,

это IType является дополнительной информацией, а не заменой семантического представления объявленного элемента. IType может возвращать таблицу символов всех членов типа, но не предоставляет средства доступа таким же образом, как ITypeElement делает. Вместо этого (и в зависимости от того, что моделируется) IType по сути является оберткой вокруг объявленного элемента и экземпляра ISubstitution, который обеспечивает замены для параметров общего типа (массив представлен в виде System.Array тип с базовым типом элемента, который сам по себе является IType, так как это может быть закрытый универсальный или другой массив). Подстановка также может быть пустой подстановкой, которая ничего не заменяет, учитывая типы, представленные в виде открытых универсальных элементов, или типы, которые не являются универсальными. IDeclaredType интерфейс является IType это относится к объявленному элементу.

В сторону: разрешение ссылок фактически разрешает объявленный элемент и ISubstitutionОпять же, для моделирования дженериков. При разрешении ссылки для сигнатуры объявления метода необходимо знать, что это IList<T>, и что T является.

Для того, чтобы получить IType Например, вам нужно либо получить его из существующего объявленного элемента (сигнатура метода, базовый класс и т. д.), либо создать его с помощью TypeFactory.CreateType, Скорее всего, вам нужно будет указать ISubstitutionТоже если это универсальный тип. Вы также можете получить набор общих, "предопределенных" типов, используя:

var type = psiModule.GetPredefinedType(context).String; 

Вы можете использовать эти типы, чтобы перейти в один из TypeFactory.CreateType методы действовать как параметры типа для ITypeElement Вы также проходите.

Итак, в результате мы объявляем класс в источнике, это дает нам ITreeNode, IDeclaration а также ITypeDeclaration, Мы можем использовать IDeclarationили разрешите ссылки, чтобы получить семантическое представление этого объявления, IDeclaredElement, с ITypeElement являясь производным интерфейсом, представляющим класс. Использование объявленных элементов на основе CLR IType для представления использования типов, таких как базовые классы, которые, возможно, должны быть закрытыми, или параметры методов, которые могут быть открытыми, или массивы. IDeclaredType это использование типа, которое может вернуть нас к объявленному элементу. И типы часто внутренне представлены с объявленным элементом и ISubstitution, который может заполнять любые общие параметры или заменять идентификатор, когда нет общих параметров. И, наконец, вы можете получить IType с помощью TypeFactory.CreateType или используя свойства на PredefinedType,

Проверьте этот код:

void Do(IDataContext dataContext)
{
  foreach (var reference in dataContext.GetData(DataConstants.REFERENCES))
  {
    var resolveResultWithInfo = reference.Resolve().Result;
    var typeElement = resolveResultWithInfo.DeclaredElement as ITypeElement;
    if (typeElement != null)
    {
      var substitution = resolveResultWithInfo.Substitution;
      var declaredType = TypeFactory.CreateType(typeElement, substitution);
    }
  }
}
Другие вопросы по тегам