ctags chokes on source file with unbalanced braces

2019-07-19 06:57发布

问题:

I am using ctags to generate a tags file for a C project I am working on, but many functions are missing in the file. This appears to be caused by unbalanced braces in the source files due to using #ifdef. A (simplified) example:

#include <stdio.h>

struct mystruct {
        long member;
#ifndef _MSC_VER
}__attribute__ ((packed));
#else /* _MSC_VER */
};
#pragma pack(pop)
#endif /* _MSC_VER */

char* greeting_text(){
  return "Hello world\n";
}

int main( int argc, const char* argv[] ){
  char * greeting = greeting_text();
  printf(greeting);
  return 0;
}

This compiles and works flawlessly with gcc -Wall under Linux. However, if I parse it using ctags problem.c, the tags file only contains entries for mystruct -- the functions are missing.

ctags --verbose reports:

OPENING problem.c as C language file
problem.c: unexpected closing brace at line 8
problem.c: retrying file with fallback brace matching algorithm
OPENING problem.c as C language file
problem.c: unexpected closing brace at line 8

so apparently ctags does not like the preprocessor tricks in the file.

Is there a way to make ctags handle this?

The manpage of ctags even explicitly mentions this problem, but indicates ctags can work around this. However, this does not appear to work...

This is with Exuberant Ctags 5.8 (Debian package 1:5.8-4).

Edit:

I'm also interested in alternatives to ctags that handle these kinds of constructs.

回答1:

Because of the problems with ctags, I ended up using cscope instead.

While it's not perfect, it handles macros better than ctags, and it can integrate with vim just like ctags can (see http://vimdoc.sourceforge.net/htmldoc/if_cscop.html#:cscope ).



回答2:

I would try running the preprocessor (gcc -E) on the files before giving them to ctags. Whether this will produce a good result I am not certain, but it would be worth a try. Certainly all components of your code should appear then, but will ctags recognize the other-file references that gcc leaves in the output? Not sure.



回答3:

You could try to rewrite the code so that there only is one closing brace, for example:

struct mystruct {
        long member;
}
#ifndef _MSC_VER
__attribute__ ((packed))
#endif
;
#ifdef _MSC_VER
#pragma pack(pop)
#endif /* _MSC_VER */

Of course, you can define some convenience macros to make it easier to read.



回答4:

You could run the unifdef tool to selectively (and temporarily) replace the inactive part of the code with blank lines (unifdef -l -U_MSC_VER). The result is

#include <stdio.h>

struct mystruct {
        long member;

}__attribute__ ((packed));





char* greeting_text(){
  return "Hello world\n";
}

int main( int argc, const char* argv[] ){
  char * greeting = greeting_text();
  printf(greeting);
  return 0;
}

Ctags has no problem parsing this correctly and the line numbers remain the same (important if you create ctags searches by line number):

$ cat tags
!_TAG_FILE_FORMAT       2       /extended format; --format=1 will not append ;" to lines/
!_TAG_FILE_SORTED       1       /0=unsorted, 1=sorted, 2=foldcase/
!_TAG_PROGRAM_AUTHOR    Darren Hiebert  /dhiebert@users.sourceforge.net/
!_TAG_PROGRAM_NAME      Exuberant Ctags //
!_TAG_PROGRAM_URL       http://ctags.sourceforge.net    /official site/
!_TAG_PROGRAM_VERSION   5.6     //
greeting_text   y.c     /^char* greeting_text(){$/;"    f
main    y.c     /^int main( int argc, const char* argv[] ){$/;" f
member  y.c     /^        long member;$/;"      m       struct:mystruct file:
mystruct        y.c     /^struct mystruct {$/;" s       file:

unifdef is available on many operating systems as a package (e.g. FreeBSD, various Linux distris, Cygwin). Homepage: http://dotat.at/prog/unifdef/