Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does including a header using the full path lead to better error messages?

There was a recent post on Ask Ubuntu, where OP was trying to compile a program which included term.h. When the code had #include <term.h>, the errors were:

In file included from clear_screen_UNIX.c:5:0:
clear_screen_UNIX.c:9:6: error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before ‘->’ token
 void clear_screen(void) {
      ^
clear_screen_UNIX.c: In function ‘main’:
clear_screen_UNIX.c:23:14: error: called object is not a function or function pointer
  clear_screen();
              ^
clear_screen_UNIX.c:26:14: error: called object is not a function or function pointer
  clear_screen();

The OP then included the full path to term.h (#include "/usr/include/term.h"), which led to the much more useful message:

In file included from clear_screen_UNIX.c:7:0:
/usr/include/term.h:125:21: error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before ‘->’ token
 #define CUR cur_term->type.
                     ^
/usr/include/term.h:202:40: note: in expansion of macro ‘CUR’
 #define clear_screen                   CUR Strings[5]
                                        ^
clear_screen_UNIX.c:9:6: note: in expansion of macro ‘clear_screen’
 void clear_screen(void) {
      ^
clear_screen_UNIX.c: In function ‘main’:
clear_screen_UNIX.c:23:14: error: called object is not a function or function pointer
  clear_screen();
              ^
clear_screen_UNIX.c:26:14: error: called object is not a function or function pointer
  clear_screen();

These messages clearly indicate the problem is due to a macro expansion.

I verified the results myself, too. I wonder why GCC produced much better errors when the full path was given. Can I make it produce similar messages with the system include syntax as well?

I am using GCC 4.9.2, and I suspect OP was using GCC 4.8.2 (given the version of Ubuntu).

like image 962
muru Avatar asked Apr 19 '15 23:04

muru


1 Answers

Conclusion

The reason GCC gives different/better messages if the full path of the header is given is that the GCC preprocessor gives information to GCC's cc1 compiler that the included header is a system header file or local header file by some digits at the end of the comment line of the preprocessor-generated .i file.

Then the cc1 compiler will generate more helpful messages if the header file is an local header file, and will suppress some error message if the header file is a system header, according to the GCC documentation.

To make the normal version of the code output error messages just like the code which specified the full path of the header file, as you asked for, GCC needs to stop including all system directories by specifying option -nostdinc, and then explicitly tell GCC the directories it could search for header files while not treat the directory as system directory using -I flag.

For your code, the command line could be like below (GCC_INCLUDE_DIR is your default GCC include directory, for the system default GCC, it could be /usr/lib/gcc/x86_64-unknown-linux-gnu/4.9.2/include/) :

gcc -c t.c -nostdinc -I/usr/include/ -IGCC_INCLUDE_DIR

Source code

Moved the source code here from this original post to make this answer more helpful.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <term.h>

//#include "/usr/include/term.h"

void clear_screen(void) {
    if (!cur_term) {
        int result;
        setupterm( NULL, STDOUT_FILENO, &result );
    if (result <= 0)
        return;
    }

    putp( tigetstr( "clear" ) );
}

int main(void) {
    puts("I am going to clear the screen");
    sleep(1);
    clear_screen();
    puts("Screen Cleared");
    sleep(1);
    clear_screen();

    return 0;

}

The difference between Preprocessor Generated Files

You could use the following command line to ask GCC to output the preproceser generated code. This code will be fed into the actual compiler of GCC, cc1. If the preprocessor generated files are exactly the same, the cc1 compiler's behavior should be exactly the same. (Assuming the code is put into file t.c)

 gcc -E t.c -o t.i

Following is the difference between the two gcc preprocessor generated .i files. The t.fullpath.i is the file generated with the full path header file, while t.i is the code without full path (Some of the diff output have been removed, since they are only filename differences.)

$ diff t.i t.fullpath.i
2920,2922c2920,2924
< # 1 "/usr/include/term.h" 1 3 4
< # 47 "/usr/include/term.h" 3 4
---
> # 1 "/usr/include/term.h" 1
> # 47 "/usr/include/term.h"
2924,2925c2926,2927
< # 48 "/usr/include/term.h" 2 3 4
< # 80 "/usr/include/term.h" 3 4
---
> # 48 "/usr/include/term.h" 2
> # 80 "/usr/include/term.h"
3007,3008c3009,3010
< # 81 "/usr/include/term.h" 2 3 4
< # 673 "/usr/include/term.h" 3 4
---
> # 81 "/usr/include/term.h" 2
> # 673 "/usr/include/term.h"
3041c3043
< # 729 "/usr/include/term.h" 3 4
---
> # 729 "/usr/include/term.h"
The different flag in the preprocessor generated code's comments makes the difference

GCC's cc1 compiler will take advantage of the preprocessor genrated information to generate the source code location of the error message, and also the debug information which will be used for gdb in the future.

For the following format:

# line-number "source-file" [flags]

The digits 3 and 4 of the flags mean:

  • 3: Following text comes from a system header file (#include <> vs #include "")
  • 4: Following text should be treated as being wrapped in an implicit extern "C" block.

    For more information about different kind of these flags, please refer to this link.

Therefore, for the code without fully specified path, cc1 compiler will treat it as a system header file, and assume that system code is mostly correct, and then just output error message of the user's code. That is why the error message is shorter.

like image 93
Kun Ling Avatar answered Oct 04 '22 04:10

Kun Ling