Roslyn 语法树中的各种语法节点及每个节点的含义

使用 Roslyn 进行源码分析时,我们会对很多不同种类的语法节点进行分析。如果能够一次性了解到各种不同种类的语法节点,并明白其含义和结构,那么在源码分析的过程中将会更加得心应手。

本文将介绍 Roslyn 中各种不同的语法节点、每个节点的含义,以及这些节点之间的关系和语法树结构。


本文内容

基本概念

using System;

namespace Walterlv.Demo
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello Walterlv!");
        }
    }
}

以上是一个非常简单但完整的 .cs 文件。

在 Roslyn 的解析中,这就是一个“编译单元”(Compilation Unit)。编译单元是 Roslyn 语法树的根节点。紧接着的 using System 是 using 指令(Using Directives);随后是命名空间声明(Namespace Declaration),包含子节点类型声明(Class Declaration);类型声明包含子节点方法声明(Method Declaration)。

接下来,我们会介绍 Roslyn 语法树中各种不同种类的节点,以及其含义。

语法节点

语法树

CompilationUnit,是语法树的根节点。

关键字

UsingKeyword、NamespaceKeyword、PublicKeyword、InternalKeyword、PrivateKeyword、ProtectedKeyword、StaticKeyword、ClassKeyword、InterfaceKeyword、StructKeyword。

分别是 C# 的各种关键字:usingnamespacepublicinternalprivateprotectedstaticclassinterfacestruct

InKeyword、OutKeyword、RefKeyword、ReturnKeyword、ConstKeyword、DefaultKeyword。

分别是 C# 的另一波关键字 inoutrefreturnconstdefault

ByteKeyword、CharKeyword、IntKeyword、LongKeyword、BoolKeyword、FloatKeyword、DoubleKeyword、DecimalKeyword。

分别是 C# 中的基元类型关键字bytecharintlongboolfloatdoubledecimal。需要注意的是,var 和 dynamic 并不是基元类型关键字,在语法节点中,它是 IdentifierName。

AsyncKeyword、AwaitKeyword。

分别是 asyncawait 关键字。

TrueKeyword、FalseKeyword。

分别是 true 和 false 关键字。

LockKeyword、CheckedKeyword、UncheckedKeyword、UnsafeKeyword、FixedKeyword。

分别是 lockcheckeduncheckedunsafefixed 关键字。

符号

DotToken、SemicolonToken、OpenBraceToken、CloseBraceToken、LessThanToken、GreaterThanToken、OpenParenToken、CloseParenToken。

分别是 C# 中的各种符号:.;{}<>()

空白

EndOfLineTrivia 表示换行,WhitespaceTrivia 表示空格,EndOfFileToken 表示文件的末尾。

通常,这两个语法节点会在另一个节点的里面,作为另一个节点的最后一部分。比如 using Walterlv.Demo; 是一个 UsingDirective,它的最后一个节点 Semicolon 中就会包含换行符 EndOfLineTrivia。

指令

UsingDirective 是 using 指令。一个 using 指令包含一个 UsingKeyword,一个 QualifiedName 和一个 Semicolon(;)。

声明

NamespaceDeclaration、ClassDeclaration、MethodDeclaration、PropertyDeclaration、FieldDeclaration、VariableDeclaration。

分别是命名空间、类型、方法、属性、。

其中,属性声明包含一个 AccessorList,即属性访问器列表,访问期列表可以包含 GetAccessorDeclaration(属性 get)、SetAccessorDeclaration(属性 set)的声明。

这些声明通常是嵌套存在的。例如一个常规的文件的第 0、1 级语法节点通常是这样的:

  • CompilationUnit

    • UsingDirective
    • UsingDirective
    • NamespaceDeclaration
    • EndOfFileToken

类型声明是命名空间声明的子节点,类型成员的声明是类型声明的子节点。

名称和标识符

  • QualifiedName

    • 限定名称,可以理解为完整的名称。
    • 例如命名空间 Walterlv.DemoTool 的限定名称就是这个全称 Walterlv.DemoTool;类型 Walterlv.DemoTool.Foo 的限定名称也是这个全程 Walterlv.DemoTool.Foo。
  • IdentifierName
    • 标识名称,当前上下文下的唯一名称。
    • 例如 Walterlv 和 DemoTool 都是 Walterlv.DemoTool 这个命名空间的标识符。
  • IdentifierToken
    • 标识符,具体决定 IdentifierName 的一个字符串。
    • 这其实与 IdentifierName 是一样的意思,但是在语法树上的不同节点。
  • GenericName
    • 泛型名称,即 Foo 这种。

特性

AttributeList、Attribute。

一个允许添加特性的地方,如果添加了特性,那么可以得到 AttributeList 节点,内部包含了多个 Attribute 子节点。

形参和实参

