ceriel 33 years ago
parent
commit
270ceb3829
16 changed files with 2046 additions and 0 deletions
  1. 15 0
      doc/lint/.distr
  2. 9 0
      doc/lint/Makefile
  3. 18 0
      doc/lint/abstract
  4. 58 0
      doc/lint/appendix_A
  5. 52 0
      doc/lint/appendix_B
  6. 34 0
      doc/lint/chap1
  7. 57 0
      doc/lint/chap2
  8. 294 0
      doc/lint/chap3
  9. 979 0
      doc/lint/chap4
  10. 107 0
      doc/lint/chap5
  11. 107 0
      doc/lint/chap6
  12. 139 0
      doc/lint/chap7
  13. 56 0
      doc/lint/chap8
  14. 48 0
      doc/lint/chap9
  15. 59 0
      doc/lint/contents
  16. 14 0
      doc/lint/frontpage

+ 15 - 0
doc/lint/.distr

@@ -0,0 +1,15 @@
+Makefile
+abstract
+appendix_A
+appendix_B
+chap1
+chap2
+chap3
+chap4
+chap5
+chap6
+chap7
+chap8
+chap9
+contents
+frontpage

+ 9 - 0
doc/lint/Makefile

@@ -0,0 +1,9 @@
+# $Header$
+
+FP =	frontpage
+
+DOC =	abstract contents chap1 chap2 chap3 chap4 chap5 chap6 chap7\
+	chap8 chap9 appendix_A appendix_B
+
+../lint.doc:	$(FP) $(DOC)
+	cat $(FP) $(DOC) > ../lint.doc

+ 18 - 0
doc/lint/abstract

@@ -0,0 +1,18 @@
+.TL
+Lint, a C Program Checker
+.AU
+Frans Kunst
+.AI
+Vrije Universiteit
+Amsterdam
+.AB
+This document describes an implementation of a program which
+does an extensive consistency and plausibility check on a set
+of C program files.
+This may lead to warnings which help the programmer to debug
+the program, to remove useless code and to improve his style.
+The program has been used to test itself and has found
+bugs in sources of some heavily used code.
+.AE
+.LP
+.bp

+ 58 - 0
doc/lint/appendix_A

@@ -0,0 +1,58 @@
+.ds
+.SH
+Appendix A
+.LP
+.SH
+The warnings
+.LP
+.SH
+Pass one warnings
+.DS
+.ft CW
+%s may be used before set
+maybe %s used before set
+%s unused in function %s
+%s set but not used in function %s
+argument %s unused in function %s
+static [variable, function] %s unused
+%s declared extern but never used
+
+long conversion may lose accuracy
+comparison of unsigned with negative constant
+unsigned comparison with 0?
+degenerate unsigned comparison
+nonportable character comparison
+possible pointer alignment problem
+
+%s evaluation order undefined
+
+null effect
+constant in conditional context
+use if-else construction
+while (0) ?
+do ... while (0) ?
+[case, default] statement in strange context
+
+function %s has return(e); and return;
+statement not reached
+function %s declared %s but no value returned
+
+declare the VARARGS args you want checked!
+.ft P
+.DE
+.SH
+Pass two warnings
+.DS
+.ft CW
+%s variable # of args
+%s arg %d used inconsistently
+%s multiply defined
+%s value declared inconsistently
+%s used but not defined
+%s defined (%s(%d)) but never used
+%s declared but never defined
+%s value is used but none is returned
+%s returns value which is [sometimes, always] ignored
+%s also defined in library
+.ft P
+.DE

+ 52 - 0
doc/lint/appendix_B

@@ -0,0 +1,52 @@
+.SH
+Appendix B
+.TL
+The Ten Commandments for C Programmers
+.AU
+Henry Spencer
+.IP 1
+Thou shalt run \fIlint\fR frequently and study its pronouncements with
+care, for verily its perception and judgement oft exceed thine.
+.IP 2
+Thou shalt not follow the NULL pointer, for chaos and madness await thee at
+its end.
+.IP 3
+Thou shalt cast all function arguments to the expected type if they are not
+of that type already, even when thou art convinced that this is unnecessary,
+lest they take cruel vengeance upon thee when thou least expect it.
+.IP 4
+If thy header files fail to declare the return types of thy library functions,
+thou shalt declare them thyself with the most meticulous care,
+lest grievous harm befall thy program.
+.IP 5
+Thou shalt check the array bounds of all strings (indeed, all arrays),
+for surely where thou typest ``foo'' someone someday shall type
+``supercalifragilisticexpialidocious''.
+.IP 6
+If a function be advertised to return an error code in the event of
+difficulties, thou shalt check for that code, yea, even though the checks
+triple the size of thy code and produce aches in thy typing fingers,
+for if thou thinkest ``it cannot happen to me'',
+the gods shall surely punish thee for thy arrogance.
+.IP 7
+Thou shalt study thy libraries and strive not to re-invent them without cause,
+that thy code may be short and readable and thy days pleasant and productive.
+.IP 8
+Thou shalt make thy program's purpose and structure
+clear to thy fellow man by using the
+One True Brace Style,
+even if thou likest it not,
+for thy creativity is better used in solving problems than in creating
+beautiful new impediments to understanding.
+.IP 9
+Thy external identifiers shall be unique in the first six characters,
+though this harsh discipline be irksome and the years of its necessity
+stretch before thee seemingly without end,
+lest thou tear thy hair out and go mad on that fateful day when
+thou desirest to make thy program run on an old system.
+.IP 10
+Thou shalt foreswear, renounce, and abjure the vile heresy which claimeth
+that ``All the world's a VAX'', and have no commerce with the
+benighted heathens who cling to this barbarous belief,
+that the days of thy program may be long even though the days of thy
+current machine be short.

+ 34 - 0
doc/lint/chap1

@@ -0,0 +1,34 @@
+.NH 1
+Introduction
+.PP
+C [1][2] is a dangerous programming language.
+The programmer is allowed to do almost anything, as long as
+the syntax of the program is correct.
+This has a reason. In this way it is possible to make a fast
+compiler which produces fast code.
+The compiler will be fast because it doesn't do much checking
+at compile time.
+The code is fast because the compiler doesn't generate run time
+checks.
+The programmer should protect himself against producing error
+prone code.
+One way to do that is to obey the
+.I
+Ten Commandments for C programmers
+.R
+[appendix B].
+This document describes an implementation of the
+.I lint
+program, as referred to in Commandment 1.
+It is a common error to run
+.I lint
+only after a few hours of debugging and some
+bug can't be found.
+.I Lint
+should be run when large pieces of new code are accepted by the
+compiler and as soon as bugs arise.
+Even for working programs it is useful to run
+.I lint,
+because it can find constructions that may lead to problems in 
+the future.
+.bp

