You can use the lint program to ensure that C programs do not contain syntax errors and to verify that the programs do not contain data type errors. This chapter describes most of the checking operations performed by lint, including the following:
See lint(1) for a complete list of lint options.
The lint program checks a program more carefully than some C compilers and displays messages that point out possible problems. Some of the messages require corrections to the source code; others are only informational messages and do not require corrections.
The lint command has the following syntax:
lint [ options ] [ file ... ]
The cc driver flags, -std, -std0, and -std1 are available as options to lint. These flags affect the parsing of the source as well as the selection of the lint library to use. Selecting either the -std or -std1 flags turns on ANSI parsing rules in lint.
When you use the -MA lint flag, -std1 is used for the C preprocessing phase and \_ANSI_C_SOURCES is defined using the -D preprocessor flag. The following table describes the action lint takes for each flag:
Lint | Pre-processor | Lint | Lint Library |
|
|||
Option | Switch | Parsing | |
-MA | -std1 and -D_ANSI_C_SOURCE | ANSI | llib-lansi.ln |
-std | -std | ANSI | llib-lcstd.ln |
-std1 | -std1 | ANSI | llib-lcstd.ln |
-std0 | -std0 | EXTD | llib-lc.ln |
Table Note: EXTD is Extended C language, also known as K&R C.
Suffix | Description |
.c | C source file |
.i | File produced by the C preprocessor (cpp) |
.ln | lint library file |
Note that lint library files are the result of a previous invocation of the lint program with either the -c or -o option. They are analogous to the .o files produced by the cc command when given a .c file as input. The ability to specify lint libraries as input to the lint program facilitates intermodule interface checking in large applications. Adding rules that specify the construction of lint libraries to their makefiles can make building such applications more efficient. See Section 6.10 for a discussion on how to create a lint library.
You can also specify as input a lint library that resides in one of the system's default library search directories by using the -lx option. The library name must have the following form:
llib-llibname.ln
By default, the lint program appends the extended C (K&R C) lint library (llib-lc.ln) to the list of files specified on the command line. If the -std or -std1 flag is used, it appends the standard C lint library (llib-lcstd.ln) instead.
The following additional libraries are included with the system:
Library | Description | Specify As |
crses | Checks curses library call syntax | -lcrses |
m | Checks math library call syntax | -lm |
port | Checks for portability with other systems | -p (not -lport) |
ansi | Enforces ANSI C standard rules | -MA (not -lansi) |
If you specify no flags on the command line, the lint program checks the specified C source files and writes messages about any of the following coding problems that it finds:
The lint program also checks for syntax errors in statements in the source programs. Syntax checking is always done and is not influenced by option flags.
If lint does not report any errors, the program has correct syntax and will compile without errors. Passing that test, however, does not mean that the program will operate correctly or that the logic design of the program is accurate.
See Section 6.10 for information on how to create your own lint library.
The lint program checks for dead code, that is, parts of a program that are never executed because they cannot be reached. It writes messages about statements that do not have a label but immediately follow statements that change the program flow, such as goto, break, continue, and return.
The
lint
program also detects and writes messages for the following conditions:
while(1) for (;;)
Some programs that include these types of loops may produce correct results. These types of loops can cause problems, however.
The lint program does not recognize functions that are called but can never return to the calling program. For example, a call to exit may result in code that cannot be reached, but lint does not detect it.
Programs generated by yacc and lex may have hundreds of break statements that cannot be reached. The lint program normally writes an error message for each of these break statements. Use the -O flag to the cc command when compiling the program to eliminate the resulting object code inefficiency, so that these extra statements are not important. Use the -b flag with the lint program to prevent it from writing these messages when checking yacc and lex output code. (For information on yacc and lex, see Programming Support Tools.)
The lint program enforces the type checking rules of the C language more strictly than the compiler does. In addition to the checks that the compiler makes, lint checks for potential data type errors in the following areas:
Details on each of these potential problem areas are provided
in the sections that follow.
The C language allows the following data types to be mixed in statements, and the compiler does not indicate an error when they are mixed:
The C language converts data types within this group automatically to provide the programmer with more flexibility in programming. This flexibility, however, means that the programmer, not the language, must ensure that the data type mixing produces the desired result.
You can mix these data types when using them in the following ways (in the examples, alpha is type char and num is type int):
alpha = num; /* alpha converts to int */
value=(alpha < num) ? alpha : num;
/* alpha converts to int */
if( alpha != num ) /* alpha converts to int */
funct(x) /* returns an integer */ { return( alpha ); }
The data types of pointers must agree exactly, except that you can mix arrays of x's with pointers to x's.
The lint program checks structure operations for the following requirements:
The lint program makes similar checks for references to unions.
The lint program applies strict rules to function argument and return value matching. Arguments and return values must agree in type, with the following exceptions:
The lint program checks enumerated data type variables to ensure that they meet the following requirements:
Type casts in the C language allow the program to treat data of one type as if it were data of another type. The lint program can check for type casts and write a message if it finds one.
The -wp and the -h options for the lint command line control the writing of warning messages about casts. If neither of these flags are used, lint produces warning messages about casts that may cause portability problems.
In migration checking mode, -Qc suppresses cast warning messages (see Section 6.6).
The lint program checks for variables and functions that are declared in a program, but not used. The lint program checks for the following errors in the use of variables and functions:
Details on each of these potential problem areas are provided in the sections that follow.
If a function returns a value under one set of conditions but not under another, you cannot predict the results of the program. The lint program checks functions for this type of behavior. For example, if both of the following statements are in a function definition, a program calling the function may or may not receive a return value:
return(expr);
.
.
.
return;
These statements cause the lint program to write the following message to point out the potential problem:
function name has return(e); and return
The lint program also checks functions for returns that are caused by reaching the end of the function code (an implied return). For example, in the following part of a function, if a tests false, checkout calls fix\(ulit and then returns with no defined return value:
checkout (a) { if (a) return (3); fix\(ulit (); }
These statements cause the lint program to write the following message:
function checkout has return(e); and return
If fix\(ulit, like exit, never returns, lint still writes the message even though nothing is wrong.
The lint program checks for cases in which a function returns a value and the calling program may not use the value. If the value is never used, the function definition may be inefficient and should be examined to determine whether it should be modified or eliminated. If the value is sometimes used, the function may be returning an error code that the calling program does not check.
To prevent lint from checking for problems with functions, specify one or more of the following flags to the lint command:
-x | Do not check for variables that are declared in an extern statement but never used. |
-v | Do not check for arguments to functions that are not used, except for those that are also declared as register arguments. |
-u | Do not check for functions and external variables that are either used and not defined, or defined and not used. Use this flag to eliminate useless messages when you are running lint on a subset of files of a larger program. (When using lint with some, but not all, files that operate together, many of the functions and variables defined in those files may not be used. Also, many functions and variables defined elsewhere may be used.) |
You can also place directives in the program to control checking:
/*ARGSUSED*/
/*VARARGSn*/
To check the first several arguments and leave the later arguments unchecked, add a digit (n) to the end of the VARARGS directive to give the number of arguments that should be checked, such as:
/*VARARGS2*/
When lint reads this directive, it checks only the first two arguments.
/*LINTLIBRARY*/
This is equivalent to using the -v and -x flags.
/*LINTSTDLIB [ _filename] */
The
/*LINTSTDLIB*/
directive implicitly activates the functions of the
/*NOTUSED*/
and
/*LINTLIBRARY*/
directives to reduce warning noise levels.
When a file is referenced
(filename),
only prototypes in that file are expanded.
Multiple
/*LINTSTDLIB_filename*/
statements are allowed. (See
Section 6.10.1
for more details on the use of
/*LINTSTDLIB*/
directives.)
/*NOTDEFINED*/
/*NOTREACHED*/
When placed at appropriate points in a program (typically immediately following a return, break, or continue statement), the /*NOTREACHED*/ directive stops comments about unreachable code. Note that lint does not recognize the exit function and other functions that may not return.
/*NOTUSED*/
The /*NOTUSED*/ directive is similar to the /*LINTLIBRARY*/ directive, although /*NOTUSED*/ also applies to external symbols.
The lint program checks for the use of a local variable (auto and register storage classes) before a value has been assigned to it. Using a variable with an auto (automatic) or register storage class also includes taking the address of the variable. This is necessary because the program can use the variable (through its address) any time after it knows the address of the variable. Therefore, if the program does not assign a value to the variable before it finds the address of the variable, lint reports an error.
Because lint only checks the physical order of the variables and their usage in the file, it may write messages about variables that are initialized properly (in execution sequence).
The lint program recognizes and writes messages about:
Note
The operating system initializes static and extern variables to zero. Therefore, lint assumes that these variables are set to zero at the start of the program and does not check to see if they have been assigned a value when they are used. When developing a program for a system that does not do this initialization, ensure that the program sets static and extern variables to an initial value.
Use lint to check for all common programming techniques that might cause problems when migrating programs from 32-bit operating systems to the 64-bit Digital UNIX operating system. The -Q option provides support for checking programs written for ULTRIX and DEC OSF/1 Version 1.0 that you are migrating to 64-bit systems.
Because the -Q option disables checking for most other programming problems, you should use this option only for migration checking. Suboptions are available to suppress specific categories of checking. For example, entering -Qa suppresses the checking of pointer alignment problems. You can enter more than one suboption with the -Q option, for example, -QacP to suppress checking for pointer alignment problems, problematic type casts, and function prototype checks, respectively. For more information about migration checking, see lint(1).
The lint command provides the -N option and related suboptions to allow you to increase the size of various internal tables at run time if the default values are not enough for your program. These tables include:
These tables are dynamically allocated by the lint program. The -N option may be used on large source files to improve performance.
Use lint to help ensure that you can compile and run C programs using different C language compilers and other systems.
The following sections indicate areas to check before compiling the program on another system. Checking only these areas, however, does not guarantee that the program will run on any system.
Note
The llib-port.ln library is brought in by using the -p flag, not by using the -lport flag.
Some systems define characters in a C language program as signed quantities with a range from -128 to 127; other systems define characters as positive values. The lint program checks for character comparisons or assignments that may not be portable to other systems. For example, the following fragment may work on one system but fail on systems where characters always take on positive values:
char c;
.
.
.
if( ( c = getchar() ) <0 )\.\.\.
This statement causes the lint program to write the following message:
nonportable character comparison
To make the program work on systems that use positive values for characters, declare c as an integer because getchar returns integer values.
Bit fields may also produce problems when a program is transferred to another system. Bit fields may be signed quantities on the new system. Therefore, when constant values are assigned to a bit field, the field may be too small to hold the value. To make this assignment work on all systems, declare the bit field to be of type unsigned before assigning values to it.
When changing from one type of system to another, be aware of differences in the information retained about external names during the loading process:
When transferring from one system to another, you should always take the following steps to avoid problems with loading a program:
The -p flag tells lint to change all external symbols to lowercase and limit them to six characters while checking the input files. The messages produced indicate the terms that may need to be changed.
Be careful when using complicated expressions because of the following considerations:
The following situations illustrate the types of problems that can result from these differences:
printf( "%d %d\n", ++years, amort( interest, years ) );
a[i]=b[i++];
This statement causes the lint program to write the following message:
warning: i evaluation order undefined
Use lint to detect possible coding errors and to detect differences from the coding style that lint expects. Although coding style is mainly a matter of individual taste, examine each difference to ensure that the difference is both needed and accurate. The following sections indicate the types of coding and style problems that lint can find.
If you assign variables of type long to variables of type int, the program may not work properly. The long variable is truncated to fit in the integer space and data may be lost.
An error of this type occurs frequently when a program that uses more than one typedef is converted to run on a different system.
To prevent lint from writing messages when it detects assignments of long variables to int variables, use the -a flag.
The lint program detects possible or potential errors in operator precedence. Without parentheses to show order in complex sequences, these errors can be hard to find. For example, the following statements are not clear:
if(x&077==0). . . /* evaluated as: if(x & (077 == 0) ) */ /* should be: if( (x & 077) == 0) */
x<<2+40 /* evaluated as: x <<(2+40) */ /* should be: (x<<2) + 40 */ /* shift x left 42 positions */
Use parentheses to make the operation more clearly understood. If you do not, lint writes a message.
The lint program writes messages about variables that are declared in inner blocks in ways that conflict with their use in outer blocks. This practice is allowed, but may cause problems in the program.
Use the -h flag with the lint program to prevent lint from checking for conflicting declarations.
For programming projects that define additional library routines, you can create an additional lint library to check the syntax of the programs. Using this library, the lint program can check the new functions in addition to the standard C language functions. Perform the following steps to create a new lint library:
The following sections describe these steps.
The following example shows an input file that defines three additional functions for lint to check.
/*LINTLIBRARY*/
#include <dms.h>
int dmsadd( rmsdes, recbuf, reclen ) int rmsdes; char *recbuf; unsigned reclen; { return 0; } int dmsclos( rmsdes) int rmsdes; { return 0; } int dmscrea( path, mode, recfm, reclen ) char *path; int mode; int recfm; unsigned reclen; { return 0; }
The input file is a text file that you create with an editor. It consists of:
/*LINTLIBRARY*/
Alternatively, you can create a lint library file from function prototypes. For example, assume that the dms.h file includes the following prototypes:
int dmsadd(int, char*, unsigned); int dmsclose(int); int dmscrea(char*, int, int, unsigned);
In this case, the input file contains the following:
/*LINTSTDLIB*/ #include <dms.h>
In the case where a header file may include other headers, the LINTSTDLIB command can be restricted to specific files:
/*LINTSTDLIB_dms.h*/
In this case, only prototypes declared in dms.h will be expanded. Multiple LINTSTDLIB commands can be included.
In all cases, the name of the input file must have the prefix: llib-l. For example, the name of the sample input file created in this section could be llib-ldms. When choosing the name of the file, ensure that it is not the same as any of the existing files in the /usr/ccs/lib directory.
The following command creates a lint library file from the input file described in the previous section:
%
lint [options] -c llib_ldms.c
This command tells lint to create a lint library file, llib-ldms.ln, using the file llib-ldms.c as input. To use llib-ldms.ln as a system lint library (that is, a library specified in the -lx option of the lint command), move it to /usr/ccs/lib. Use the -std or -std1 flag to use ANSI preprocessing rules to build the library.
To check a program using a new library, use the lint command with the following format:
lint -lpgm filename.c
The variable pgm represents the identifier for the library, and the variable filename.c represents the name of the file containing the C language source code that is to be checked. If no other flags are specified, the lint program checks the C language source code against the standard lint library in addition to checking it against the indicated special lint library.
Although most error messages produced by lint are self-explanatory, certain messages may be misleading without additional explanation. Usually, once you understand what a message means, correcting the error is straightforward. The following is a list of the more ambiguous lint messages:
A constant is used with the NOT operator (!).
This is a common coding pratice and the message does not usually indicate a problem. The following program illustrates the type of code that can generate this message:
%
cat x.c
#include <stdio.h> #define SUCCESS 0% lint -u x.c
main() { int value = !SUCCESS;
printf("value = %d\n", value); return 0; }
The program runs as expected, even though lint complains.
Recommended Action: Suppress these lint warning messages by using the -wC option.
A constant is used where a conditional is expected. This problem occurs often in source code, due to the way in which macros are encoded. For example:
typedef struct _dummy_q { int lock; struct _dummy_q *head, *tail; } DUMMY_Q;
#define QWAIT 1 #define QNOWAIT 0 #define DEQUEUE(q, elt, wait) [1] \ for (;;) { \ simple_lock(&(q)->lock); \ if (queue_empty(&(q)->head)) \ if (wait) { [1] \ assert(q); \ simple_unlock(&(q)->lock); \ continue; \ } else \ *(elt) = 0; \ else \ dequeue_head(&(q)->head); \ simple_unlock(&(q)->lock); \ break; \ }
int doit(DUMMY_Q *q, int *elt) { DEQUEUE(q, elt, QNOWAIT); }
Recommended Action: Suppress these lint warning messages by using the -wC option.
A signed long is copied to a smaller entity (for example, an int). This message is not necessarily misleading, but if it occurs frequently, it may or may not indicate a coding problem, as shown in the following example.
long BuffLim = 512; [1]
void foo (buffer, size) char *buffer; int size; { register int count; register int limit = size < (int)BufLimit ? size : (int)BufLim; [1]
Recommended Action: Review code sections for which lint reports this message, or suppress the message by using the -wl option.
A line in the declaration section of the program contains just a semicolon (;).
Although you would not deliberately write code like this, it is easy to inadvertantly generate such code by using a macro, followed by a semicolon. If, due to conditionalization, the macro is defined as empty, this message can result.
Recommended Action: Remove the trailing semicolon.
An unsigned comparison is being performed against a signed value when the result is expected to be less than zero.
The following program illustrates this situation:
%
cat x.c
#include <stdio.h> unsigned long offset = -1;% cc -g -o x x.c
main() { if (offset < 0) { [1] puts ("code is Ok..."); return 0; } else { puts ("unsigned comparison failed..."); return 1; } }
Recommended Action:
You can fix the previous example in two ways:
In certain cases, it might be necessary to cast the signed value to
unsigned.
This error is not strictly related to function prototypes, as the message implies. Actually, this error occurs from invoking any function that has not been previously declared or defined.
Recommended Action: Add the function prototype declaration.
The lint program detected a cast or statement that does nothing. The following code segments illustrate various coding practices that cause lint to generate this message:
scsi_slot = device->ctlr_hd->slot,unit_str; [1]
#define MCLUNREF(p) \ (MCLMAPPED(p) && --mclrefcnt[mtocl(p)] == 0)
(void) MCLUNREF(m); [2]
Recommended Action: Remove unnecessary casts or statements or update macros.
A pointer is used in a way that may cause an alignment problem. The following code segment illustrates the type of code that produces this message:
read(p, args, retval) struct proc *p; void *args; long *retval; { register struct args { long fdes; char *cbuf; unsigned long count; } *uap = (struct args *) args; [1] struct uio auio; struct iovec aiov;
An attempt was made to assign a constant value to a bit field when the field is too small to hold the value.
The following code segment illustrates this problem:
%
cat x.c
struct bitfield { unsigned int block_len : 4; } bt;% lint -u x.c
void test() { bt.block_len = 0xff; }
As you can see, this code compiles without error. However, because the bit field may be too small to hold the constant, the results may not be what the programmer intended and a run-time error may occur.
Recommended Action: Change the bit field size or assign a different constant value.
An unsigned comparison is being performed against zero when the result is expected to be equal to or greater than zero.
The following program illustrates this situation:
%
cat z.c
#include <stdio.h> unsigned offset = -1;% cc -o z z.c
main() { if (offset > 0) { [1] puts("unsigned comparison with 0 Failed"); return 1; } else { puts("unsigned comparison with 0 is Ok"); return 0; } }
Recommended Action:
You can fix the previous example in two ways:
Several lint warning classes have been added to the lint program to allow the suppression of messages associated with constants used in conditionals, portability, and prototype checks. By using the warning class option to the lint command, you can suppress messages in any of the warning classes.
The warning class option has the following format:
-wclass [ class... ]
All warning classes are active by default, but may be individually deactivated by including the appropriate option as part of the class argument. Table 6-1 lists the individual options.
Note
Several lint messages are dependent on more than one warning class. Therefore, you may need to specify several warning classes for the message to be suppressed. Notes in Table 6-1 indicate which messages can only be suppressed by specifying multiple warning classes.
For example, because lint messages related to constants in conditional expressions do not necessarily indicate a coding problem (as described in Section 6.11), you may decide to use the -wC option to suppress them.
The
-wC
option suppresses the following messages:
Because many of the messages associated with portability checks are related to non-ANSI compilers and limit restrictions that do not exist in the C compiler for Digital UNIX, you can use the -wp option to suppress them. The -wp option suppresses the following messages:
Although the use of function prototypes is a recommended coding practice (as described in Section 6.12.1), many programs do not include them. You can use the -wP option to suppress prototype checks. The -wP option suppresses the following messages:
Warning Class | Description of Class |
a | Non-ANSI features. Suppresses: |
· Partially elided initialization |
|
· Static function %s not defined or used |
|
|
|
c | Comparisons with unsigned values. Suppresses: |
· Comparison of unsigned with negative constant | |
· Degenerate unsigned comparison | |
· Unsigned comparison with 0? | |
|
|
d | Declaration consistency. Suppresses: |
· External symbol type clash for %s | |
· Illegal member use: perhaps %s.%s |
|
· Incomplete type for %s has already been completed | |
· Redeclaration of %s | |
· Struct/union %s never defined |
|
· %s redefinition hides earlier one
|
|
|
|
h | Heuristic complaints. Suppresses: |
· Constant argument to NOT |
|
· Constant in conditional context |
|
· Enumeration type clash, op %s | |
· Illegal member use: perhaps %s.%s |
|
· Null effect |
|
· Possible pointer alignment problem, op %s |
|
· Precedence confusion possible: parenthesize! |
|
· Struct/union %s never defined |
|
· %s redefinition hides earlier one |
|
|
|
k | K&R type code expected. Suppresses: |
· Argument %s is unused in function %s |
|
· Function prototype not in scope |
|
· Partially elided initialization |
|
· Static function %s is not defined or used |
|
· %s may be used before set
|
|
· %s redefinition hides earlier one
|
|
· %s set but not used in function %s |
|
|
|
l | Assign long values to non-long variables. Suppresses: |
· Conversion from long may lose accuracy | |
· Conversion to long may sign-extend incorrectly | |
|
|
n | Null-effect code. Suppresses: |
· Null effect |
|
|
|
o | Unknown order of evaluation. Suppresses: |
· Precedence confusion possible: parenthesize! |
|
· %s evaluation order undefined | |
|
|
p | Various portability concerns. Suppresses: |
· Ambiguous assignment for non-ansi compilers | |
· Illegal cast in a constant expression | |
· Long in case or switch statement may be truncated in | |
non-ansi compilers | |
· Nonportable character comparison | |
· Possible pointer alignment problem, op %s |
|
· Precision lost in assignment to (sign-extended?) field | |
· Precision lost in field assignment | |
· Too many characters in character constant | |
|
|
r | Return statement consistency. Suppresses: |
· Function %s has return(e); and return; | |
· Function %s must return a value | |
· main() returns random value to invocation environment | |
|
|
S | Storage capacity checks. Suppresses: |
· Array not large enough to store terminating null | |
· Constant value (0x%x) exceeds (0x%x) | |
|
|
u | Proper usage of variables and functions. Suppresses: |
· Argument %s unused in function %s |
|
· Static function %s not defined or used |
|
· %s set but not used in function %s |
|
· %s unused in function %s |
|
|
|
A | Activate all warnings. Default option in lint script. Specifying another A class toggles the setting of all classes. |
|
|
C | Constants occurring in conditionals. Suppresses: |
· Constant argument to NOT |
|
· Constant in conditional context |
|
|
|
D | External declarations are never used. Suppresses: |
· Static %s %s unused | |
|
|
O | Obsolescent features. Suppresses: |
· Storage class not the first type specifier | |
|
|
P | Prototype checks. Suppresses: |
· Function prototype not in scope |
|
· Mismatched type in function argument | |
· Mix of old and new style function declaration | |
· Old style argument declaration |
|
· Use of old-style function definition in presence of prototype | |
|
|
R | Detection of unreachable code. Suppresses: |
· Statement not reached |
Table notes:
In addition to correcting the various errors reported by the lint program, Digital recommends adding function prototypes to your program for both external and static functions. These declarations provide the compiler with information it needs to check arguments and return values.
The cc compiler provides an option that automatically generates prototype declarations. By specifying the -proto[is] option for a compilation, you create an output file (with the same name as the input file but with a .H extension) that contains the function prototypes. The i option includes identifiers in the prototype, and the s option generates prototypes for static functions as well.
You can copy the function prototypes from a \.H file and place them in the appropriate locations in the source and include files.