Как получить 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);
}
}
}