+ 57 - 0
doc/lint/chap2

@@ -0,0 +1,57 @@
+.NH
+Outline of the program
+.PP
+The program can be divided into three parts. A first pass, which
+parses C program files and outputs definitions, a second pass which
+processes the definitions and a driver,
+which feeds the set of files to the first pass and
+directs its output to the second pass. Both passes produce the
+warnings on standard error output, which are redirected to standard
+output by the driver.
+.PP
+The first pass is based on an existing C front end, called
+.I cem
+[3].
+.I Cem
+is part of the Amsterdam Compiler Kit (ACK), as described in [4].
+.PP
+Most of the code of
+.I cem
+is left unchanged. This has several reasons. A lot of work, which
+is done by
+.I cem
+, must also be done by
+.I lint.
+E.g. the lexical analysis, the macro expansions,
+the parsing part and the semantical analysis.
+Only the code generation part is turned off.
+An advantage of this approach is, that a person who understands 
+.I cem
+will not have to spend to much time in understanding
+.I lint.
+.PP
+All changes and extensions to
+.I cem
+can be turned off by not defining the compiler directive
+.ft CW
+LINT.
+.R
+Compiling should then result in the original C compiler.
+.PP
+The second pass is a much less complex program.
+It reads simple definitions generated by the first pass and
+checks their consistency.
+This second pass gives warnings
+about wrong usage of function arguments, their results and
+about external variables, which are used and defined in more
+than one file.
+.PP
+The driver is a shell program, to be executed by the
+.UX
+shell
+.I sh.
+It executes the two passes and let them communicate through a
+filter (sort).
+Actually it is simplex communication: the first pass only talks to
+the second pass through the filter.
+.bp

+ 294 - 0
doc/lint/chap3

@@ -0,0 +1,294 @@
+.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

+ 979 - 0
doc/lint/chap4

