如何使用Resharper SDK从IClrDeclaredElement获取IDeclaredType

时间:2014-11-07 04:51:19

标签: c# resharper-sdk

我正在为Resharper编写导航插件,我的情况是我有一个IDeclaredElement列表,我从中获取

var declaredElements = context.GetData(DataConstants.DECLARED_ELEMENTS)

此元素是用户将鼠标光标置于其上的元素。

我想要做的是获取声明元素的IDeclaredType,包括它可能具有的任何类型参数,如果它是通用类型。

The resharper SDK documentation is quite light when it comes to the type system,并没有真正解释各种类型之间的关系。

我已经在其他插件周围寻找试图找到这个例子的例子,但是已经空了。我已经检查过每一个Util和Extension课程,看看是否有某种方法可以提供我想要的东西,但是没有。

关于我发现的唯一一件事是:

declaredElements.First().GetSuperTypes()

返回类型层次结构,不包括当前类型。有用,但不是我想要的。

有没有人对此API有任何经验或了解它的工作原理?我喜欢一个解释类型之间关系的答案。

我对它的理解,简而言之:

  • 名称(IDeclaredElement, IDeclaredType)中带有声明的类型似乎是指物理代码元素。
  • IType似乎是所有类型的顶级界面,与物理代码元素不对应
  • 我不清楚名称(ITypeElement, IDeclaredElement)中带有Element的类型的含义,也许它指的是AST元素。

对此有所澄清。

2 个答案:

答案 0 :(得分:9)

我在文档中添加了一个问题,以便更新:https://github.com/JetBrains/resharper-devguide/issues/4

我试着在这里提供一个盆栽的解释。

ITreeNode类型层次结构定义代码的抽象语法树。这提供了大量信息,但是非常低级 - 它直接映射到代码的原始文本。它也错过了一些更高级别的信息。例如,如果我想获取类声明的所有类型成员,我可以为该类运行AST,并收集所有适当的树节点,但是我还必须处理部分类,并且AST提供没有关于定位班级其他部分的信息。同样,如果我看到类声明public class Foo : Bar,我将不得不手动解析Bar基类型。

IDeclaredElement类型层次结构本质上是语法树的语义视图。在最简单的层面上,声明的元素是#34;具有声明的东西"。这可以是类声明,也可以是方法声明,或甚至与代码无关的东西 - HTML元素,CSS类甚至颜色和文件系统路径(这就是它被称为"元素" - 它需要一个可以应用于许多不同的事物的名称)。

例如,CLR类型用ITypeElement接口表示,该接口派生自IDeclaredElement。它提供了获取目标类型的方法,属性,构造函数等的声明元素的方法和属性。因此,(几乎)可以仅根据声明的方式提供CLR源项目的语义视图元素。几乎,但不完全。

声明的元素具有GetDeclarations方法,该方法提供IDeclaration语法树节点,这些节点是声明元素的声明。同样,IDeclaration节点提供DeclaredElement属性,以便能够从节点获取声明的元素。

此外,ReSharper有一个非常强大的机制叫做引用,它允许树节点有一个传出引用,它将解析为一个声明的元素(它也可能无法解析,这是一个错误,例如使用一个方法,它不是&尚未编写,或者它可以解析为多个元素,例如使用不限定哪个重载的方法)。这些引用可以应用于任何节点,例如引用变量声明的变量名称,或Barpublic class Foo : Bar引用Bar的声明元素(来自哪个)可以获得IDeclarationBar的源代码。

这提供了一组令人印象深刻的功能 - 代码文件的语法视图,代码声明的语义视图以及将所有内容连接在一起的引用,但这并不涵盖所有内容。声明的元素提供声明的 thing 的语义视图,但不是用于表示所有使用场景。

具体而言(查看CLR类型),它不能将类型的用法表示为数组,指针或封闭泛型类型。 ITypeElement可以提供类FooBar<T>的语义视图,但它不能代表Foo[]Bar<Quux>

声明的元素需要能够将这些使用场景建模为基类,方法签名等。为此,派生的声明元素(如ITypeElement)使用额外的接口层次结构来表示&#34;输入系统&#34;信息。此层次结构取决于所分析的语言。对于CLR类型,它是IType层次结构,对于JavaScript,它是IJavaScriptType

这个IType是附加信息,而不是声明元素的语义视图的替代。 IType可以返回所有类型成员的符号表,但不会像ITypeElement那样提供访问者。相反,(并且取决于被建模的内容)IType本质上是声明元素和ISubstitution实例的包装器,它为泛型类型参数提供替换(表示数组)作为System.Array类型,具有底层元素类型,它本身是IType,因为它可能是一个封闭的泛型或另一个数组)。替换也可以是空替换,它不能替代任何东西,允许表示为开放式泛型的类型,或者根本不是通用的类型。 IDeclaredType接口是IType,引用声明的元素。

  

除此之外:解析引用实际上解析为声明的元素,而ISubstitution再次解析为泛型。在解析方法声明签名的引用时,您需要知道它是IList<T>以及T是什么。

为了获得IType实例,您需要从现有的声明元素(方法签名,基类等)中获取一个实例,或者使用TypeFactory.CreateType创建它。如果它是通用类型,您很可能也需要指定ISubstitution。你也可以得到一堆常见的,#34;预定义的&#34;类型,通过:

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

您可以使用这些类型传递到其中一个TypeFactory.CreateType方法,作为您传入的ITypeElement的类型参数。

所以,结果是,我们在源代码中声明了一个类,这给了我们ITreeNodeIDeclarationITypeDeclaration。我们可以使用IDeclaration或解析引用来获取此声明的语义视图IDeclaredElement,其中ITypeElement是表示类的派生接口。基于CLR的声明元素使用IType来表示类型用法,例如可能需要是封闭泛型的基类,或者可能是开放泛型或数组的方法参数。 IDeclaredType是一种类型用法,可以让我们回到声明的元素。并且类型通常在内部用声明的元素和ISubstitution表示,它可以填充任何通用参数,或者是没有泛型参数的ID替换。最后,您可以使用IType或使用TypeFactory.CreateType上的属性获得PredefinedType

答案 1 :(得分:2)

查看此代码:

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);
    }
  }
}