Version 2 (modified by Util, 4 years ago)

Add formatting to Splint Wiki page

Splint

I'm not really sure how to organize this page; I'm hoping that myself and others can expand it, and eventually, converge on something that actually works. I actually started writing this months ago as docs/splint.pod, which is why it looks an awful lot like POD. Please feel free to expand, correct, and reorganize as you see fit.

OVERVIEW

I'm writing this to document my experiences (and failures) with getting this to work, in the hopes that it's useful to others who are also working on this.

"make splint" is a makefile target to run the "splint" tool on the Parrot codebase.

Splint is a very VERY picky "lint" tool, which analyzes C source code for coding mistakes and portability issues. It parses your C sources and generates a huge number of warnings, assuming you can get it to parse the source at all.

Unfortunately, splint's parser is just as strict as its warning generator is. Also unfortunately, a parse error causes splint to exit right then and there, preventing it from giving you a nifty summary at the end. And the "parse error" message tells you very little of value in finding and fixing the problem.

SYNOPSIS

perl Configure.pl --cgoto=0
make splint

STRATEGY

Splint has internal libraries defining common system headers. There's an "ansi" library, there's a "posix" library (extending the ANSI one), and a "unix" library (extending the POSIX one). If a file includes stdio.h, it checks its library to see if it contains a version of stdio.h, and if it does, it uses the library version instead of the one provided by your system C library headers.

Parrot does lots of detection for system specifics, during the Configure.pl process. It detects the existence of header files, and of things defined within those header files, so it can make the best use of the system you're building it for.

I think this is a fundamental difference in philosophy, which we need to overcome. Splint wants things to be as generic as possible, but parrot is trying to be as specific as possible. I don't know what the best strategy to reconcile this is, but there are 2 obvious possibilities:

  1. Convince splint to use the system headers as much as possible.
  2. Convince splint to use the system headers as little as possible.

Option 1 allows parrot to use whatever platform-specific things it has detected, but required (at least, for me) a lot of work convincing splint to parse my system headers properly. This involves disabling splint's header-libraries as much as possible, and then just trying to run it, seeing what breaks, finding a fix or workaround, and repeating until it passes. The below "Parse error in a system header file" section describes some specific issues I found there, and how I worked around them.

Option 2 is more intrusive, but hopefully, less time consuming. It would also probably have a more portable result. Rather than trying to convince Splint to work with whatever Parrot's trying to do, we try to convince Parrot to use no headers/features except those you'd find defined in the ANSI spec, so that everything ends up coming from splint's header-libraries and the system headers are never needed. Specifically, override the contents of include/parrot/has_header.h and include/parrot/feature.h with a set of features compatible with splint. (Maybe use an #ifdef S_SPLINT_S for this, so the headers can be used for splint without breaking normal compilation.)

My personal feeling at the moment is that Option 2 is the way to go. I'm hoping that if we can get the has_header.h and feature.h contents right, all of the problems I've been having with Option 1 will just vanish.

PROBLEMS I'VE RUN INTO

There are several types of problems which have often cropped up, for me. The end result is usually an uninformative parse error.

Parse error in a system header file. Common sources for this problem:

conflict between splint's "header library" and a system header

This occurs when a system header is not covered by splint's internal library, but relies on another header that was defined by a splint library, and defined in an incompatible fashion. In my case, "+posixstrictlib" caused splint to use an internal version of netinet/in.h, but not an arpa/inet.h. Unfortunately, my arpa/inet.h relied on netinet/in.h to define an "in_addr_t" typedef, which splint's library version didn't. The solution is to disable +posixstrictlib, or (in my arpa/inet.h case) upgrade to +unixstrictlib.

build-specific defines required

This occurs when a system header expects the compiler to define some build-specific stuff. In #parrot, particle mentioned a problem where his winnt.h was relying on MSVC to define something specifying which target the source was being built for. The solution is to figure out what needs to be defined, and define it on the splint command-line, with a -D option.

__attribute syntax

This occurs when a system header uses "__attribute" instead of "__attribute__". Note the trailing underscores, in the latter. Apparently, splint recognizes the latter, but not the former. Unfortunately, my system has glibc headers, and glibc's pthread.h uses __attribute all over the place. The solution for this is to define __attribute to __attribute__ on the splint command line, by adding an option like "-D__attribute=__attribute__".

C library dependence on compiler headers

Glibc was written with gcc in mind. The headers in /usr/include sometimes depend on compiler-specific stuff, either language parser extensions, or things defined by gcc headers in /usr/lib/gcc-lib/*. From this perspective, splint is essentially just a different compiler (though it doesn't generate any binaries, so we can't just do a "perl Configure.pl --cc=splint" directly).

Recursive #define in a header file

My /usr/include/bits/confname.h includes both enums and defines for the same tokens. It looks like this:

enum
{
_PC_LINK_MAX,
#define _PC_LINK_MAX _PC_LINK_MAX
_PC_MAX_CANON,
#define _PC_MAX_CANON _PC_MAX_CANON

etc, etc. I'm guessing they want to use an enum for compile-time typechecking, but retain compatibility with code that uses #ifdef to determine its presence, or something. Anyway, splint barfs on this with an "internal error":

/usr/include/bits/confname.h:31:27: *** Internal Bug at cscannerHelp.c:2422: Unexpanded macro not function or constant: int _PC_MAX_CANON [errno: 25]
     *** Please report bug to splint-bug@splint.org ***
       (attempting to continue, results may be incorrect)
/usr/include/bits/confname.h:32:1: Parse Error: Non-function declaration: _PC_MAX_CANON : int. (For help on parse errors, see splint -help parseerrors.)
*** Cannot continue.

The nice thing about this error, of course, is that it waits until after all sourcefile processing to emit this error. Which means you think everything is running great, and yet you're denied your nifty summary at the end.

For now, I've wrapped my system include with #ifndef S_SPLINT_S. But its another argument in favor of keeping splint as far away from system headers as possible...

Parse error in a Parrot source (or header) file

This section handles problems on a case by case basis.

src/ops/core_ops_cg.c and src/ops/core_ops_cgp.c

These files are an implementation of the "CGoto" runloop. The CGoto runloop implements an array of pointers to goto target symbols, and uses nonstandard (gcc-specific) syntax which splint cannot parse. The solution is to re-run Configure.pl with the "--cgoto=0" option.

AVAILABLE TOOLS AND REFERENCES

Splint has a reference manual, available at  http://www.splint.org/manual/manual.html.

In addition, splint has a special --help topic, which you can get by typing splint --help parseerrors, with some useful advice for parse errors and how to resolve them.

Splint also has a "+keep" option, which causes it to tell you where to find its temporary (preprocessed) files, and to not automatically remove them. This flag is very useful to see the preprocessed output that splint actually parses, and thus, see what caused a parse error.

UPDATED CODE

I've had splint crash on me, and I think it's because negative shifts cause a crash, rather than generating a splint error. This bug has been fixed in the CVS version of the code. The CVS version builds just fine.