为什么C++编译器在行之后而不是在行上给出错误?

Cha*_*dia 9 c++ compiler-construction grammar compilation

今天我正在与我的编译器发生另一项国内事务时,这个问题突然出现在我的脑海中.尽管我的粉红色(由于我在工作时所做的所有分号按压),我在一个if声明之前设法错过了一个.显然,这导致编译错误:

错误C2143:语法错误:缺少';' 在"如果"之前

所以我在想"好哎呀,你为什么不能告诉我缺少分号,而不是行的行之后的问题." 然后我开始尝试其他类似的语法错误:

错误C2065:'myUndeclared':未声明的标识符

错误C2143:语法错误:在'if'之前缺少')'

等等...

现在,所有这些错误同样会带我走到问题之后,并在if声明之前抱怨某事.

考虑以下:

SomeFunction(x) //Notice, there is no ';' here

if(bSomeCondition)
{
    ...
}
Run Code Online (Sandbox Code Playgroud)

我收到两个编译错误:

(第265行)错误C2065:'x':未声明的标识符

(第266行)错误C2143:语法错误:缺少';' 在"如果"之前

但是,第一个错误正确告诉我行号,尽管丢失了分号.这告诉我编译器在解析时不会被绊倒,并且能够使它超过分号问题.那么,为什么编译器坚持以这种方式报告语法错误呢?在找到它们的行上报告其他错误(非语法).这是否与编译器进行多次传递有关?基本上,我希望具有C++编译器工作知识的人可以具体解释编译器正在做什么,这需要以"之前"的方式报告错误.

Ada*_*ght 22

对"为什么C/C++错误消息很糟糕"这个更普遍的问题的简短回答是"有时候C++很难解析"(它实际上没有上下文无关语法).但是,这并不是一个真正有效的原因 - 人们仍然可以制作比大多数C++编译器记录更好的诊断信息的工具.

更实际的答案是"编译器作者继承了遗留代码库,这些代码库没有重视错误消息",加上温和的"编译器作者懒惰",以及"诊断报告不是一个激动人心的问题".大多数编译器编写者会添加新的语言功能或3%的codegen性能改进,而不是在代码库上进行重大的重构以允许正确的错误报告.关于"为什么错误没有正确地定位到'导致'他们"的行的具体问题就是这样的一个例子.实际上没有技术上的原因,编译器通常无法确定;缺少a,然后告诉你最后一个;缺失语句的源跨度- 即使存在C++的一般空白不变性.只是存储这些信息(很大程度上)在历史上被忽略了.

也就是说,没有受到数十年旧代码阻碍的新编译器做得更好.看看Clang编译器,它以合理的错误消息为荣.在上诊断页显示了他们是如何比GCC更好的.这种情况的一个例子是:

  $ gcc-4.2 t.c
  t.c: In function 'foo':
  t.c:5: error: expected ';' before '}' token
  $ clang t.c
  t.c:4:8: error: expected ';' after expression
    bar()
         ^
         ;
Run Code Online (Sandbox Code Playgroud)

或者,更令人印象深刻:

  $ cat t.cc
  template<class T>
  class a {}
  class temp {};
  a<temp> b;
  struct b {
  }
  $ gcc-4.2 t.cc
  t.cc:3: error: multiple types in one declaration
  t.cc:4: error: non-template type 'a' used as a template
  t.cc:4: error: invalid type in declaration before ';' token
  t.cc:6: error: expected unqualified-id at end of input
  $ clang t.cc
  t.cc:2:11: error: expected ';' after class
  class a {}
            ^
            ;
  t.cc:6:2: error: expected ';' after struct
  }
   ^
   ;
Run Code Online (Sandbox Code Playgroud)

看,它甚至告诉我们输入什么来解决问题!</ clang_salespitch>

  • 为什么?我们可以清楚地看到,C++的空白不变性导致行报告问题没有压倒一切的技术原因.它只是一个编程选择,主要是由传统的C++解析器强制执行.Clang解析器可以很好地处理提问者案例. (4认同)

Oli*_*rth 12

因为在C++中,白色空间总体上并不重要.所以这是有效的代码:

SomeFunction(x)

;if(bSomeCondition)
{
    ...
}
Run Code Online (Sandbox Code Playgroud)

所以编译器消息只是报告在结束之前没有出现过分号if.


Soa*_*Box 6

在这段代码中:

SomeFunction(x)
if (y) {
}
Run Code Online (Sandbox Code Playgroud)

如你所说,错误将在第2行报告为missing ';' before 'if'.

第1行没有错.没有分号就完全有效,除了一个分号(例如点,数学运算符,赋值或指针等)之外,还可以使用几个表达式.

因此,报告上一行的错误可能并不总是有意义,请举例:

SomeFunction(x)
+= 10
- 5
// blank line
// blank line
if (y) {
}
Run Code Online (Sandbox Code Playgroud)

哪条线有错误?这条线与- 5?还是其中一条评论专栏?对于编译器,错误实际上是'if',因为它是第一个可以检测到错误的地方.要报告不同的线路,编译器会已经报到的最后一个正确解析令牌错误,而不是第一位置处检测到的错误.这听起来有些倒退,并且说//blank line1缺少分号更令人困惑,因为改变它//blank line;当然不会改变或修复错误.

顺便说一句,这不是C或C++独有的.这是在大多数解析器中报告错误的常用方法.