Почему компилятор C ++ выдает ошибки после строк, а не на них? - PullRequest
9 голосов
/ 16 августа 2011

Этот вопрос возник у меня сегодня на работе, когда у меня возникла еще одна домашняя история с моим компилятором.Несмотря на мой положительный эффект (из-за всей точки с запятой, которую я нажимаю на работе), мне удалось пропустить одну перед выражением if.Очевидно, это привело к ошибке компиляции:

ошибка C2143: синтаксическая ошибка: отсутствует ';'до 'если'

Поэтому я подумал: "Ну и дела, почему ты не можешь сказать мне строку, в которой пропущена точка с запятой вместо строки после после проблемы".и я продолжил экспериментировать с другими подобными синтаксическими ошибками:

ошибка C2065: 'myUndeclared': необъявленный идентификатор

ошибка C2143: синтаксическая ошибка: отсутствует ')' перед 'if'

и т.д ...

Теперь все эти ошибки, аналогично, приведут меня к строке после проблемы и будут что-то жаловаться до if оператор.

Примите во внимание следующее:

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

if(bSomeCondition)
{
    ...
}

Я получаю две ошибки компиляции:

(строка 265) ошибка C2065: 'x': необъявленный идентификатор

(строка 266) ошибка C2143: синтаксическая ошибка: отсутствует ';'before 'if'

Однако первая ошибка правильно сообщает мне номер строки, несмотря на пропущенную точку с запятой.Это наводит меня на мысль, что компилятор не запутался в синтаксическом анализе и способен преодолеть проблему с точкой с запятой.Итак, почему компилятор настаивает на грамматических ошибках, сообщаемых таким образом?Другие ошибки (не грамматические) сообщаются в строках, в которых они найдены.Связано ли это с компилятором, делающим несколько проходов?По сути, я надеюсь, что кто-то, имеющий практические знания о компиляторе C ++, может конкретно объяснить, что делает компилятор, что требует сообщения об ошибках таким способом «до».

Ответы [ 9 ]

22 голосов
/ 16 августа 2011

Короткий ответ на более общий вопрос «Почему сообщения об ошибках C / C ++ отстают» - «Иногда C ++ действительно трудно анализировать» (фактически он не имеет контекстно-свободной грамматики).Однако на самом деле это не является веской причиной - все же можно создавать инструменты, которые записывают лучшую диагностическую информацию, чем большинство компиляторов C ++.

Более практичный ответ: «Авторы компиляторов унаследовали унаследованные кодовые базы, которые не оценивали ошибкусообщения », в сочетании с легкой дозой« авторы компилятора ленивы », увенчанные« Диагностическая отчетность не является захватывающей проблемой ».Большинство авторов компиляторов добавили бы новую языковую функцию или 3% -ное улучшение производительности codegen, вместо того, чтобы делать существенный рефакторинг на базе кода, чтобы позволить достойные сообщения об ошибках.Конкретный вопрос о том, «почему ошибки не локализованы должным образом в строке, которая их« вызвала », является примером этого.На самом деле нет технической причины, по которой компиляторы обычно не могут решить, что отсутствует ;, а затем сообщают вам об исходном диапазоне последнего оператора, не содержащего ;, даже при наличии общей неизменности пробелов в C ++.Просто хранение этой информации (в значительной степени) исторически игнорировалось.

Тем не менее, новые компиляторы, которым не мешает десятилетия старого кода, работают намного лучше.Взгляните на Clang compiler , который гордится разумными сообщениями об ошибках.Страница по диагностике показывает, насколько они лучше, чем 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()
         ^
         ;

Или, что более впечатляюще:

  $ 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
  }
   ^
   ;

Посмотрите, это даже говорит нам, что печатать, где решить проблему!

12 голосов
/ 16 августа 2011

Потому что в C ++ пробел в целом не имеет значения. Так что это действительный код:

SomeFunction(x)

;if(bSomeCondition)
{
    ...
}

Таким образом, сообщение компилятора просто сообщает, что точка с запятой не появилась где-то до if.

