All side effects before a sequence point must be complete before the sequence point, and no evaluations after the sequence point shall have taken place [ANSI, Section 2.1.2.3]. Between sequence points, side effects and evaluations may take place in any order. Hence, the order in which expressions or arguments are evaluated is not specified. Compilers are free to evaluate function arguments and parts of expressions (that do not contain sequence points) in any order. The behavior of code that uses a value that is modified by another expression that is not required to be evaluated before or after the other use is undefined.
LCLint detects instances where undetermined order of evaluation produces undefined behavior. If modifies clauses and globals lists are used, this checking is enabled in expressions involving function calls. Evaluation order checking is controlled by the evalorder flag.
When checking systems without modifies and globals information, evaluation order checking may report errors when unconstrained functions are called in procedure arguments. Since LCLint has no annotations to constrain what these functions may modify, it cannot be guaranteed that the evaluation order is defined if another argument calls an unconstrained function or uses a global variable or storage reachable from a parameter to the unconstrained function. Its best to add modifies and globals clauses to constrain the unconstrained functions in ways that eliminate the possibility of undefined behavior. For large legacy systems, this may require too much effort. Instead, the -evalorderuncon flag may be used to prevent reporting of undefined behavior due to the order of evaluation of unconstrained functions.
Figure 20 shows examples of infinite loops detected by LCLint. An error is reported for the loop in line 14, since neither of the values used in the loop condition (x directly and glob1 through the call to f) is modified by the body of the loop. If the declaration of g is changed to include glob1 in the modifies clause no error is reported. (In this example, if we assume the annotations are correct, then the programmer has probably called the wrong function in the loop body. This isn't surprising, given the horrible choices of function and variable names!)
If an unconstrained function is called within the loop body, LCLint will assume that it modifies a value used in the condition test and not report an infinite loop error, unless infloopsuncon is on. If infloopsuncon is on, LCLint will report infinite loop errors for loops where there is no explicit modification of a value used in the condition test, but where they may be an undetected modification through a call to an unconstrained function (e.g., line 15 in Figure 20).
For switches on enum types, LCLint reports an error if a member of the enumerator does not appear as a case in the switch body (and there is no default case). (Controlled by misscase.)
An example of switch checking is shown in Figure 21.
LCLint reports an error if the marker preceding a break is not consistent with its effect. An error is reported if innerbreak precedes a break that is not breaking an inner loop, switchbreak precedes a break that is not breaking a switch, or loopbreak precedes a break that is not breaking a loop.
if (x == 0) { return "nil"; } else if (x == 1) { return "many"; }produces an error message since the second if has no matching else branch.
Figure 22. Statements with no effect.
Alternate types (Section 8.2.2) can be used to declare functions that return values that may safely be ignored by declaring the result type to alternately by void. Several functions in the standard library are specified to alternately return void to prevent ignored return value errors for standard library functions (e.g., strcpy) where the result may be safely ignored (see Apppendix F).
Figure 23 shows example of ignored return value errors reported by LCLint.
The /*@unused@*/ annotation can be used before a declaration to indicate that
the item declared need not be used. Unused declaration errors are not reported
for identifiers declared with unused.
10.5 Complete
Programs
LCLint can be used on both complete and partial programs. When checking
complete programs, additional checks can be done to ensure that every
identifier declared by the program is defined and used, and that functions that
do not need to be exported are declared static.
LCLint checks that all declared variables and functions are defined (controlled by compdef). Declarations of functions and variables that are defined in an external library, may be preceded by /*@external@*/ to suppress undefined declaration errors.
LCLint reports external declarations which are unused (Controlled by topuse). Which declarations are reported also depends on the declaration use flags (see Section 10.4).
The partial flag sets
flags for checking a partial system. Top-level unused declarations,
undefined declarations, and unnecessary external names are not reported
if partial is set.
10.5.1 Unnecessary External Names
LCLint can report variables and functions that are declared with global scope
(i.e., without using static), that are not used outside the file in which they
are defined. In a stand-alone system, these identifiers should usually be
declared using static to limit their scope. If the exportstatic flag is on,
LCLint will report declarations that could have file scope. It should only be
used when all relevant source files are listed on the LCLint command line;
otherwise, variables and functions may be incorrectly identified as only used
in the file scope since LCLint did not process the other file in which they are
used.
10.5.2 Declarations Missing from Headers
A common practice in C programming styles, is that every function or variable
exported by M.c is declared in M.h. If
the exportheader flag is on, LCLint will report exported declarations in M.c that are not declared in M.h.
The ANSI Standard includes limits on minimum numbers that a conforming compiler
must support. Whether of not a particular compiler exceeds these limits, it is
worth checking that a program does not exceed them so that it may be safely
compiled by other compilers. In addition, exceeding a limit may indicate a
problem in the code (e.g., it is too complex if the control nest depth limit is
exceeded) that should be fixed regardless of the compiler. The following
limits are checked by LCLint. For each limit, the maximum value may be set
from the command line (or locally using a stylized comment). If the
ansilimits
flag is on, all limits are checked with the minimum values of a conforming
compiler.
Maximum nesting depth of file inclusion (#include). (ANSI minimum is 8)controlnestdepth
Maximum nesting of compound statements, control structures. (ANSI minimum is 15)numenummembers
Number of members in an enum declaration. (ANSI minimum is 127)numstructfields
Number of fields in a struct or union declaration. (ANSI minimum is 127)
Since human beings themselves are not fully debugged yet, there will be bugs in your code no matter what you do.
- Chris Mason, Zero-defects memo (Microsoft Secrets, Cusumano and Selby)
Next: Appendix A: Availability
Contents