lint is divided into 3 separate programs: lint, lint1, and
lint2 (the latter two programs reside in /usr/libexec).

lint calls /usr/libexec/cpp to preprocess the program, then passes
the output to lint1, which does most of the work. lint1 then outputs
a .ln file, which is parsed by lint2 to do more holistic checks. all
of this is driven by /usr/bin/lint, which is like a wrapper program.

lint1 implements its own C parser.  it is incapable of parsing some
weird gcc things, such as __attribute__ and so on. OpenBSD's source
tree already does a good job of removing gcc'isms when parsers other
than gcc are detected.

lint1 keeps a symbol table for the current context, which always
includes global symbols for the current translation unit, as well as
locals (inside a function definition). When it parses a function
definition, it pushes a symbol table context onto the stack, and
then pops it off when the function definition ends.

lint1 does the vast majority of its checks one expression at a time.
It uses the symbol table (which contains types of symbols) and almost
nothing else when doing type conversions. All of the checks happen at
parse time. lint1 does not really build an abstract syntax tree (AST)
to represent the entire program; it only keeps track of the symbols
in the current context, and some minimal information about the types
of enclosing control blocks (loops, switch statements, etc). When lint1
is finished parsing an expression, you will not see any more warnings
regarding that expression.

$OpenBSD: README,v 1.2 2007/09/24 19:56:34 jmc Exp $