static char sccsid
[] = "@(#)lint.c 1.2 (Berkeley) %G%";
/* these are appropriate for the -p flag */
int vflag
= 1; /* tell about unused argments */
int xflag
= 0; /* tell about unused externals */
int argflag
= 0; /* used to turn off complaints about arguments */
int libflag
= 0; /* used to generate library descriptions */
int vaflag
= -1; /* used to signal functions with a variable number of args */
int aflag
= 0; /* used to check precision of assignments */
/* flags for the "outdef" function */
contx( p
, down
, pl
, pr
) register NODE
*p
; register *pl
, *pr
; {
if( asgop(p
->in
.op
) ) break;
if( p
->in
.op
== UNARY MUL
&& ( p
->in
.type
== STRTY
|| p
->in
.type
== UNIONTY
|| p
->in
.type
== UNDEF
) ) {
/* struct x f( ); main( ) { (void) f( ); }
* the the cast call appears as U* UNDEF
break; /* the compiler does this... */
if( down
== EFF
&& hflag
) werror( "null effect" );
/* called after processing each job */
/* flag is nonzero if errors were detected */
register struct symtab
*p
;
for( p
=stab
; p
< &stab
[SYMTSZ
]; ++p
){
if( p
->stype
!= TNULL
) {
if( p
->stype
== STRTY
|| p
->stype
== UNIONTY
){
if( dimtab
[p
->sizoff
+1] < 0 ){ /* never defined */
if( hflag
) werror( "struct/union %.8s never defined", p
->sname
);
if( hflag
) werror( "struct/union %s never defined", p
->sname
);
uerror( "static variable %.8s unused",
uerror( "static variable %s unused",
/* with the xflag, worry about externs not used */
/* the filename may be wrong here... */
if( xflag
&& p
->suse
>= 0 && !libflag
){
outdef( p
, LDX
, NOFILE
);
if( p
->suse
< 0 ){ /* used */
outdef( p
, LUM
, SVLINE
);
astype( t
, i
) ATYPE
*t
; {
if( (tt
=BTYPE(t
->aty
))==STRTY
|| tt
==UNIONTY
){
if( i
<0 || i
>= DIMTABSZ
-3 ){
werror( "lint's little mind is blown" );
k
= ((-j
)<<5)^dimtab
[i
]|1;
if( stab
[j
].suse
<= 0 ) {
werror( "no line number for %.8s",
werror( "no line number for %s",
else k
= (stab
[j
].suse
<<5) ^ dimtab
[i
];
bfcode( a
, n
) int a
[]; {
/* code for the beginning of a function; a is an array of
indices in stab for the arguments; n is the number */
/* this must also set retlab */
register struct symtab
*cfp
;
/* if variable number of arguments, only print the ones which will be checked */
if( n
< vaflag
) werror( "declare the VARARGS arguments you want checked!" );
outdef( cfp
, libflag
?LIB
:LDI
, vaflag
>=0?-n
:n
);
/* output the arguments */
t
.aty
= stab
[a
[i
]].stype
;
if( !astype( &t
, stab
[a
[i
]].sizoff
) ) {
fwrite( (char *)&t
, sizeof(ATYPE
), 1, stdout
);
/* count arguments; p points to at least one */
/* the arguemnts are a tower of commas to the left */
c
= 1; /* count the rhs */
t
.extra
= (p
->in
.op
==ICON
);
if( !astype( &t
, p
->in
.csiz
) ) {
fwrite( (char *)&t
, sizeof(ATYPE
), 1, stdout
);
lprt( p
, down
, uses
) register NODE
*p
; {
register struct symtab
*q
;
register struct lnm
*np1
, *np2
;
/* first, set variables which are set... */
if( p
->in
.op
== ASSIGN
) use1
= VALSET
;
else if( p
->in
.op
== UNARY AND
) use1
= VALADDR
;
else if( asgop( p
->in
.op
) ){ /* =ops */
if( down
== EFF
) use1
|= VALASGOP
;
/* print the lines for lint */
if( p
->in
.left
->in
.type
== CHAR
&& p
->in
.right
->in
.op
==ICON
&& p
->in
.right
->tn
.lval
< 0 ){
werror( "nonportable character comparison" );
if( (p
->in
.op
==EQ
|| p
->in
.op
==NE
) && ISUNSIGNED(p
->in
.left
->in
.type
) && p
->in
.right
->in
.op
== ICON
){
if( p
->in
.right
->tn
.lval
< 0 && p
->in
.right
->tn
.rval
== NONAME
&& !ISUNSIGNED(p
->in
.right
->in
.type
) ){
werror( "comparison of unsigned with negative constant" );
if( p
->in
.right
->in
.op
== ICON
&& p
->in
.right
->tn
.lval
== 0 && p
->in
.right
->tn
.rval
== NONAME
){
werror( "unsigned comparison with 0?" );
if( p
->in
.right
->in
.op
== ICON
&& p
->in
.right
->tn
.lval
<= 0 && !ISUNSIGNED(p
->in
.right
->in
.type
) && p
->in
.right
->tn
.rval
== NONAME
){
werror( "degenerate unsigned comparison" );
/* go recursively left, then right */
lprt( p
->in
.left
, down1
, use1
);
lprt( p
->in
.right
, down2
, use2
);
acount
= ctargs( p
->in
.right
);
if( p
->in
.left
->in
.op
== ICON
&& (id
=p
->in
.left
->tn
.rval
) != NONAME
){ /* used to be &name */
struct symtab
*sp
= &stab
[id
];
/* if a function used in an effects context is
* cast to type void then consider its value
* to have been disposed of properly
* thus a call of type undef in an effects
* context is construed to be used in a value
if ((down
== EFF
) && (p
->in
.type
!= UNDEF
)) {
} else if (down
== EFF
) {
/* look for &name case */
if( (id
= p
->tn
.rval
) >= 0 && id
!= NONAME
){
q
->sflags
|= (SREF
|SSET
);
if( (id
= p
->tn
.rval
) >= 0 && id
!= NONAME
){
if( (uses
&VALUSED
) && !(q
->sflags
&SSET
) ){
if( q
->sclass
== AUTO
|| q
->sclass
== REGISTER
){
if( !ISARY(q
->stype
) && !ISFTN(q
->stype
) && q
->stype
!=STRTY
){
werror( "%.8s may be used before set", q
->sname
);
werror( "%s may be used before set", q
->sname
);
if( uses
& VALASGOP
) break; /* not a real use */
if( uses
& VALSET
) q
->sflags
|= SSET
;
if( uses
& VALUSED
) q
->sflags
|= SREF
;
if( uses
& VALADDR
) q
->sflags
|= (SREF
|SSET
);
lnp
->flgs
= (uses
&VALADDR
)?0:((uses
&VALSET
)?VALSET
:VALUSED
);
if( ++lnp
>= &lnames
[LNAMES
] ) --lnp
;
/* recurse, going down the right side first if we can */
switch( optype(p
->in
.op
) ){
lprt( p
->in
.right
, down2
, use2
);
lprt( p
->in
.left
, down1
, use1
);
if( optype(p
->in
.op
) == BITYPE
){
if( p
->in
.op
== ASSIGN
&& p
->in
.left
->in
.op
== NAME
){ /* special case for a = .. a .. */
else lmerge( np1
, np2
, p
->in
.op
!= COLON
);
/* look for assignments to fields, and complain */
if( p
->in
.op
== ASSIGN
&& p
->in
.left
->in
.op
== FLD
&& p
->in
.right
->in
.op
== ICON
) fldcon( p
);
lmerge( np1
, np2
, flag
) struct lnm
*np1
, *np2
; {
/* np1 and np2 point to lists of lnm members, for the two sides
* flag is 1 if commutation is possible, 0 otherwise
* lmerge returns a merged list, starting at np1, resetting lnp
* it also complains, if appropriate, about side effects
register struct lnm
*npx
, *npy
;
for( npx
= np2
; npx
< lnp
; ++npx
){
/* is it already there? */
for( npy
= np1
; npy
< np2
; ++npy
){
if( npx
->lid
== npy
->lid
){ /* yes */
if( npx
->flgs
== 0 || npx
->flgs
== (VALSET
|VALUSED
) )
else if( (npx
->flgs
|npy
->flgs
)== (VALSET
|VALUSED
) ||
(npx
->flgs
&npy
->flgs
&VALSET
) ){
if( flag
) werror( "%.8s evaluation order undefined", stab
[npy
->lid
].sname
);
if( flag
) werror( "%s evaluation order undefined", stab
[npy
->lid
].sname
);
if( npy
->flgs
== 0 ) npx
->flgs
= 0;
else npy
->flgs
|= npx
->flgs
;
/* not there: update entry */
/* all finished: merged list is at np1 */
/* code for the end of a function */
register struct symtab
*cfp
;
if( retstat
& RETVAL
) outdef( cfp
, LRV
, DECTY
);
if( retstat
== RETVAL
+NRETVAL
)
werror( "function %.8s has return(e); and return;", cfp
->sname
);
werror( "function %s has return(e); and return;", cfp
->sname
);
aocode(p
) struct symtab
*p
; {
/* called when automatic p removed from stab */
register struct symtab
*cfs
;
if(p
->suse
>0 && !(p
->sflags
&(SMOS
|STAG
)) ){
if( p
->sclass
== PARAM
){
if( vflag
) werror( "argument %.8s unused in function %.8s",
if( vflag
) werror( "argument %s unused in function %s",
if( p
->sclass
!= TYPEDEF
) werror( "%.8s unused in function %.8s",
if( p
->sclass
!= TYPEDEF
) werror( "%s unused in function %s",
if( p
->suse
< 0 && (p
->sflags
& (SSET
|SREF
|SMOS
)) == SSET
&&
!ISARY(p
->stype
) && !ISFTN(p
->stype
) ){
werror( "%.8s set but not used in function %.8s", p
->sname
, cfs
->sname
);
werror( "%s set but not used in function %s", p
->sname
, cfs
->sname
);
if( p
->stype
== STRTY
|| p
->stype
== UNIONTY
|| p
->stype
== ENUMTY
){
if( dimtab
[p
->sizoff
+1] < 0 ) werror( "structure %.8s never defined", p
->sname
);
if( dimtab
[p
->sizoff
+1] < 0 ) werror( "structure %s never defined", p
->sname
);
defnam( p
) register struct symtab
*p
; {
/* define the current location as the name p->sname */
if( p
->sclass
== STATIC
&& p
->slevel
>1 ) return;
if( !ISFTN( p
->stype
) ) outdef( p
, libflag
?LIB
:LDI
, USUAL
);
/* n integer words of zeros */
andable( p
) NODE
*p
; { /* p is a NAME node; can it accept & ? */
if( p
->in
.op
!= NAME
) cerror( "andable error" );
if( (r
= p
->tn
.rval
) < 0 ) return(1); /* labels are andable */
if( stab
[r
].sclass
== AUTO
|| stab
[r
].sclass
== PARAM
) return(0);
if( stab
[r
].sclass
== REGISTER
) uerror( "can't take & of %.8s", stab
[r
].sname
);
if( stab
[r
].sclass
== REGISTER
) uerror( "can't take & of %s", stab
[r
].sname
);
/* this is called to do local transformations on
an expression tree preparitory to its being
written out in intermediate code.
/* the major essential job is rewriting the
automatic variables and arguments in terms of
/* conversion ops which are not necessary are also clobbered here */
/* in addition, any special features (such as rewriting
exclusive or) are easily handled here as well */
if( p
->in
.left
->in
.type
==ENUMTY
){
p
->in
.left
= pconvert( p
->in
.left
);
/* assume conversion takes place; type is inherited */
tl
= p
->in
.left
->in
.type
;
if( aflag
&& (tl
==LONG
||tl
==ULONG
) && (t
!=LONG
&&t
!=ULONG
) ){
werror( "long assignment may lose accuracy" );
if( aflag
>=2 && (tl
!=LONG
&&tl
!=ULONG
) && (t
==LONG
||t
==ULONG
) && p
->in
.left
->in
.op
!= ICON
){
werror( "assignment to long may sign-extend incorrectly" );
if( ISPTR(tl
) && ISPTR(t
) ){
switch( ISFTN(t
) + ISFTN(tl
) ){
case 0: /* neither is a function pointer */
if( talign(t
,p
->fn
.csiz
) > talign(tl
,p
->in
.left
->fn
.csiz
) ){
if( hflag
||pflag
) werror( "possible pointer alignment problem" );
werror( "questionable conversion of function pointer" );
p
->in
.left
->in
.type
= p
->in
.type
;
p
->in
.left
->fn
.cdim
= p
->fn
.cdim
;
p
->in
.left
->fn
.csiz
= p
->fn
.csiz
;
if( p
->in
.right
->in
.op
!= ICON
) cerror( "bad conversion");
return( buildtree( o
==PMCONV
?MUL
:DIV
, p
->in
.left
, p
->in
.right
) );
offcon( off
, t
, d
, s
) OFFSZ off
; TWORD t
;{ /* make a structure offset node */
/* storage class for such as "int a;" */
return( pflag
? EXTDEF
: EXTERN
);
cinit( p
, sz
) NODE
*p
; { /* initialize p into size sz */
if( p
->in
.left
->in
.op
== ICON
) return;
if( p
->in
.left
->in
.op
== NAME
&& p
->in
.left
->in
.type
== MOE
) return;
uerror( "illegal initialization" );
/* make a name look like an external name in the local machine */
if( isupper(*p
) ) aa
[i
] = tolower( *p
);
static int stripping
= 0;
/* PATCHED by ROBERT HENRY on 8Jul80 to fix 14 character file name bug */
cerror( "filename too long" );
if( strncmp( s
, fsname
.f
.fn
, LFNM
) ){
if( strcmp(s
, fsname
.f
.fn
)) {
strncpy( fsname
.f
.fn
, s
, LFNM
);
fwrite( (char *)&fsname
, sizeof(fsname
), 1, stdout
);
fwrite( fsname
.f
.fn
, strlen(fsname
.f
.fn
)+1, 1, stdout
);
where(f
){ /* print true location of error */
if( f
== 'u' && nerrors
> 1 )
--nerrors
; /* don't get "too many errors" */
fprintf( stderr
, "%s(%d): ", strip(ftitle
), lineno
);
/* a number of dummy routines, unneeded by lint */
cisreg(t
) TWORD t
; {return(1);} /* everyting is a register variable! */
fldty(p
) struct symtab
*p
; {
; /* all types are OK here... */
fldal(t
) unsigned t
; { /* field alignment... */
if( t
== ENUMTY
) return( ALCHAR
); /* this should be thought through better... */
if( ISPTR(t
) ){ /* really for the benefit of honeywell (and someday IBM) */
if( pflag
) uerror( "nonportable field type" );
else uerror( "illegal field type" );
main( argc
, argv
) char *argv
[]; {
for( p
=argv
[1]; argc
>1 && *p
; ++p
){
/* for the moment, -s triggers -h */
case 'u': /* done in second pass */
case 'n': /* done in shell script */
werror( "option %c now default: see `man 6 lint'", *p
);
uerror( "illegal option: %c", *p
);
if( !pflag
){ /* set sizes to sizes of target machine */
SZINT
= ALINT
= sizeof(int)*SZCHAR
;
SZFLOAT
= ALFLOAT
= sizeof(float)*SZCHAR
;
SZDOUBLE
= ALDOUBLE
= sizeof(double)*SZCHAR
;
SZLONG
= ALLONG
= sizeof(long)*SZCHAR
;
SZSHORT
= ALSHORT
= sizeof(short)*SZCHAR
;
SZPOINT
= ALPOINT
= sizeof(int *)*SZCHAR
;
/* now, fix some things up for various machines (I wish we had "alignof") */
ALLONG
= ALDOUBLE
= ALFLOAT
= ALINT
;
return( mainp1( argc
, argv
) );
ctype( type
) unsigned type
; { /* are there any funny types? */
/* put out a common declaration */
outdef( &stab
[i
], libflag
?LIB
:LDC
, USUAL
);
isitfloat ( s
) char *s
; {
/* s is a character string;
if floating point is implemented, set dcon to the value of s */
fldcon( p
) register NODE
*p
; {
/* p is an assignment of a constant to a field */
/* check to see if the assignment is going to overflow, or otherwise cause trouble */
if( !hflag
& !pflag
) return;
s
= UPKFSZ(p
->in
.left
->tn
.rval
);
v
= p
->in
.right
->tn
.lval
;
switch( p
->in
.left
->in
.type
){
if( v
>=0 && (v
>>(s
-1))==0 ) return;
werror( "precision lost in assignment to (possibly sign-extended) field" );
if( v
<0 || (v
>>s
)!=0 ) werror( "precision lost in field assignment" );
outdef( p
, lty
, mode
) struct symtab
*p
; {
/* output a definition for the second pass */
/* if mode is > USUAL, it is the number of args */
else if( mode
== SVLINE
){
strncpy( rc
.l
.name
, exname(p
->sname
), LCHNM
);
if( mode
== DECTY
) t
= DECREF(t
);
astype( &rc
.l
.type
, p
->sizoff
);
rc
.l
.nargs
= (mode
>USUAL
) ? mode
: 0;
fwrite( (char *)&rc
, sizeof(rc
), 1, stdout
);
rc
.l
.name
= exname(p
->sname
);
fwrite( rc
.l
.name
, strlen(rc
.l
.name
)+1, 1, stdout
);