@@ -0,0 +1,979 @@
+.NH 1
+How lint checks
+.NH 2
+The first pass first pass data structure
+.PP
+The data structure of
+.I cem
+is changed a little and some structures have been added.
+.NH 3
+The changes
+.NH 4
+Idf descriptor
+.PP
+A member
+.ft CW
+id_line
+.R
+is added
+to the
+.I idf
+selector.
+This line number is used for some warnings.
+.NH 4
+Def descriptor
+.PP
+The
+.I def
+selector is extended with the members
+.ft CW
+df_set
+.R and
+df_line.
+.R
+The
+.ft CW
+df_used
+.R
+member did exist already, but was only used for code generation.
+This usage is eliminated so it can be used by
+.I lint.
+The meaning of these members should be clear.
+.NH 3
+The additions
+.NH 4
+Lint_stack_entry descriptor
+.DS B
+.ft CW
+struct lint_stack_entry {
+        struct lint_stack_entry *next;
+        struct lint_stack_entry *previous;
+        short ls_class;
+        int ls_level;
+        struct state *ls_current;
+        union {
+                struct state *S_if;
+                struct state *S_end;
+                struct switch_states switch_state;
+        } ls_states;
+};
+.R
+.DE
+.PP
+Structure to simulate a stacking mechanism.
+.IP \f(CWnext\fP 15
+Pointer to the entry on top of this one.
+.IP \f(CWprevious\fP
+Pointer to the entry beneath this one.
+.IP \f(CWls_class\fP
+The class of statement this entry belongs to.
+Possible classes are \f(CWIF\fP, \f(CWWHILE\fP, \f(CWDO\fP,
+\f(CWFOR\fP, \f(CWSWITCH\fP and \f(CWCASE\fP.
+.IP \f(CWls_level\fP
+The level the corresponding statement is nested.
+.IP \f(CWls_current\fP
+A pointer to the state descriptor which describes the state
+of the function (the state of the automatic variables, if the next
+statement can be reached, et cetera) if control passes the
+flow of control to the part of the program currently parsed.
+The initialization of this state is as follows
+.RS
+.IP
+If \f(CWls_class\fP in [\f(CWIF\fP, \f(CWSWITCH\fP] the state
+after parsing the conditional expression.
+.IP
+If \f(CWls_class\fP in [\f(CWWHILE\fP, \f(CWFOR\fP] the state
+after parsing the code between the brackets.
+.IP
+If \f(CWls_class\fP in [\f(CWDO\fP, \f(CWCASE\fP] the state at
+entrance of the statement after the \f(CWDO\fP or \f(CWCASE\fP
+token.
+.RE
+.IP \f(CWls_states\fP 15
+Union of pointers to state descriptors containing different information
+for different values of \f(CWls_class\fP.
+.RS
+.IP
+If \f(CWls_class\fP is \f(CWIF\fP and in case of parsing an else part,
+\f(CWls_states.S_if\fP points to the state that is reached after the
+if part.
+.IP
+If \f(CWls_class\fP in [\f(CWWHILE\fP, \f(CWFOR\fP, \f(CWDO\fP]
+then \f(CWls_states.S_end\fP contains a conservative description
+of the state of the program after `jumping'
+to the end of the statement after the \f(CWWHILE\fP, \f(CWDO\fP
+or \f(CWFOR\fP token.
+I.e. the state at reaching a break (not inside a switch) or
+continue statement.
+.IP
+If ls_class is \f(CWSWITCH\fP, \f(CWls_states\fP is used as a structure
+.DS B
+.ft CW
+struct switch_states {
+        struct state S_case;
+        struct state S_break;
+};
+.R
+.DE
+containing two pointers to state descriptors.
+\f(CWls_states.switch_state.S_case\fP contains
+a conservative description
+of the state of the program after \f(CWcase ... case\fP
+parts are parsed.
+\f(CWls_states.switch_state.S_break\fP the state after parsing
+all the \f(CWcase ... break\fP parts.
+The reason for \f(CWls_states.switch_state.default_met\fP should be
+self-explanatory.
+.IP
+In case \f(CWls_class\fP is \f(CWCASE\fP, \f(CWls_states\fP is not used.
+.RE
+.NH 4
+State descriptor
+.DS B
+.ft CW
+struct state {
+        struct state *next;
+        struct auto_def *st_auto_list;
+        int st_nrchd;
+        int st_warned;
+};
+.R
+.DE
+.IP \f(CWst_auto_list\fP 15
+Pointer to a list of definitions of the automatic variables whose
+scope contain the current position in the program.
+.IP \f(CWst_nrchd\fP
+True if the next statement can't be reached.
+.IP \f(CWst_warned\fP
+True if a warning has already been given.
+.NH 4
+Auto_def descriptor
+.DS B
+.ft CW
+struct auto_def {
+        struct auto_def *next;
+        struct idf *ad_idf;
+        struct def *ad_def;
+        int ad_used;
+        int ad_set;
+        int ad_maybe_set;
+};
+.R
+.DE
+.IP \f(CWnext\fP 15
+Points to the next auto_definition of the list.
+.IP \f(CWad_idf\fP
+Pointer to the idf descriptor associated with this auto_definition.
+.IP \f(CWad_def\fP
+Ditto for def descriptor.
+.IP \f(CWad_used\fP
+Indicates the state of this automatic variable.
+Ditto for \f(CWad_set\fP and \f(CWad_maybe_set\fP.
+Only one of \f(CWad_set\fP and \f(CWad_maybe_set\fP may be true.
+.NH 4
+Expr_state descriptor
+.DS B
+.ft CW
+struct expr_state {
+        struct expr_state *next;
+        struct idf *es_idf;
+        arith es_offset;
+        int es_used;
+        int es_set;
+};
+.R
+.DE
+.PP
+This structure is introduced to keep track of which variables,
+array entries and structure members (union members) are set
+and/or used in evaluating an expression.
+.IP \f(CWnext\fP 15
+Pointer to the next descriptor of this list.
+.IP \f(CWes_idf\fP
+Pointer to the idf descriptor this descriptor belongs to.
+.IP \f(CWes_offset\fP
+In case of an array, a structure or union, this member contains
+the offset the compiler would generate for locating the array
+entry or structure/union member.
+.IP \f(CWes_used\fP
+True if the indicated memory location is used in evaluating the
+expression.
+.IP \f(CWes_set\fP
+Ditto for set.
+.NH 4
+Outdef descriptor
+.DS B
+.ft CW
+struct outdef {
+        int od_class;
+        char *od_name;
+        char *od_file;
+        unsigned int od_line;
+        int od_nrargs;
+        struct tp_entry *od_entry;
+        int od_returns;
+        struct type *od_type;
+};
+.DE
+.R
+.PP
+As structures of this type are not allocated dynamically by a
+storage allocator, it contains no next member.
+An outdef can be given to to \f(CWoutput_def()\fP to be passed to the
+second pass.
+Basically this forms the interface with the second pass.
+.IP \f(CWod_class\fP 15
+Indicates what kind of definition it is.
+Possible classes are \f(CWEFDF\fP, \f(CWEVDF\fP, \f(CWSFDF\fP,
+\f(CWSVDF\fP, \f(CWLFDF\fP, \f(CWLVDF\fP,
+\f(CWEFDC\fP, \f(CWEVDC\fP, \f(CWIFDC\fP, \f(CWFC\fP, \f(CWVU\fP.
+([\f(CWE\fPxternal, \f(CWS\fPtatic, \f(CWL\fPibrary, \f(CWI\fPmplicit]
+[\f(CWF\fPunction, \f(CWV\fPariable]
+[\f(CWD\fPe\f(CWF\fPinition, \f(CWD\fPe\f(CWC\fPlaration,
+\f(CWC\fPall, \f(CWU\fPsage])
+.IP \f(CWod_name\fP
+The name of the function or variable.
+.IP \f(CWod_file\fP
+The file this definition comes from.
+.IP \f(CWod_nrargs\fP
+If \f(CWod_class\fP is one of \f(CWEFDF\fP, \f(CWSFDF\fP or
+\f(CWLFDF\fP, this member contains the
+number of arguments this function has.
+If the function was preceded by the pseudocomment
+\f(CW/*\ VARARGS\ */\fP,
+\f(CWod_nrargs\fP gets the value \f(CW-1-n\fP.
+.IP \f(CWod_entry\fP
+A pointer to a list of \f(CWod_nrargs\fP cells, each containing a
+pointer to the type descriptor of an argument. (\f(CW-1-od_nrargs\fP
+cells if
+\f(CWod_nrargs < 0\fP.)
+\f(CWTp_entry\fP is defined as
+.DS B
+.ft CW
+struct tp_entry {
+        struct tp_entry *next; /* pointer to next cell */
+        struct type *te_type;  /* an argument type     */
+};
+.R
+.DE
+.IP \f(CWod_returns\fP 15
+For classes \f(CWEFDF\fP, \f(CWSFDF\fP and \f(CWLFDF\fP this
+member tells if the function returns an expression or not.
+In case \f(CWod_class\fP is \f(CWFC\fP it is true if the value
+of the function is used, false otherwise.
+For other classes this member is not used.
+.IP \f(CWod_type\fP
+A pointer to the type of the function or variable defined or
+declared.
+Not used for classes \f(CWFC\fP and \f(CWVU\fP.
+.NH 2
+The first pass checking mechanism
+.PP
+In the description of the implementation of the pass one 
+warnings, it is assumed that the reader is familiar with the
+\fILLgen\fP parser generator, as described in [6].
+.NH 3
+Used and/or set variables
+.PP
+To be able to give warnings like
+.ft CW
+%s used before set
+.R
+and
+.ft CW
+%s set but not used in function %s
+.R
+, there needs to be a way to keep track of the state of a variable.
+A first approach to do this was by adding two fields to the
+\fIdef\fP selector: 
+.ft CW
+df_set
+.R
+and
+.ft CW
+df_used.
+.R
+While parsing the program, each time an expression was met
+this expression was analyzed and the fields of each \fIdef\fP
+selector were possibly set during this analysis.
+This analysis was done by passing each expression to a
+function 
+.ft CW
+lint_expr
+.R
+, which walks the expression tree in a way similar to the function
+\f(CWEVAL\fP in the file \fIeval.c\fP of the original
+.I
+cem
+.R
+compiler.
+This approach has one big disadvantage: it is impossible to keep
+track of the flow of control of the program.
+No warning will be given for the program fragment of figure 3.
+.KF
+.DS B
+.ft CW
+func()
+{
+        int i;
+
+        if (cond)
+                i = 0;
+        else
+                use(i);  /* i may be used before set */
+}
+.I
+.DE
+.br
+.ce
+figure\ 3.
+.R
+.KE
+.PP
+It is clear that it would be nice having
+.I lint
+warn for this construction.
+.PP
+This was done in the second approach.
+When there was a choice between two statements, each statement
+was parsed with its own copy of the state at entrance of the
+.I
+choosing statement.
+.R
+A state consisted of the state of the automatic variables
+(including register variables).
+In addition to the possibilities of being used and set,
+a variable could be \fImaybe set\fP.
+These states were passed between the statement parsing routines
+using the \fILLgen\fP parameter mechanism.
+At the end of a choosing statement, the two states were merged
+into one state, which became the state after this statement.
+The construction of figure 4 was now detected, but switch
+statements still gave problems and continue and break statements
+were not understood.
+The main problem of a switch statement is, that the closing bracket
+(`\f(CW)\fP') has to be followed by a \fIstatement\fP.
+The syntax shows no choice of statements, as is the case with
+if, while, do and for statements.
+Using the \fILLgen\fP parameter mechanism, it is not a trivial
+task to parse the different case parts of a switch statement
+with the same initial state and to merge the results into one
+state.
+This observation led to the third and final approach, as described
+next.
+.PP
+Instead of passing the state of the program through the statements
+parsing routines using the \fILLgen\fP parameters, a special stack is
+introduced, the
+.I lint_stack.
+When a choosing statement is parsed, an entry is pushed on the stack
+containing the information that is needed to keep track of the
+state of the program.
+Each entry contains a description of the
+.I current
+state of the program and a field that indicates what part of the
+program the parser is currently parsing.
+For all the possible choosing statements I describe the actions
+to be taken.
+.PP
+At entrance of an if statement, an entry is pushed on the stack
+with the current state being a copy of the current state of the
+stack element one below.
+The class of this entry is \f(CWIF\fP.
+At reaching the else part, the current state is moved to
+another place in this stack entry (to \f(CWS_IF\fP), and a new copy
+of the current state at entrance of this if statement is made.
+At the end of the else part, the two states are merged into
+one state, the new current state, and the \f(CWIF\fP entry is
+popped from the stack.
+If there is no else part, then the state that is reached after
+parsing the if part is merged with the current state at entrance
+of the if statement into the new current state.
+.PP
+At entrance of a while statement a \f(CWWHILE\fP entry is pushed
+on the stack containing a copy of the current state.
+If a continue or break statement is met in the while statement,
+the state at reaching this continue or break statement is
+merged with a special state in the \f(CWWHILE\fP entry, called
+\f(CWS_END\fP.
+(If \f(CWS_END\fP did not yet contain a state, the state is copied
+to \f(CWS_END\fP.)
+At the end of the while statement this \f(CWS_END\fP is merged with the 
+current state, which result is merged with the state at entrance
+of the while statement into the new current state.
+.PP
+A for statement is treated similarly.
+A do statement is treated the same way too, except that \f(CWS_END\fP
+isn't merged with the state at entrance of the do statement,
+but becomes the new current state.
+.PP
+For switch statements a \f(CWSWITCH\fP entry is pushed on the stack.
+Apart from the current state, this entry contains two other
+states, \f(CWS_BREAK\fP and \f(CWS_CASE\fP.
+\f(CWS_BREAK\fP initially contains no state, \f(CWS_CASE\fP
+initially contains a
+copy of the current state at entrance of the switch statement.
+After parsing a case label, a \f(CWCASE\fP entry is pushed on the stack,
+containing a copy of the current state.
+If, after zero or more statements, we meet another case label,
+the state at reaching this case label is merged with \f(CWS_CASE\fP
+of the \f(CWSWITCH\fP entry below and a new copy of the state
+at entrance
+of the switch statement is put in the \f(CWCASE\fP entry.
+If we meet a break statement, we merge the current state with
+\f(CWS_BREAK\fP of the \f(CWSWITCH\fP entry below and pop the
+\f(CWCASE\fP entry.
+In addition to this, the occurrence of a default statement
+inside the switch statement is recorded in the \f(CWSWITCH\fP entry.
+At the end of the switch statement we check if we have met a
+default statement.
+If not, \f(CWS_BREAK\fP is merged with the current state at entrance
+of the switch statement. (Because it is possible that no case
+label will be chosen.)
+Next the \f(CWS_CASE\fP is `special_merged' with \f(CWS_BREAK\fP
+into the new current state.
+For more details about these merge functions see the sources.
+.PP
+With the approach described above, 
+.I lint
+is aware of the flow
+of control in the program.
+There still are some doubtful constructions
+.I lint
+will not detect and there are some constructions (although rare)
+for which
+.I lint
+gives an incorrect warning (see figure 4).
+.KF
+.DS B
+.ft CW
+{
+        int i;
+
+        for (;;) {
+                if (cond) {
+                        i = 0;
+                        break;
+                }
+        }
+        use(i);
+        /* lint warns: maybe i used before set
+         * although  the  fragment  is correct
+         */
+}
+.DE
+.br
+.I
+.ce
+figure\ 4.
+.R
+.KE
+.PP
+A nice advantage of the method is, that the parser stays clear,
+i.e. it isn't extended with extra parameters which must pass the
+states.
+In this way the parser still is very readable and we have a nice
+interface with
+.I lint
+using function calls.
+.NH 3
+Undefined evaluation orders
+.PP
+In expressions the values of some variables are used and some
+variables are set.
+Of course, the same holds for subexpressions.
+The compiler is allowed to choose the order of evaluation of
+subexpressions involving a commutative and associative operator
+(\f(CW*\fP, \f(CW+\fP, \f(CW&\fP, \f(CW|\fP, \f(CW^\fP),
+the comma in a parameter list or an assignment operator.
+In section 3.4 it is made clear that this will lead to
+statements with ambiguous semantics.
+.PP
+The way these constructs are detected is rather straight forward.
+The function which parses an expression (\f(CWlint_expr\fP)
+returns a linked
+list containing information telling which variables are set and
+which variables are used.
+A variable is indicated by its
+.I idf
+descriptor and an
+.I offset.
+This offset is needed for discriminating entries of the same
+array and members of the same structure or union, so it is
+possible to warn about the statement
+.ft CW
+a[b[0]]\ =\ b[0]++;.
+.R
+When \f(CWlint_expr\fP meets a commutative operator (with respect to the
+evaluation order), it calls itself recursively with the operands
+of the operator as expression.
+The returned results are checked for undefined evaluation orders
+and are put together.
+This is done by the function \f(CWcheck_and_merge\fP.
+.NH 3
+Useless statements
+.PP
+Statements which compute a value that is not used,
+are said to have a \fInull effect\fP.
+Examples are \f(CWx = 2, 3;\fP, \f(CWf() + g();\fP and
+\f(CW*p++;\fP.
+(\f(CW*\fP and \f(CW++\fP have the same precedence and associate
+from right to left.)
+.PP
+A conditional expression computes a value too.
+If this value isn't used, it is better to use an if-else
+statement.
+So, if
+.I lint
+sees
+.DS B
+.ft CW
+b ? f() : g();
+.R
+.DE
+.LP
+it warns \f(CWuse if-else construction\fP.
+.NH 3
+Not-reachable statements
+.PP
+The algorithm to detect not-reachable statements (including not
+reachable initializations) is as follows.
+Statements after a label and a case statement and the compound
+statement of a function are always reachable.
+Other statements are not-reachable after:
+.QS
+.RS
+.IP - 1
+a goto statement
+.IP -
+a return statement
+.IP -
+a break statement
+.IP -
+a continue statement
+.IP -
+a switch statement
+.IP -
+an endless loop (a while, do or for loop with a conditional
+which always evaluates to true and without a break statement)
+.IP -
+an if-else statement of which both if part and else part
+end up in a not-reachable state
+.IP -
+a switch statement of which all \f(CWcase ... break\fP parts
+(including
+a \f(CWdefault ... break\fP part) end up in a not-reachable state
+.IP -
+the pseudocomment \f(CW/*\ NOTREACHED\ */\fP
+.RE
+.QE
+.PP
+The algorithm is easily implemented using the \f(CWst_nrchd\fP selector
+in the
+.I state
+descriptor.
+The \f(CWst_warned\fP selector is used to prevent superfluous warnings.
+To detect an endless loop, after a while (<true>), for (..;<true>;..)
+and do part the current state of the stack entry beneath the top one
+is set to not reached.
+If, in the statement following, a break statement is met, this same
+state is set to reached.
+If the while (<cond>) part of the do statement is met, this state
+is set to reached if <cond> doesn't evaluates to true.
+The detection of not-reachable statements after a switch statement
+is done in a similar way.
+In addition it is checked if a default statement isn't met, in
+which case the statement after the switch statement can be reached.
+The warning \f(CWstatement not reached\fP is not given for compound
+statements.
+If
+.I lint
+did, it would warn for the compound statement in a switch statement,
+which would be incorrect.
+.PP
+Not-reachable statements are still interpreted by
+.I lint.
+I.e. when
+.I lint
+warns that some statement can't be reached, it assumes this is
+not what the programmer really wants and it ignores this fact.
+In this way a lot of useless warnings are prevented in the case of
+a not-reachable statement.
+See figure 5.
+.KF
+.DS B
+.ft CW
+{
+        int i;
+
+        for (;;) {
+                /* A loop in which the programmer
+                 * forgot to introduce a conditional
+                 * break statement.
+                 * Suppose i is not used in this part.
+                 */
+        }
+        /* some more code in which i is used */
+}
+/* The warning "statement not reached" highlights the bug.
+ * An additional warning "i unused in function %s" is 
+ * formally correct, but doesn't provide the programmer
+ * with useful information.
+ */
+.DE
+.I
+.ce
+figure\ 5.
+.R
+.KE
+.NH 3
+Functions returning expressions and just returning
+.PP
+Each time a return statement is met,
+.I lint
+checks if an expression is returned or not.
+If a function has a return with expression and a return without
+expression,
+.I lint
+warns
+.ft CW
+function %s has return(e); and return;.
+.R
+If the flow of control can
+.I
+fall through
+.R
+the end of the compound statement of a function, this indicates
+an implicit return statement without an expression.
+If the end of the compound statement of the function can be reached,
+.I lint
+introduces this implicit return statement without expression.
+.PP
+Sometimes the programmer knows for sure that all case parts inside
+a switch statement include all possible cases, so he doesn't
+introduce a default statement.
+This can lead to an incorrect warning.
+Figure 6 shows how to prevent this warning.
+.KF
+.DS B
+.ft CW
+            func()
+            {
+                    switch (cond) {
+                    case 0: return(e0);
+                    case 1: return(e1);
+                    }
+                    /* NOTREACHED */
+            }
+/* no warning: "function func has return(e); and return; */
+.DE
+.I
+.ce
+figure\ 6.
+.R
+.KE
+.PP
+The pseudocomment \f(CW/*\ NOTREACHED\ */\fP can also be used to tell
+.I lint
+that some function doesn't return. See figure 7.
+.KS
+.DS B
+.ft CW
+  func()
+  {
+          switch (cond) {
+          case 0: return(e0);
+          case 1: return(e1);
+          default: error();   /* calls exit or abort */
+                   /* NOTREACHED */
+          }
+  }
+/* no warning: "function func has return(e); and return;" */
+.I
+.DE
+.ce
+figure\ 7.
+.R
+.KE
+.NH 3
+Output definitions for the second pass
+.PP
+The first pass can only process one program file.
+To be able to process a program that spreads over more than one file,
+the first pass outputs definitions that are processed by a second
+pass.
+The format of such a definition is different for different classes:
+.PP
+For class in {EFDF, SFDF, LFDF}
+.DS C
+<name>:<class>:<file>:<line>:<nr of args>:<type of args>:<returns value>:<type>
+.DE
+.LP
+A negative \fInr of args\fP indicates that the function can be called with
+a varying number of arguments.
+.PP
+For class = FC
+.DS C
+<name>:<class>:<file>:<line>:<value is used>:<type>
+.DE
+.LP
+The \fIvalue is used\fP part can have three meanings:
+the value of the function is ignored;
+the value of the function is used;
+the value of the function is cast to type \fIvoid\fP.
+.PP
+For other classes
+.DS C
+<name>:<class>:<file>:<line>:<type>
+.DE
+.LP
+Definitions of class VU (Variable Usage) are only output for \fIused\fP
+global variables.
+.PP
+Structure and union types that are output to the intermediate file
+are simplified.
+(The following occurrences of \fIstructure\fP should be
+read as \fIstructure or union\fP and \fIstruct\fP as \fIstruct or
+union\fP.)
+Structures that are identified by a \fIstructure tag\fP are output
+to the intermediate file as \f(CWstruct <tag>\fP.
+Structures without a structure tag are output as
+\f(CWstruct {<mems>}\fP with \f(CW<mems>\fP a semicolon-separated
+list of types of the members of this structure.
+An alternative way would be to output the complete structure definition.
+However, this gives practical problems.
+It is allowed to define some object of a structure type with a
+structure tag, without this structure being defined at that place.
+The first approach leaves errors, such as in figure 8, undetected.
+.KF
+.DS B
+.ft CW
+    "a.c"                           "b.c"
+
+struct str {                    struct str {
+        float f;                        int i;
+} s;                            };
+
+main()                          func(s)
+{                                       struct str s;
+        func(s);                {}
+}
+.I
+.DE
+.ce
+figure\ 8.
+.R
+.KE
+.PP
+To be able to detect these errors, the first pass should also output
+definitions of structure tags.
+The example of figure 8 would then get a warning like
+.ft CW
+structure str defined inconsistently
+.R
+.PP
+More information on these definitions in section 4.3 and 4.4.
+.NH 3
+Generating libraries
+.PP
+.I Lint
+knows the library `-lc', `-lm' and `-lcurses'.
+If a program uses some other library, it is possible to generate
+a corresponding \fIlint library\fP.
+To do this, precede all the C source files of this library by
+the pseudocomment \f(CW/*\ LINTLIBRARY\ */\fP.
+Then feed these files one by one to the first pass of
+.I lint
+collecting the standard output in a file and ignoring the warnings.
+The resulting file contains library definitions of the functions
+and external variables defined in the library sources, and not more
+than that.
+If this file is called `llib-l\fIname\fP.ln
+.I lint
+can be told to search the library by passing it as argument in
+the command line `-llib-l\fIname\fP.ln.
+The implementation of this feature is simple.
+.PP
+As soon as the pseudocomment \f(CW/*\ LINTLIBRARY\ */\fP is met,
+only function and variable definitions are output with class LFDF
+and LVDF respectively.
+Other definitions, which otherwise would have been output, are
+discarded.
+.PP
+Instead of generating a special lint library file, one can make a
+file containing the library definitions and starting with
+\f(CW/* LINTLIBRARY */\fP.
+This file can then be passed to
+.I lint
+just by its name.
+This method isn't as efficient as the first one.
+.NH 3
+Interpreting the pseudocomments
+.PP
+The interpretation of the pseudocomments is done by the lexical
+analyzer, because this part of the program already took care of the
+comments. 
+At first sight this seems very easy: as soon as some pseudocomment
+is met, raise the corresponding flag.
+Unfortunately this doesn't work.
+The lexical analyzer is a \fIone token look ahead scanner\fP.
+This causes the above procedure to raise the flags one token too
+soon.
+A solution to get the right effect is to reserve two flags per
+pseudocomment.
+The first is set as soon as the corresponding pseudocomment is 
+scanned.
+At the returning of each token this flag is moved to the second flag.
+The delay in this way achieved makes the pseudocomments have effect
+at the correct place.
+.NH 2
+The second pass data structure
+.NH 3
+Inp_def descriptor
+.DS B
+.ft CW
+struct inp_def {
+        struct inp_def *next;
+        int id_class;
+        char id_name[NAMESIZE];
+        char id_file[FNAMESIZE];
+        unsigned int id_line;
+        int id_nrargs;
+        char argtps[ARGSTPSSIZE];
+        int id_returns;
+        char id_type[TYPESIZE];
+        int id_called;
+        int id_used;
+        int id_ignored;
+        int id_voided;
+};
+.R
+.DE
+.PP
+This description is almost similar to the \fIoutdef\fP descriptor as
+described in 4.1.2.5.
+There are some differences too.
+.IP \f(CWnext\fP 15
+As structures of this type are allocated dynamically, this field
+is added so the same memory allocator as used in the first pass can be
+used.
+.LP
+\f(CWid_called
+.br
+id_used
+.br
+id_ignored\fP
+.IP \f(CWid_voided\fP 15
+Some additional fields only used for function definitions.Their
+meaning should be clear.
+.PP
+The other fields have the same meaning as the corresponding fields
+in the \fIoutdef\fP descriptor.
+Some attention should be paid to \f(CWid_argtps\fP and \f(CWid_type\fP.
+These members have type \f(CWarray of char\fP, in contrast to
+their counterparts in the \fIoutdef\fP descriptor.
+The only operation performed on types is a check on equality.
+Types are output by the first pass as a string describing the type.
+The type of \f(CWi\fP in \f(CWint *i();\fP e.g. is output as
+\f(CWint *()\fP.
+Such a string is best put in an \f(CWarray of char\fP to be compared
+easily.
+.NH 2
+The second pass checking mechanism
+.PP
+After all the definitions that are output by the first pass are
+sorted by name, the definitions belonging to one name are ordered
+as follows.
+.QS
+.RS
+.IP - 1
+external definitions
+.IP -
+static definitions
+.IP -
+library definitions
+.IP -
+declarations
+.IP -
+function calls
+.IP -
+variable usages
+.RE
+.QE
+.PP
+The main program of the second pass is easily explained.
+For all different names, do the following.
+First read the definitions.
+If there is more than one definition, check for conflicts.
+Then read the declarations, function calls and variable usages and
+check them against the definitions.
+After having processed all the declarations, function calls and
+variable usages, check the definitions to see if they are used
+correctly.
+The next three paragraphs will explain the three most important
+functions of the program.
+.NH 3
+Read_defs()
+.PP
+This function reads all definitions belonging to the same name.
+Only one external definition is allowed, so if there are more, a
+warning is given.
+In different files it is allowed to define static functions or
+variables with the same name.
+So if a static function is read, \f(CWread_defs\fP checks if there isn't
+already an external definition, and if not it puts the static
+definition in the list of static definitions, to be used later.
+If no external or static definitions are met, a library definition is
+taken as definition.
+If a function or a variable is defined with the same name as a function
+or a variable in a library (which is allowed)
+.I lint
+gives a warning.
+Of course it is also possible that there is no definition at all.
+In that case \f(CWcheck\fP will warn.
+.NH 3
+Check()
+.PP
+\f(CWCheck\fP verifies declarations, function calls and variable
+usages against the definitions.
+For each of these entries the corresponding definition is looked up.
+As there may be more than one static definition, first a static
+definition from the same file as the entry is searched.
+If not present, the external definition (which may be a library
+definition) is taken as definition.
+If no definition can be found and the current entry is an external
+declaration,
+.I lint
+warns.
+However in the case of an implicit function declaration
+.I lint
+will not warn, because
+we will get a warning \f(CW%s used but not defined\fP later on.
+Next a check is done if the declarations are consistent with their
+definitions.
+After the declarations, the function calls and variable usages are
+verified against their corresponding definitions.
+If no definition exists,
+.I lint
+warns.
+Else the field \f(CWid_called\fP is set to 1.
+(For variable definitions this should be interpreted as \fIused\fP.)
+For variable usages this will be all.
+If we are processing a function call we also check the number and types
+of the arguments and we warn for function values which are used from
+functions that don't return a value.
+For each function call we administrate if a function value is used,
+ignored or voided.
+.NH 3
+Check_usage()
+.PP
+Checks if the external definition and static definitions are used
+correctly.
+If a function or variable is defined but never used,
+.I lint
+warns, except for library definitions.
+Functions, which return a value but whose value is always or
+sometimes ignored, get a warning.
+(A function value which is voided (cast to void) is not ignored,
+but it isn't used either.)
+.bp

+ 107 - 0
doc/lint/chap5

@@ -0,0 +1,107 @@
+.NH 1
+How to make lint shut up
+.PP
+It can be very annoying having
+.I lint
+warn about questionable constructs of which the programmer already is
+aware.
+There should be a mechanism to give 
+.I lint
+some extra information in the source code.
+This could be done by introducing some special keywords, which
+would have a special meaning to
+.I lint.
+This is a bad solution, because these keywords would cause existing
+C compilers not to work on these programs.
+A neater solution is to invent some comments having a special
+meaning to 
+.I lint.
+We call these comments 
+.I pseudocomments.
+The pseudocomments have no meaning to existing C compilers, so
+compilers will not have to be rewritten for C programs containing
+the previously proposed special keywords.
+The following pseudocomments are recognized by 
+.I lint.
+.LP
+\f(CW/* VARARGS\fIn\fP */\fR
+.br
+.in 5
+The next function can be called with a variable number of arguments.
+Only check the first \fIn\fP arguments.
+The \fIn\fP must follow the word \f(CWVARARGS\fP immediately.
+This pseudocomment is useful for functions like e.g. printf.
+(The definition of the function printf should be preceded by
+\f(CW/*\ VARARGS1\ */\fP.)
+.in
+.LP
+\f(CW/* VARARGS */\fP
+.br
+.in 5
+Means the same as \f(CW/* VARARGS0 */\fP.
+.in
+.LP
+\f(CW/* ARGSUSED */\fP
+.br
+.in 5
+Don't complain about unused arguments in the next function.
+When we are developing a program we sometimes write functions of
+which we do not yet use the arguments.
+Because we do want to use
+.I lint
+on these programs, it is nice to have this pseudocomment.
+.in
+.LP
+\f(CW/* NOTREACHED */\fP
+.br
+.in 5
+.I Lint
+makes no attempt to discover functions which never return,
+although it \fIis\fP possible to find functions that don't return.
+This would require a transitive closure with respect to the already
+known \fInot-returning\fP functions; an inacceptable time consuming
+process.
+To make 
+.I lint
+aware of a function that doesn't return, a call of this function
+should be followed by the pseudocomment \f(CW/*\ NOTREACHED\ */\fP.
+This pseudocomment can also be used to indicate that some case part
+inside a switch (especially a default part) can't be reached.
+The above mentioned cases of use of this pseudocomment are
+examples. 
+The comment can be used just to indicate that some part of the
+program can't be reached.
+It sometimes is necessary to introduce an extra compound statement
+to get the right effect. 
+See figure 9.
+.KF
+.DS B
+.ft CW
+        if (cond)
+                /* if part */ ;
+        else {
+                error();  /* doesn't return */
+                /* NOTREACHED */
+        }
+/* Without  the compound  else  part, lint  would  assume
+ * the statement after the if statement to be NOTREACHED,
+ * instead of the end of the else part.
+ */
+.I
+.DE
+.ce
+figure\ 9.
+.R
+.KE
+.in
+.LP
+\f(CW/* LINTLIBRARY */\fP
+.br
+.in 5
+All definitions following this comment are assumed to be library
+definitions.
+It shuts off complaints about unused functions and variables.
+See also section 4.2.7 for how to use this comment for generating
+lint libraries.
+.in
+.bp

+ 107 - 0
doc/lint/chap6

@@ -0,0 +1,107 @@
+.NH 1
+User options
+.PP
+.I Lint
+recognizes the following command line flags.
+Some of them are identical to the flags of
+.I cem.
+.I Lint
+warns for flags it doesn't know.
+.LP
+\f(CW-D<name>
+.br
+-D<name>=<text>\fP
+.br
+.in 5
+Causes \f(CW<name>\fP to be defined as a macro.
+The first form is equivalent to `\f(CW-D<name>=1\fP'.
+The second form is equivalent to putting `\f(CW#define <name> <text>\fP'
+in front of all the source files.
+.in
+.LP
+\f(CW-U<name>\fP
+.br
+.in 5
+Acts as if the line `\f(CW#undef <name>\fP' is put in front of all
+the source files.
+.in
+.LP
+\f(CW-I<directory>\fP
+.br
+.in 5
+This puts \f(CW<directory>\fP in the include directory
+list.
+.in
+.LP
+\f(CW-R\fP
+.br
+.in 5
+Turn off the `strict' option.
+Default
+.I lint
+checks the program according to the Reference Manual, because this
+gives a definition of the language with which there is a better chance
+of writing portable programs.
+With this flag on, some constructs, otherwise not allowed, are
+accepted.
+.in
+.LP
+\f(CW-l<name>
+.br
+-llib-l<name>.ln
+.br
+-l\fP
+.br
+.in 5
+`\f(CW-l<name>\fP' tells 
+.I lint
+to search the lint library
+\f(CWllib-l<name>.ln\fP for missing
+definitions of functions and variables.
+The option `\f(CW-llib-l<name>.ln\fP' makes
+.I lint
+search the lint library file \f(CWllib-l<name>.ln\fP in the current
+directory for missing definitions.
+Default is `\f(CW-lc\fP'; this default can be suppressed by
+`\f(CW-l\fP'.
+.in
+.LP
+\f(CW-a\fP
+.br
+.in 5
+Warn for conversions from integer to long and vice versa.
+.in
+.LP
+\f(CW-b\fP
+.br
+.in 5
+Don't report not-reachable break statements.
+This flag is useful for running
+.I lint
+on a \fIlex\fP- or \fIyacc\fP-generated source file.
+.in
+.LP
+\f(CW-h\fP
+.br
+.in 5
+Check for useless statements and possible pointer alignment problems.
+.in
+.LP
+\f(CW-n\fP
+.br
+.in 5
+Don't complain about unused and undefined functions and variables.
+.in
+.LP
+\f(CW-v\fP
+.br
+.in 5
+Don't warn about unused arguments of functions.
+.in
+.LP
+\f(CW-x\fP
+.br
+.in 5
+Complain about unused external variables.
+.in
+.bp

+ 139 - 0
doc/lint/chap7

@@ -0,0 +1,139 @@
+.NH
+Ideas for further development
+.PP
+Although the program in its current state is a useful program,
+there are still a lot of features that should be implemented
+in following versions.
+I'll summarize them in this section.
+.IP \(bu
+Actually the program consists of three passes.
+The filter
+.I sort
+is a complete pass, just as the first and the second pass.
+I think we speed up the program by removing the filter and making
+the second pass accept an unsorted file.
+The sorting process can be done in parallel to the first pass if
+both processes communicate through a pipe.
+In addition to this sorting, the second pass can generate already
+some warnings.
+(Warnings like \f(CW%s defined but never used\fP can only be
+generated after having processed all the input.)
+These warnings generated in parallel to the warnings of the first pass,
+should be sent to an intermediate file, otherwise the warnings would
+get messed up.
+Such an improvement will have best effect on a multi processing
+machine, but even on single processing machines this will give a better
+performance. (On a single processing machine the pipe should be
+replaced by an intermediate file.)
+.IP \(bu
+Expressions could be classified so
+.I lint
+can warn for some classes of expressions in strange contexts.
+Suppose as class <boolean>.
+\f(CWb\fP Will be of class <boolean> if e.g. \f(CWb\fP is assigned to
+the expression \f(CW<ex1> || <ex2>\fP.
+The following expression should then give a warning
+.DS B
+.ft CW
+b + i;    /* weird expression */
+.R
+.DE
+.IP \(bu
+A mechanism to check printf like routines.
+This mechanism should verify the format string against the following
+arguments.
+There is a public domain program that can be used to do this job.
+It is called printfck and should be used as a filter between the
+source files and
+.I lint.
+.IP \(bu
+Raise warnings for incomplete initializer lists like
+.DS B
+.ft CW
+int a[10] = {0, 1, 2};
+/* initializer list not complete */
+.R
+.DE
+.IP \(bu
+Warnings for constructs like
+.DS B
+.ft CW
+for (i = 0; i < 10; i++) {
+        . . . .
+        i--;
+        /* loop control variable affected */
+        . . . .
+}
+.R
+.DE
+and
+.DS B
+.ft CW
+while (var) {
+        /* statements in which the value
+         * of var is never changed
+         */
+}
+/* loop control variable not updated */
+.R
+.DE
+.IP \(bu
+A warning \f(CWbad layout\fP for program fragments like
+.DS B
+.ft CW
+if (cond1)
+        if (cond2)
+                statement();
+else  /* bad layout */
+        statement();
+.R
+.DE
+.IP \(bu
+A warning \f(CWassignment in conditional context\fP in case of
+.DS B
+.ft CW
+if (a = b)
+.R
+.DE
+.IP
+The programmer probably meant \f(CWif (a == b)\fP.
+No warning should be given for \f(CWif ((a = b) != c)\fP,
+nor for \f(CWif ((a = b))\fP.
+.IP \(bu
+Warnings for empty statements in strange contexts, like
+.DS B
+.ft CW
+if (cond);  /* mistake */
+        statement();
+.R
+.DE
+.IP
+(This mistake would also be detected by a warning \f(CWbad layout\fP.)
+.IP \(bu
+A mechanism to prevent the warning \f(CWpossible pointer alignment
+problem\fP for functions of which the programmer already knows that
+no problem will arise.
+E.g. for functions like malloc and family.
+.IP \(bu
+The current version of
+.I lint
+warns for conversions from long to int (if -a flag is
+on).
+It even warns if the programmer used the proper cast, as e.g.
+.DS B
+.ft CW
+int i;
+long l = 0L;
+
+i = (int)l;
+.R
+.DE
+.IP
+In this case I think
+.I lint
+need not warn.
+The explicit cast indicates that the programmer knows what he is
+doing.
+This feature is not implemented because the expression tree doesn't
+show if the cast was implicit or explicit.
+.bp

+ 56 - 0
doc/lint/chap8

@@ -0,0 +1,56 @@
+.NH 1
+Testing the program
+.PP
+There is no test-suite for testing
+.I lint.
+I have written a lot of small files that each test one
+particular property of the program.
+At this moment there are about 220 test programs.
+.PP
+It would take a lot of time and effort to run these tests by hand.
+To ease this work I wrote a program that runs these tests
+automatically.
+The test program (the program that runs the tests) needs, associated
+with each .c file, a .w file, containing from each expected warning
+a substring. E.g. when the following warnings should be given by
+.I lint:
+.DS B
+.ft CW
+        file t.c, line 3, i evaluation order undefined
+        file t.c, line 6, a set but not used in function main
+.R
+.DE
+it is sufficient to write a file \f(CWt.w\fP containing
+.DS B
+.ft CW
+        a set but not used in function main
+        i evaluation order undefined
+.R
+.DE
+The test program is called with all the .c files to be tested
+as arguments.
+.PP
+Sometimes it is necessary to test
+.I lint
+on two files.
+The test program runs
+.I lint
+on two files when two consecutive
+arguments are of the form \fIname\fPa.c and \fIname\fPb.c.
+It then compares the output of
+.I lint
+with the file \fIname\fP.w.
+.PP
+.I Lint
+is also tested by running it on existing programs.
+.I Lint
+has been run on some \s-2UNIX\s+2 utility programs in
+/usr/src/cmd, on Unipress Emacs (consisting of more than 30,000
+lines of code) and the program itself.
+Bugs have been found in e.g. /usr/src/cmd/cat.c and 
+/usr/src/cmd/ld.c.
+To test the robustness of the program, it was run on the
+password file /etc/passwd and on `mixed' C program files.
+These mixed C program files are C program files that were
+broken in chunks and then put together in a different order.
+.bp

+ 48 - 0
doc/lint/chap9

@@ -0,0 +1,48 @@
+.NH 1
+References
+.IP [1]
+Dennis M. Ritchie,
+.I
+C Reference Manual,
+.R
+Bell Laboratories,
+Murray Hill,
+New Jersey,
+1978.
+.IP [2]
+B.W. Kernighan and D.M. Ritchie,
+.I
+The C Programming Language,
+.R
+Prentice Hall,
+1978.
+.IP [3]
+Eric H. Baalbergen, Dick Grune, Maarten Waage,
+.I
+The CEM Compiler,
+.R
+Manual IM-4, Vrije Universiteit, Amsterdam,
+1985.
+.IP [4]
+Andrew S. Tanenbaum et al., 
+.I
+A practical tool kit for making portable compilers,
+.R
+Comm. ACM,
+Sep. 1983.
+.IP [5]
+S. C. Johnson,
+.I
+Lint, a C program verifier,
+.R
+Bell Laboratories,
+Murray Hill,
+New Jersey,
+1978.
+.IP [6]
+Dick Grune, Ceriel J. H. Jacobs,
+.I
+A Programmer-friendly LL(1) Parser Generator,
+.R
+IR 127, Vrije Universiteit, Amsterdam,
+1987.

+ 59 - 0
doc/lint/contents

@@ -0,0 +1,59 @@
+.DS
+
+
+
+
+
+
+.DE
+.SH
+Contents
+.R
+.sp 1
+.IP 1.
+Introduction
+.IP 2.
+Outline of the program
+.IP 3.
+What lint checks
+.RS
+.IP 3.1
+Set, used and unused variables
+.IP 3.2
+Flow of control
+.IP 3.3
+Functions
+.IP 3.4
+Undefined evaluation order
+.IP 3.5
+Pointer alignment problems
+.IP 3.6
+Libraries
+.RE
+.IP 4.
+How lint checks
+.RS
+.IP 4.1
+The first pass data structure
+.IP 4.2
+The first pass checking mechanism
+.IP 4.3
+The second pass data structure
+.IP 4.4
+The second pass checking mechanism
+.RE
+.IP 5.
+How to make lint shut up
+.IP 6.
+User options
+.IP 7.
+Ideas for further development
+.IP 8.
+Testing the program
+.IP 9.
+References
+.LP
+Appendix A \- The warnings
+.br
+Appendix B \- The Ten Commandments for C programmers
+.bp

+ 14 - 0
doc/lint/frontpage

@@ -0,0 +1,14 @@
+.TL
+.sp 5
+Lint, a C Program Checker
+.AU
+Frans Kunst
+.AI
+Vrije Universiteit
+Amsterdam
+.LP
+.sp 8
+.ce
+Afstudeer verslag
+.ce
+18 mei 1988