形参是 parameter,实参是 argument。前者是定义的参数,后者是实际传入的参数。

语法节点中有两种不同的形参和实参,一个是泛型,一个是普通参数。

  • ParameterList

    • 形参列表,出现在方法声明中,即 void Foo(string a, bool b) 中的 (string a, bool b) 部分。
  • Parameter
    • 形参,即以上例子中的 string a 和 bool b 部分。
  • ArgumentList
    • 实参列表,出现在方法调用中,即 this.Foo(a, b) 中的 (a, b) 部分。
  • Argument
    • 实参,即以上例子中的 a 和 b 部分。
  • TypeParameterList
    • 泛型形参列表,出现在类型声明或者方法声明中,即 void Foo<T1, T2>(string a) 中的 <T1, T2> 部分。
  • TypeParameter
    • 泛型形参,即以上例子中的 T1 和 T2 部分。
  • TypeArgumentList
    • 泛型实参列表,出现在使用泛型参数的地方,例如 this.Foo<T1, T2>() 中的 <T1, T2> 部分。
  • TypeArgument
    • 泛型实参,即以上例子中的 T1 和 T2 部分。

语句块

  • Block

    • 即用 { 和 } 包裹的语句代码。
    • 当然并不是所有 { 和 } 包裹的都是语句(例如类型声明就不是),里面真正有代码时才是语句。
  • EqualsValueClause
    • 等号子句,例如 = null。我们经常称之为“赋值”语句。

语句

一个语句是指包含分号在内的实际执行的句子。

  • LocalDeclarationStatement

    • 本地变量声明语句,即 var a = 0; 这样的句子;其中,去掉分号的部分即前面我们提到的变量声明 VariableDeclaration。
    • 一个本地变量声明的语句也可以不包含赋值。
  • ExpressionStatement
    • 表达式语句,即 this.Foo(); 这样的一次方法调用。如果去掉分号,剩下的部分是表达式(Expression)。
  • IfStatement
    • if 语句,即一个完整的 if-else if-else
  • ForStatement
    • for 语句。
  • ForEachStatement
    • for 语句。
  • WhileStatement
    • while 语句,即一个完整的 while
  • DoStatement
    • do-while 语句。
  • DefaultStatement
    • default(); 语句。
  • ReturnStatement
    • return 语句。
  • CheckedStatement
    • checked 语句。
  • UncheckedStatement
    • checked 语句。
  • UnsafeStatement
    • unsafe 语句。
  • FixedStatement
    • unsafe 语句。

表达式

  • EqualsExpression

    • 相等判断表达式,即 a == b
  • InvocationExpression
    • 调用表达式,即 Class.Method(xxx) 或 instance.Method(xxx) 这种完整的调用。
  • SimpleMemberAccessExpression
    • 这是 InvocationExpression 的子节点,是方法调用除去参数列表的部分,即 Class.Method 或 instance.Method
    • 如果是获取属性(没有参数列表),那么也是这个节点。
  • AwaitExpression
    • await 表达式,即 await this.Foo() 这样的调用。
  • DefaultExpression
    • default() 表达式。
  • TrueLiteralExpression
    • true 表达式。
  • FalseLiteralExpression
    • false 表达式。
  • ParenthesizedLambdaExpression
    • 带括号的 lambda 表达式,例如:
    • () => xxx(a) => xxx(a, b) => xxx(int a, string b) => xxx
    • () => { }(a) => { }(a, b) => { }(int a, string b) => { }
  • SimpleLambdaExpression
    • 不带括号的 lambda 表达式,例如:
    • a => xxxa => { }

基元类型

PredefinedType 是所有基元类型的节点。它的子节点可能是 BoolKeyword、StringKeyword 或其它基元类型的关键字。

C# 内建类型

NullableType、TupleType、ArrayType。

这三个分别是 C# 中语法级别支持的类型,分别是可空类型、元组类型和数组类型。

  • NullableType

    • 即 bool? 这种用于创建 Nullable<bool> 的语法。
  • TupleType
    • 即 (bool, string) 这种用于创建 ValueTuple<bool, string> 的语法。
  • ArrayType
    • 即 [] 这种用于创建数组类型的语法。

本文会经常更新,请阅读原文: https://blog.walterlv.com/post/roslyn-syntax-tree-nodes.html ,以避免陈旧错误知识的误导,同时有更好的阅读体验。

