ack/doc/lint/chap3
1991-09-30 17:58:00 +00:00

295 lines
7.1 KiB
Plaintext

.NH
What lint checks
.NH 2
Set, used and unused variables
.PP
We make a distinction between two classes of variables:
the class of automatic variables (including register variables)
and the other variables.
The other variables, global variables, static variables, formal
parameters et cetera, are assumed to have a defined value.
Global variables e.g., are initialized by the compiled code at
zeros; formal parameters have a value which is equal to the value
of the corresponding actual parameter.
These variables can be used without explicitly initializing them.
The initial value of automatic variables is undefined (if they are
not initialized at declaration).
These variables should be set before they are used.
A variable is set by
.IP
.RS
.IP 1.
an assignment (including an initialization)
.IP 2.
taking the address
.RE
.PP
The first case is clear. The second case is plausible.
It would take to much effort (if at all possible) to check
if a variable is set through one of its aliases.
Because
.I lint
should not warn about correct constructs, it does this conservative
approach.
Structures (and unions) can also be set by setting at
least one member.
Again a conservative approach.
An array can be set by using its name (e.g. as actual parameter
of a function call).
.I Lint
warns for usage as
.I rvalue
of automatic variables which are not set.
.PP
A variable is used if
.IP
.RS
.IP 1.
it is used as a
.I rvalue
.IP 2
its address is taken
.IP
Arrays and structures (and unions) are also used if one entry
or one member respectively is used.
.RE
.PP
When a variable is never used in the part of the program where it is
visible, a warning is given.
For variables declared at the beginning of a compound statement,
a check is made at the end of this statement.
For formal parameters a check is made at the end of the function
definition.
At the end of a file this is done for global static definitions.
For external variables a warning can be given when all the files
are parsed.
.NH 2
Flow of control
.PP
The way
.I lint
keeps track of the flow of control is best explained by means of
an example.
See the program of figure 1.
.KF
.DS B
.ft CW
if (cond)
/* a statement which is executed if cond is true,
* the if-part
*/
else
/* the else-part */
.DE
.br
.ce
.I
figure\ 1.
.R
.KE
.PP
After evaluation of \f(CWcond\fP, two things can happen.
The if-part is executed or the else-part is executed (but not both).
Variables which are set in the if-part but not in the else-part,
need not be set after the if statement, and vice versa.
.I Lint
detects this and assumes these variables after the if statement to
be \fImaybe set\fR.
(See figure 2.)
.KF
.DS B
.ft CW
int cond;
main()
{
int i, j;
if (cond) {
i = 0;
j = 0;
}
else
use(i); /* i may be used before set */
use(j); /* maybe j used before set */
}
.DE
.br
.ce
.I
figure 2.
.R
.KE
.PP
If both the if-part and the else-part are never left (i.e. they
contain an endless loop or a return statement),
.I lint
knows that the if statement is never left too.
Besides the if statement,
.I lint
knows the possible flows of control in while, do, for and
switch statements.
It also detects some endless loops like \f(CWwhile(1)\fP,
\f(CWdo ... while (1)\fP, \f(CWfor (;;)\fP.
.NH 2
Functions
.PP
Most C compilers will not complain if a function is called with actual
parameters of a different type than the function expects.
Using a function in one file as a function of
type
.I A
while defining it in another file as a function of type
.I B
is also allowed by most compilers.
It needs no explanation that this can lead to serious trouble.
.PP
.I Lint
checks if functions are called with the correct number of arguments,
if the types of the actual parameters correspond with the types of
the formal parameters and if function values are used in a way
consistently with their declaration.
When the result of a function is used, a check is made to see if
the function returns a value.
When a function returns a value,
.I lint
checks if the values of all calls of this function are used.
.NH 2
Undefined evaluation order
.PP
The semantics of C do not define evaluation orders for some
constructs, which, at first sight, seem well defined.
The evaluation order of the expression
.ft CW
a[i]\ =\ i++;
.R
e.g., is undefined.
It can be translated to something with the semantics of
.ft CW
a[i]\ =\ i; i++;
.R
which is what probably was meant, or
.ft CW
a[i+1]\ =\ i; i++;.
.R
An easier example to explain why, is
.ft CW
j\ =\ a[i]\ +\ i++;.
.R
`\f(CW+\fR' Is a so called
.I commutative
operator (with respect to the evaluation order) , as is `\f(CW=\fR'.
This allows the compiler to choose which term to evaluate first.
It is easy to see, that it makes a difference for the value of
.ft CW
j,
.R
which order is chosen.
The expression
.ft CW
i++
.R
is said to have
.I
side effects.
.R
It affects the value of
.ft CW
i.
.R
Because this value is used in the other term, this gives a conflict.
.PP
A function call with reference to a variable as argument can have
side effects to.
Therefor, the evaluation order of
.ft CW
i
.R
in the expression
.ft CW
f(&i)\ +\ i
.R
is undefined.
When a function is called with an array as argument, this array
can be affected by the function, because only the address of the
array is passed to the function.
(In Pascal a copy of the array is passed to the function if the
formal parameter is not declared \fIvar\fP.)
So the evaluation order of
.ft CW
a
.R
in the expression
.ft CW
f(a)\ +\ a[0]
.R
is undefined.
This one is not yet detected by
.I lint.
.PP
Global variables can still cause trouble.
If function
.ft CW
f
.R
affects the global variable
.ft CW
i,
.R
the value of the expression
.ft CW
f()\ +\ i
.R
is undefined, because the evaluation order of \f(CWi\fP is undefined.
.PP
The evaluation order of the arguments of a function is not
defined, so the expression
.ft CW
f(i,\ i++)
.R
gives a warning
.ft CW
i evaluation order undefined.
.R
.NH 2
Pointer alignment problems
.PP
For pointers to objects of different types there are different
alignment restrictions.
On some machines pointers to type char can have both odd and even
values, whereas pointers to type int should contain an even address.
.I Lint
could warn for all pointer conversions.
This is not what
.I lint
does.
.I Lint
assumes that some pointers are more restricted than others, and
that pointers of some types can safely be converted to a pointer
of a less restrictive type.
The order of restriction is as follows (`\(<=' means
`is not more restricted than') :
.PP
.ce
char \(<= short \(<= int \(<= long
.ce
float \(<= double
.NH 2
Libraries
.PP
C is a small language.
As a matter of fact it has no i/o routines.
To make it a useful language, C is supported by libraries.
These libraries contain functions and variables that can be used by any
C program.
.I Lint
knows some libraries too.
At this moment it knows the `-\fIlc\fR', `-\fIlm\fR' and
`-\fIlcurses\fR' libraries.
The `-\fIlc\fR' library, containing definitions for functions from
chapter two and three of the \s-2UNIX\s+2 programmers manual, is default.
.I Lint
warns for definitions of functions or global variables with the
same name as a function definition in a library.
.bp