根本的问题是编译器诊断处理你没有写的东西。
为了给你一个有意义的错误信息,编译器必须猜测你的意思,然后告诉你你的代码与那个有什么不同。
如果您缺少分号,编译器显然无法在任何地方看到该分号。当然,它可以做的一件事是猜测“也许用户缺少分号。毕竟这是一个常见的错误”。但是那个分号应该在哪里呢?因为你犯了一个错误,代码无法解析成语法树,所以没有明确的指示“这个节点从树中丢失”。并且可能有不止一个地方可以插入分号,以便正确解析周围的代码。此外,一旦发现可能的错误,您将尝试解析/重新编译多少代码?编译器可以插入分号,但至少它必须重新开始解析该代码块。但也许它在代码中进一步引入了错误。所以也许应该重新编译整个程序,只是为了确保编译器提出的修复实际上是正确的。但这也不是一个选择。时间太长了。
假设你有一些这样的代码:
struct foo {
...
}
void bar();
这里有什么错误?看着它,你和我会说“你在类定义之后缺少分号”。但是编译器怎么知道呢?void
可能是错字。也许您实际上打算编写 type 实例的名称foo
。那么真正的错误将是它后面跟着现在看起来像函数调用的东西。
所以编译器必须猜测。“这看起来可能是一个类定义,然后它看起来像一个类型的名称。如果这是真的,用户缺少一个分号来分隔它们”。
猜测并不是一门非常精确的科学。而且事情变得更加复杂,因为每次编译器试图变得聪明并进行猜测时,如果猜测错误,它只会增加混乱。
因此,有时,最好输出一个简短的消息,只说明我们确定的内容(例如,类定义后面不能跟类型名称)。这不如说“您在类定义后缺少分号”有用,但如果编译器猜错了,危害较小。
如果它告诉您缺少分号,而错误实际上是其他内容,那只会误导您。因此,在最坏的情况下,一个简洁且不太有用的错误消息可能会更好,即使它在最好的情况下并不那么好。
编写好的编译器错误并不容易,尤其是在像 C++ 这样杂乱无章的语言中。但话虽如此,一些编译器(包括 MSVC 和 GCC)可能要好得多。我相信更好的编译器诊断是 Clang 的主要目标之一。