本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名 吕毅 (包含链接: https://blog.walterlv.com ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请 与我联系 ([email protected]) 。

原文地址:https://www.cnblogs.com/bigmango/p/11407268.html

时间: 2025-01-15 00:00:34

Roslyn 语法树中的各种语法节点及每个节点的含义的相关文章

MySQL用户、库、表的操作语法 关于用户的语法

MySQL用户.库.表的操作语法 关于用户的语法 查看当前系统里的用户: select user,host from mysql.user; 查看当前登陆的用户 select user(); 创建用户 语法:create user '用户'@'主机' identified by '密码'; 示例:create user 'anuo'@'localhost' identified by '123' 创建用户并授权 grant 权限 on 库.表 to '用户名'@'主机域' identified

Oracle树查询(查询所有子节点,父节点等等)_转载

Oracle树查询(查询所有子节点,父节点等等) 转载 2016年01月14日 10:11:55 Oracle树查询的最重要的就是select...start with... connect by ...prior 语法了.依托于该语法,我们可以将一个表形结构的中以树的顺序列出来.在下面列述了Oracle中树型查询的常用查询方式以及经常使用的与树查询相关的Oracle特性函数等,在这里只涉及到一张表中的树查询方式而不涉及多表中的关联等. 以我做过的一个项目中的表为例,表结构如下: Sql代码 1

父节点和子节点事件重复绑定(如click)

以click事件为例,当父节点或子节点绑定了click事件后,点击该节点,事件会发生冒泡传递,即会造成事件重复绑定,这时候需要阻止事件的冒泡,采用stopPropagation 或cancelBubble等. stopPropagation() 方法 定义和用法 不再派发事件. 终止事件在传播过程的捕获.目标处理或起泡阶段进一步传播.调用该方法后,该节点上处理该事件的处理程序将被调用,事件不再被分派到其他节点. 语法 event.stopPropagation() 说明 该方法将停止事件的传播,

JavaScript基础及函数,BOM,DOM的节点及DOM节点操作

使用js的三种方式 1.直接在HTML标签中,使用事件属性,调用js代码: <button onclick="alert('点我呀')">点我啊!<tton> 2.在页面的任意位置,使用script标签 <script type="text/javascript"> alert('哈哈哈') </script> 3.外部:使用script标签 src属性选择外部地址, type属性选择"text/javasc

二叉搜索树建立、插入、删除、前继节点、后继节点之c++实现

一.前言 一直以来,都对树有关的东西望而却步.以前每次说要看一看,都因为惰性,时间就那么荒废掉了.今天下个决心,决定好好的数据结构中的东西看一下.不知道看这篇文章的你,是不是和我有同样的感受,空有一颗努力的心,却迟迟没有付出行动.如果是的话,如果也想好好的把树的知识巩固一下的话,就让我们一起好好儿地把知识点过一遍吧.本文争取让看完的每一个没有基础的同学,都能有所收获.在正文开始前,先给自己加个油.加油(^ω^) 二.二叉搜索树的定义 二叉搜索树是指,对于某一个节点而言,它左边的节点都小于或等于它

LeetCode:Path Sum - 树的根节点到叶节点的数字之和

1.题目名称 Path Sum(树的根节点到叶节点的数字之和) 2.题目地址 https://leetcode.com/problems/path-sum/ 3.题目内容 英文:Given a binary tree and a sum, determine if the tree has a root-to-leaf path such that adding up all the values along the path equals the given sum. 中文:给定一颗二叉树,如

16、Cocos2dx 3.0游戏开发找小三之Node:父节点、子节点、傻傻分不清楚

重开发者的劳动成果,转载的时候请务必注明出处:http://blog.csdn.net/haomengzhu/article/details/30476133 Cocos2d-x 采用了场景.层.精灵的层次结构来组织游戏元素, 与此同时,这个层次结构还对应了游戏的渲染层次,因此游戏元素可以组织成树形结构,称作渲染树. Cocos2d-x 把渲染树上的每一个游戏元素抽象为一个节点,即 Node. 一切游戏元素都继承自 Node,因此它们 都具有 Node 所提供的特性. Node 定义了一个可绘制

二叉树(12)----查找两个节点最低祖先节点(或最近公共父节点等),递归和非递归

1.二叉树定义: typedef struct BTreeNodeElement_t_ { void *data; } BTreeNodeElement_t; typedef struct BTreeNode_t_ { BTreeNodeElement_t *m_pElemt; struct BTreeNode_t_ *m_pLeft; struct BTreeNode_t_ *m_pRight; } BTreeNode_t; 2.查找二叉树中两个节点的最低祖先节点(或最近公共父节点等) 最低祖

求二叉搜索树的前驱节点和后继节点

前驱结点:节点val值小于该节点val值并且值最大的节点 后继节点:节点val值大于该节点val值并且值最小的节点 二叉树的节点val值是按照二叉树中序遍历顺序连续设定. 前驱结点 如图4的前驱结点是3 2的前驱结点是1 6的前驱结点是5 后继节点 7的后继结点是8 5的后继节点是6 2的后继节点是3 前驱节点 若一个节点有左子树,那么该节点的前驱节点是其左子树中val值最大的节点(也就是左子树中所谓的rightMostNode) 若一个节点没有左子树,那么判断该节点和其父节点的关系 2.1 若