6 голосов
/ 16 августа 2011

В этом коде:

SomeFunction(x)
if (y) {
}

Как вы сказали, ошибка будет отображаться в строке 2 как missing ';' before 'if'.

В строке 1 нет ничего неправильного. Это совершенно справедливо без точки с запятой, и возможны несколько выражений, кроме точки с запятой (например, точка, или математический оператор, или присваивание, или указатель и т. Д.). ).

Итак, сообщение об ошибке в предыдущей строке не всегда имеет смысл, возьмем следующий пример:

SomeFunction(x)
+= 10
- 5
// blank line
// blank line
if (y) {
}

Какая строка содержит ошибку? Линия с - 5? Или одна из строк комментариев? Для компилятора ошибка на самом деле связана с «если», так как это первое место, где что-то может быть обнаружено как неправильное. Чтобы сообщить о другой строке, компилятор должен будет указать последний правильно проанализированный токен как ошибку, а не первое место, где обнаружена ошибка. Это звучит немного задом наперед, и сказать, что //blank line1 пропущена точка с запятой, еще более запутанно, поскольку изменение его на //blank line;, конечно, не изменит и не исправит ошибку.

Кстати, это не уникально для C или C ++. Это распространенный способ сообщения об ошибках в большинстве анализаторов.

4 голосов
/ 16 августа 2011

Проще говоря, из-за того, как выполняется анализ. Когда синтаксический анализатор ожидает ; и вместо этого встречает if, ошибка находится в if. Самый простой разумный способ сообщить об этом - сказать, что ; ожидалось раньше if.

3 голосов
/ 16 августа 2011

Потому что следующий код будет правильным:

SomeFunction(x)

;if(bSomeCondition)
{
}

Это потому, что ненужные пробелы игнорируются.

3 голосов
/ 16 августа 2011

Потому что, когда он завершит синтаксический анализ этой строки, он еще не знает, что вам нужна точка с запятой.Давайте рассмотрим пример:

int mystuff

В этой строке отсутствует точка с запятой?Это зависит от того, что будет дальше.Например, следующая конструкция идеально подходит:

int mystuff
   = 1;

Я бы никогда так не написал, но для компилятора это нормально.

3 голосов
/ 16 августа 2011

Компилятор не зависит от пробелов. Он не знает (или не заботится), что между вашими утверждениями есть возврат каретки, табуляции или пробелы. все, что его волнует, это то, что после или перед двоеточиями, или после / перед скобками ('{', '}'), которые заканчивают и начинают классы и функции. Вот почему :)

2 голосов
/ 16 августа 2011

Короткий ответ: вы можете поместить ; в строку 266, и тогда все будет хорошо. С точки зрения компилятора ошибка есть.

Возможно, вы захотите попробовать clang, хотя я не знаю, дает ли это лучшее сообщение об ошибке для этого конкретного типа ошибки, но в целом это дает намного более четкие сообщения об ошибках .

1 голос
/ 03 декабря 2013

Это потому, что компилятор проверяет 1 полный оператор. Позвольте мне привести пример:

int a,b,c
c=a+b;
cout<<c;

Этот код генерирует ошибку компиляции, которая "; ожидается перед с / строкой 2", это происходит потому, что компилятор сначала просматривает int a, b, c строки 1, а компилятор не имеет ни малейшего понятия, будет ли какая-либо другая переменная или оператор и поэтому компилятор переходит на вторую строку (потому что разрешены пробелы), а затем он видит, что есть "c = a + b", который является оператором, и, таким образом, компилятор знает, что что-то не так, как он ожидал, либо переменная или точка с запятой (;). И так, это говорит нам, что ожидал; до выписки.

Короче говоря, компилятор не ищет точку с запятой после оператора (если бы это было так, мы не могли бы использовать пробелы в наших кодах), он ищет; прямо перед другим утверждением, потому что компилятор не имеет ни малейшего представления, как долго будет длиться первое утверждение.

...