mikebell.xyz


If you need a Computer Science tutor, code reviewer, or someone to pair program with, click here



Never Trust Compiler Error Messages

description: Debugging begins by questioning your compiler tags: c, cpp, java, debugging


A fellow codementor made a post titled Basic debugging. Dude doesn’t even get into how to debug at all. He mentions two ways, but then starts going off on how to write simple code. That is not really debugging, in my opinion. Sure, you often have to write code in order to debug, but that is not exactly the same thing. The process of writing simple code differs from the process of debugging.

Dude gets into writing simple code, and really, his rant is not about debugging at all.

First of all, I personally feel that simple code should be the standard. One statement per line. Very rarely mix function calls into other statements or expressions. Keep it simple so that when errors occur, you can reduce the bug to a line number (usually).

I think the post kind of set me off a bit because I frequently go through debugging processes with students, and the process of rewriting and/or refactoring (please God tell me the difference) code is usually something done in the process of debugging, but is not the process itself. Often, you should want to rewrite your own or others’ code in order to achieve clarity, especially if you do not quite understand how something is achieved. Mixing statements and calls into complicated one-line expressions is not going to achieve any performance benefit to how efficient or fast or small your code is.

I was thinking about my favorite bugs, which are the most annoying. Usually, you’ve written perfectly logical code everywhere else, but you forget a critical single-character of syntax and the compiler/interpreter begins shitting a brick. In high school (circa 2003-2004) I made a C++ compiler spew out over 100 errors due to omitting a single semicolon (which is where my most often spoken phrase to C/C++/Java/Javascript/PHP students comes from: “Don’t forget your semicolon”).

I wonder…can I get that error to occur again, intentionally? And, if so, I can certainly show you how you’d interpret the error messages that get generated by the compiler.

Let’s go with a simple example program in C++. We declare a class called MyClass and write a basic Hello World program.

Can you spot the syntax error?

1  #include <iostream>
2  
3  using namespace std;
4  
5  class MyClass {
6      public:
7          MyClass() {
8  };
9  
10  int main() {
11      cout << "Hello, world!" << endl;
12      return 0;
13  }

If you said “The constructor declared on line 7 is missing a closing curly-brace”, you’re correct!

However, let’s look at what the compiler clang++ says about this code:

clang++ main.cpp                                                                                                           
main.cpp:13:2: error: expected '}'
}
 ^
main.cpp:5:15: note: to match this '{'
class MyClass {
              ^
main.cpp:13:2: error: expected ';' after class
}
 ^
 ;
2 errors generated.

The compiler cites the curly-brace at the end of the file, but recognizes that the class declarator is missing its closing curly-brace. It actually cites line 13 twice and claims there are two errors:

  1. The closing curly brace it thinks is missing for the class declarator
  2. The closing curly brace it matches for the class declarator is missing a semicolon

Both of these errors are generated due to the way the compiler parses our code. The errors themselves are not the logical, syntactical problem we are actually facing, and so you cannot fully trust error messages.

Let’s look at the output from another compiler: g++

main.cpp:13:1: error: expected ‘}’ at end of input
 }
 ^
main.cpp:13:1: error: expected unqualified-id at end of input

Two errors again, but this time the actual error messages are different. Clang actually recognized the class declarator closing curly-brace was missing. This is not to diss on g++ or anything, but this is one of the first times I’ve actually compared output from the two.

What happens if you just blindly add an additional curly-brace at the end of the program, thinking “Oh, I’m just missing a curly-brace at the end, let’s add that”:

g++ main.cpp 
main.cpp:14:2: error: expected ';' after class
}

Ok, so, let’s add that missing ; now:

Undefined symbols for architecture x86_64:
  "_main", referenced from:
     implicit entry/start for main executable
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

Now it can’t even find my main program!

#include <iostream>

using namespace std;

class MyClass {
    public:
        MyClass() {
};

int main() {
    cout << "Hello, world!" << endl;
    return 0;
}
};

Now the compiler isn’t even returning an error line number or anything, it’s just saying it can’t find the int main(). This is an even worse position to be in than before, and all because you couldn’t take the time to read your own code…

Tsk tsk tsk.

What are we going to do about this?

A lot of the techniques you can use to hone-in on this sort of problem can be used to make the process of finding the bug easier. Formatting your code properly so that curly-braces and indents are symmetrical and allow your eyes to just go down the code without any sort of God-awful formatting (I have a rant on this building up and will be written eventually) is good, but that itself only makes debugging easier and is not, itself, debugging.

If you can’t even trust the compiler’s error messages, what can you trust?

Compiler error messages are sometimes only hints about what is wrong with your code, and in C++ in particular, are often not helpful at all.

Make sure that your curly-braces line up using whatever style you like, either Stroustrup-style or K&R-style:

Stroustrup-style

int main() {
    return 0;
}

K&R-style

int main()
{
    return 0;
}

And, don’t forget your semicolons!


If you need a Computer Science tutor, code reviewer, or just someone to pair program with, hit me up