7.4 Using Lint


The entire Solaris kernel and many libraries and commands are completely lint clean, both pass1 and pass2. It is important that we maintain this cleanliness as yet another tool to insure a high quality release. Although not all non-kernel code is lint-clean, new code should be, and all new commands and libraries should be entirely lint-clean. Lint-clean code should indicate to the build system that it should be linted. For an example of code in transition to lint-cleanliness, see usr/src/cmd/cmd-inet/usr.sbin/Makefile and the associated code.

Before checking in your changes use the following steps (on both SPARC and x86!) to check for any new errors:

% cd $SRC/uts
% ( make && make lint ) > ERRS 2>&1
% grep "warning:" ERRS

*ANY* warning messages must be fixed!

Note that you can also use nightly(1) to generate lint output. If you instruct nightly(1) to run lint, which you should always do by adding 'l' to your NIGHTLY_OPTIONS or explicitly using the '-l' command line option, it will produce a listing of any "lint noise" as part of its final output if your changes have introduced lint warnings; it should be empty. This can be much easier than looking through a log file as described above, and includes any lint warnings from both kernel and non-kernel code which is intended to be lint-clean.

In the build system, linting is fairly similar to a "normal" build, but it has an additional complication. In order to get meaningful output from lint pass2, all the modules must be linted together. This is accomplished by each module being responsible to produce its own pass1 output (file.ln, one per .c/.s file). It is also responsible for placing the lint-library (llib-lMODULE) in the uts/MACHINE/lint-libs directory. The final full lint is accomplished by the makefile in the uts/MACHINE directory by linting all the lint-libraries against each other.

Note that, to keep lint happy about functions defined in assembly only, there are also C prototypes in the .s files. For example:

#if defined(lint)
int
blort(int, int)
{ return 0 }
#else	/* lint */
ENTRY(blort)
ld	[%i0],....
....
SET_SIZE(blort)
#endif	/* lint */

Here are some additional rules for keeping code lint-clean and avoiding lint problems:

* *NEVER* *EVER* use /* LINTLIBRARY */ in OpenSolaris kernel source!

* Modification of header files, either directly or as a result of a merge, does not always cause the lint libraries to be rebuilt. This may result in seemingly impossible errors of a function having two different declarations. Be sure to run "make clean.lint" after any merge or modification of a header file.

* When calling a function with a return value but not using it, place a (void) cast in front. Common functions are sprintf and strcpy. Many times ignoring a function return value can hide error conditions. Functions that always have the values ignored might need to consider having their specification redeclared void, although of course this normally does not apply to public interfaces.

* Format strings for long integers use one of "%ld", %lx", or "%lu"

* Format strings for unsigned integers use one of "%u" or "%x"

* Format strings for long long integers use "%lld" or "%llx"

* Format strings for pointers should use "%p", with the pointer cast to (void *). Using %d or %x and casting to an (int) will break in a 64-bit kernel.

* For code that is supposed to work for either ILP32 or LP64, there are some macros that you can use in the format string, so that you don't need to #ifdef longs versus ints. See <sys/int_fmtio.h>.

* Use full ANSI/ISO prototypes in function declarations, as the kernel is always compiled with __STDC__ defined. New code should not use K&R-style declarations.

* Make sure machine dependent function declarations are consistent across platforms.

* Be sure you are linting against the headers in your proto area and not the installed headers on your build machine.