Diagnostic
诊断信息是个非常重要的功能,它可以帮助我们在编写代码的时候发现错误和可能有问题的地方,从而提高我们的编码效率。
为了让用户体验尽可能的好,我们的lsp分析需要尽量容忍用户的错误输入,尽可能多的分析出用户代码中的问题
Fault Tolerance
错误容忍是生成好的诊断信息的前提。在pivot-lang的中,我们分别在两个层面上实现了错误容忍:
- parser
- ast
Parser的错误容忍
nom parser架构中,如果出现了一个无法被识别的语句,整个分析器就会终止分析输出错误。这对于错误容忍的要求来说是无法被接受的。所以 我们的编译器不使用nom parser的默认错误处理机制,任何parser阶段产生的nom error都应该被视作bug,我们应该尽可能的避免这种情况。
在parse过程中,如果一些错误语句能非常明显的被识别为一个语法的未完成项(且没有歧义),我们应该将它识别为该语法类型的Node,并且在Node上加一个
flag标识它不完整(常常是is_complete
),这样在ast阶段我们就能输出对应的诊断信息。
对于最常见的基础语法单位statement
和top_statement
,parser提供了一个helper函数except
,能够在遇到不可被识别的错误语句的时候
将该“块”语句识别为ErrNode
,以方便后续的分析正常进行。
!!!注意:ErrNode虽然很好用,但是它只能输出很宽泛的诊断信息(比如无法识别该语句),它是最后的错误容忍手段,应该尽量避免使用它
AST的错误容忍
由于parse阶段的时候能够容忍错误的语句,对于一些语法错误,ast节点只需要检查自身的完整性就能够输出诊断信息了。而对于语义错误(例如类型不匹配),我们需要在ast阶段
进行分析并获取结果。这些操作目前是在各个节点的emit
函数里进行的。所有的emit
函数都返回NodeResult
类型,如果该节点的emit
中出现了错误,
分析将会中断,其对应的错误信息会被添加到ctx
(编译上下文)中并且作为error
返回。上层函数如果遇到自己依赖的函数报错一定不能重复添加该错误至ctx
中,
否则会导致错误信息重复输出。上层函数处理自己的依赖报错有两种情况:
- 直接停止分析并将该错误传递给自己的上级
- 忽略该错误继续进行分析
一般来说,大部分的expression和statement都会采用第一种方案,而statement block则会采用第二种方案