1097 lines
		
	
	
	
		
			23 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1097 lines
		
	
	
	
		
			23 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands.
 | 
						|
 * See the copyright notice in the ACK home directory, in the file "Copyright".
 | 
						|
 */
 | 
						|
/* $Header$ */
 | 
						|
/*	Lint status checking	*/
 | 
						|
 | 
						|
#include	"lint.h"
 | 
						|
 | 
						|
#ifdef	LINT
 | 
						|
 | 
						|
#include	<alloc.h>	/* for st_free */
 | 
						|
#include	"assert.h"
 | 
						|
#include	"arith.h"	/* definition arith */
 | 
						|
#include	"label.h"	/* definition label */
 | 
						|
#include	"expr.h"
 | 
						|
#include	"idf.h"
 | 
						|
#include	"def.h"
 | 
						|
#include	"code.h"	/* RVAL etc */
 | 
						|
#include	"LLlex.h"
 | 
						|
#include	"Lpars.h"
 | 
						|
#include	"stack.h"
 | 
						|
#include	"type.h"
 | 
						|
#include	"level.h"
 | 
						|
#include	"nofloat.h"
 | 
						|
#include	"l_lint.h"
 | 
						|
#include	"l_brace.h"
 | 
						|
#include	"l_state.h"
 | 
						|
#include	"l_comment.h"
 | 
						|
#include	"l_outdef.h"
 | 
						|
 | 
						|
#define min(a, b) ((a) < (b) ? (a) : (b))
 | 
						|
 | 
						|
extern char *symbol2str();
 | 
						|
extern char *func_name;
 | 
						|
extern struct type *func_type;
 | 
						|
extern int func_notypegiven;
 | 
						|
extern char loptions[];
 | 
						|
 | 
						|
/* global variables for the lint_stack */
 | 
						|
struct lint_stack_entry stack_bottom;
 | 
						|
struct lint_stack_entry *top_ls = &stack_bottom;
 | 
						|
 | 
						|
/* global variables for the brace stack */
 | 
						|
int brace_count;
 | 
						|
struct brace brace_bottom;
 | 
						|
struct brace *top_br = &brace_bottom;
 | 
						|
 | 
						|
static print_autos();
 | 
						|
 | 
						|
lint_init_stack()
 | 
						|
{
 | 
						|
/* Allocate some memory for the global stack_bottom
 | 
						|
 */
 | 
						|
	stack_bottom.ls_current = new_state();
 | 
						|
}
 | 
						|
 | 
						|
lint_start_local()
 | 
						|
{
 | 
						|
	register struct brace *br = new_brace();
 | 
						|
 | 
						|
	brace_count++;
 | 
						|
	br->br_count = brace_count;
 | 
						|
	br->br_level = level;
 | 
						|
	br->next = top_br;
 | 
						|
	top_br = br;
 | 
						|
}	
 | 
						|
 | 
						|
lint_local_level(stl)
 | 
						|
	struct stack_level *stl;
 | 
						|
{
 | 
						|
	if (s_NOTREACHED) {
 | 
						|
		top_ls->ls_current->st_notreached = 1;
 | 
						|
		s_NOTREACHED = 0;
 | 
						|
	}
 | 
						|
 | 
						|
	if (top_ls->ls_class == CASE && level == top_ls->ls_level) {
 | 
						|
		lint_break_stmt();
 | 
						|
			/* To prevent a warning for the case
 | 
						|
			 *	switch (cond) {
 | 
						|
			 *	int i;
 | 
						|
			 *	case 0:
 | 
						|
			 *		i = 0;
 | 
						|
			 *		use(i);
 | 
						|
			 *	}
 | 
						|
			 */
 | 
						|
	}
 | 
						|
 | 
						|
	check_autos();
 | 
						|
	end_brace(stl);
 | 
						|
}
 | 
						|
 | 
						|
end_brace(stl)
 | 
						|
	struct stack_level *stl;
 | 
						|
{
 | 
						|
	/*	Check if static variables and labels are used and/or set.
 | 
						|
	*/
 | 
						|
	register struct stack_entry *se = stl->sl_entry;
 | 
						|
	register struct brace *br;
 | 
						|
 | 
						|
	while (se) {
 | 
						|
		register struct idf *idf = se->se_idf;
 | 
						|
		register struct def *def = idf->id_def;
 | 
						|
 | 
						|
		if (def) {
 | 
						|
			lint_1_local(idf, def);
 | 
						|
		}
 | 
						|
		se = se->next;
 | 
						|
	}
 | 
						|
 | 
						|
	br = top_br;
 | 
						|
	top_br = br->next;
 | 
						|
	free_brace(br);
 | 
						|
}
 | 
						|
 | 
						|
lint_1_local(idf, def)
 | 
						|
	struct idf *idf;
 | 
						|
	struct def *def;
 | 
						|
{
 | 
						|
	register int sc = def->df_sc;
 | 
						|
 | 
						|
	if (	(sc == STATIC || sc == LABEL)
 | 
						|
	&&	!def->df_used
 | 
						|
	) {
 | 
						|
		def_warning(def, "%s %s declared but not used in function %s",
 | 
						|
			symbol2str(sc), idf->id_text, func_name);
 | 
						|
	}
 | 
						|
 | 
						|
	if (	loptions['h']
 | 
						|
	&&	sc == AUTO
 | 
						|
	&&	!def->df_initialized
 | 
						|
	&&	def->df_firstbrace != 0
 | 
						|
	&&	def->df_minlevel != level
 | 
						|
	) {
 | 
						|
		register int diff = def->df_minlevel - level;
 | 
						|
 | 
						|
		def_warning(def,
 | 
						|
			"local %s could be declared %d level%s deeper",
 | 
						|
			idf->id_text, diff, (diff == 1 ? "" : "s")
 | 
						|
		);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
lint_global_level(stl)
 | 
						|
	struct stack_level *stl;
 | 
						|
{
 | 
						|
	register struct stack_entry *se = stl->sl_entry;
 | 
						|
 | 
						|
	ASSERT(level == L_GLOBAL);
 | 
						|
	while (se) {
 | 
						|
		register struct idf *idf = se->se_idf;
 | 
						|
		register struct def *def = idf->id_def;
 | 
						|
 | 
						|
		if (def) {
 | 
						|
			lint_1_global(idf, def);
 | 
						|
		}
 | 
						|
		se = se->next;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
lint_1_global(idf, def)
 | 
						|
	struct idf *idf;
 | 
						|
	struct def *def;
 | 
						|
{
 | 
						|
	register int sc = def->df_sc;
 | 
						|
	register int fund = def->df_type->tp_fund;
 | 
						|
 | 
						|
	switch (sc) {
 | 
						|
	case STATIC:
 | 
						|
	case EXTERN:
 | 
						|
	case GLOBAL:
 | 
						|
	case IMPLICIT:
 | 
						|
		if (fund == ERRONEOUS)
 | 
						|
			break;
 | 
						|
 | 
						|
		if (def->df_set || def->df_used) {
 | 
						|
			/* Output a line to the intermediate file for
 | 
						|
			 * used external variables (including functions)
 | 
						|
			 */
 | 
						|
			output_use(idf);
 | 
						|
		}
 | 
						|
		else {
 | 
						|
			if (sc == STATIC) {
 | 
						|
				if (def->df_set) {
 | 
						|
					def_warning(def,
 | 
						|
						"%s %s %s set but not used",
 | 
						|
						symbol2str(sc),
 | 
						|
						symbol2str(fund),
 | 
						|
						idf->id_text);
 | 
						|
				}
 | 
						|
				else {
 | 
						|
					def_warning(def,
 | 
						|
						"%s %s %s not used anywhere",
 | 
						|
						symbol2str(sc),
 | 
						|
						symbol2str(fund),
 | 
						|
						idf->id_text);
 | 
						|
				}
 | 
						|
			}
 | 
						|
			if (loptions['x']) {
 | 
						|
				register char *fn = def->df_file;
 | 
						|
 | 
						|
				if (	(sc == EXTERN || sc == GLOBAL)
 | 
						|
				&&	def->df_alloc == 0
 | 
						|
				&&	!def->df_set
 | 
						|
				&&	!def->df_initialized
 | 
						|
				&&	strcmp(&fn[strlen(fn)-2], ".c") == 0
 | 
						|
				) {
 | 
						|
					def_warning(def,
 | 
						|
						"%s %s %s not used anywhere",
 | 
						|
						symbol2str(sc),
 | 
						|
						symbol2str(fund),
 | 
						|
						idf->id_text);
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
		break;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
change_state(idf, to_state)
 | 
						|
	struct idf *idf;
 | 
						|
{
 | 
						|
/* Changes the state of the variable identified by idf in the current state
 | 
						|
 * on top of the stack.
 | 
						|
 * For non-automatic variables, the fields in the def-descriptor are set too.
 | 
						|
 */
 | 
						|
	register struct def *def = idf->id_def;
 | 
						|
	register struct auto_def *a = top_ls->ls_current->st_auto_list;
 | 
						|
 | 
						|
	if (def) {
 | 
						|
		if (to_state == SET)
 | 
						|
			def->df_set = 1;
 | 
						|
		else
 | 
						|
			def->df_used = 1;
 | 
						|
 | 
						|
		if (def->df_firstbrace == 0) {
 | 
						|
			def->df_firstbrace = brace_count;
 | 
						|
			def->df_minlevel = level;
 | 
						|
		}
 | 
						|
		else {
 | 
						|
			register struct brace *br = top_br;
 | 
						|
 | 
						|
			/*	find the smallest brace range from which
 | 
						|
				firstbrace is visible
 | 
						|
			*/
 | 
						|
			while (br && br->br_count > def->df_firstbrace) {
 | 
						|
				br = br->next;
 | 
						|
			}
 | 
						|
			ASSERT(br && def->df_minlevel >= br->br_level);
 | 
						|
			def->df_minlevel = br->br_level;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	while(a && a->ad_idf != idf)
 | 
						|
		a = a->next;
 | 
						|
	if (a == 0)	/* identifier not in list */
 | 
						|
		return;
 | 
						|
 | 
						|
	if (to_state == SET) {
 | 
						|
		a->ad_maybe_set = 0;
 | 
						|
		a->ad_set = 1;
 | 
						|
		return;
 | 
						|
	}
 | 
						|
	/* else to_state == USED */
 | 
						|
	if (!a->ad_set) {
 | 
						|
		warning("%s%s uninitialized", idf->id_text,
 | 
						|
			(a->ad_maybe_set ? " possibly" : "")
 | 
						|
		);
 | 
						|
		a->ad_maybe_set = 0;
 | 
						|
		a->ad_set = 1;	/* one warning */
 | 
						|
	}
 | 
						|
	a->ad_used = 1;
 | 
						|
}
 | 
						|
 | 
						|
extern struct stack_level *local_level;
 | 
						|
 | 
						|
add_auto(idf)	/* to current state on top of lint_stack */
 | 
						|
	struct idf *idf;
 | 
						|
{
 | 
						|
/* Check if idf's definition is really an auto (or register).
 | 
						|
 * It could be a static or extern too.
 | 
						|
 * Watch out for register formal parameters.
 | 
						|
 */
 | 
						|
	register struct def *def = idf->id_def;
 | 
						|
	register struct auto_def *a;
 | 
						|
 | 
						|
	if (!def)
 | 
						|
		return;
 | 
						|
	switch (def->df_sc) {
 | 
						|
	case AUTO:
 | 
						|
	case REGISTER:
 | 
						|
		if (def->df_level < L_LOCAL)
 | 
						|
			return;		/* a register formal */
 | 
						|
		a = new_auto_def();
 | 
						|
		a->ad_idf = idf;
 | 
						|
		a->ad_def = idf->id_def;
 | 
						|
		a->ad_used = def->df_used;
 | 
						|
		a->ad_set = def->df_set;
 | 
						|
		a->next = top_ls->ls_current->st_auto_list;
 | 
						|
		top_ls->ls_current->st_auto_list = a;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
check_autos()
 | 
						|
{
 | 
						|
/* Before leaving a block remove the auto_defs of the automatic
 | 
						|
 * variables on this level and check if they are used
 | 
						|
 */
 | 
						|
	register struct auto_def *a1 = top_ls->ls_current->st_auto_list;
 | 
						|
	register struct auto_def *a2;
 | 
						|
 | 
						|
	ASSERT(!(a1 && a1->ad_def->df_level > level));
 | 
						|
	while (a1 && a1->ad_def->df_level == level) {
 | 
						|
		a2 = a1;
 | 
						|
		a1 = a1->next;
 | 
						|
		if (!a2->ad_used) {
 | 
						|
			if (a2->ad_set || a2->ad_maybe_set) {
 | 
						|
				def_warning(a2->ad_def,
 | 
						|
					"%s set but not used in function %s",
 | 
						|
					a2->ad_idf->id_text, func_name);
 | 
						|
			}
 | 
						|
			else {
 | 
						|
				def_warning(a2->ad_def,
 | 
						|
					"%s neither set nor used in function %s",
 | 
						|
					a2->ad_idf->id_text, func_name);
 | 
						|
			}
 | 
						|
		}
 | 
						|
		free_auto_def(a2);
 | 
						|
	}
 | 
						|
	top_ls->ls_current->st_auto_list = a1;
 | 
						|
}
 | 
						|
 | 
						|
check_args_used()
 | 
						|
{
 | 
						|
	register struct stack_entry *se = local_level->sl_entry;
 | 
						|
 | 
						|
	ASSERT(level == L_FORMAL1);
 | 
						|
	while (se) {
 | 
						|
		register struct def *def = se->se_idf->id_def;
 | 
						|
 | 
						|
		if (	(def && !def->df_used)
 | 
						|
		&&	!(f_ARGSUSED || LINTLIB)
 | 
						|
		) {
 | 
						|
			def_warning(def, "argument %s not used in function %s",
 | 
						|
					se->se_idf->id_text, func_name);
 | 
						|
		}
 | 
						|
		se = se->next;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
struct auto_def *
 | 
						|
copy_st_auto_list(from_al, lvl)
 | 
						|
	struct auto_def *from_al;
 | 
						|
{
 | 
						|
	struct auto_def *start = 0;
 | 
						|
	register struct auto_def **hook = &start;
 | 
						|
 | 
						|
	while (from_al && from_al->ad_def->df_level > lvl) {
 | 
						|
		from_al = from_al->next;
 | 
						|
	}
 | 
						|
	while (from_al) {
 | 
						|
		register struct auto_def *a = new_auto_def();
 | 
						|
 | 
						|
		*hook = a;
 | 
						|
		*a = *from_al;
 | 
						|
		hook = &a->next;
 | 
						|
		from_al = from_al->next;
 | 
						|
	}
 | 
						|
 | 
						|
	return start;
 | 
						|
}
 | 
						|
 | 
						|
free_st_auto_list(au)
 | 
						|
	register struct auto_def *au;
 | 
						|
{
 | 
						|
	register struct auto_def *a;
 | 
						|
 | 
						|
	while (au) {
 | 
						|
		a = au;
 | 
						|
		au = au->next;
 | 
						|
		free_auto_def(a);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
struct state *
 | 
						|
copy_state(from_st, lvl)
 | 
						|
	struct state *from_st;
 | 
						|
{
 | 
						|
/* Memory for the struct state and the struct auto_defs is allocated
 | 
						|
 * by this function
 | 
						|
 */
 | 
						|
	register struct state *st = new_state();
 | 
						|
 | 
						|
	st->st_auto_list = copy_st_auto_list(from_st->st_auto_list, lvl);
 | 
						|
	st->st_notreached = from_st->st_notreached;
 | 
						|
	st->st_warned = from_st->st_warned;
 | 
						|
	return st;
 | 
						|
}
 | 
						|
 | 
						|
static
 | 
						|
Free_state(stp)
 | 
						|
	struct state **stp;
 | 
						|
{
 | 
						|
/* This function also frees the list of auto_defs
 | 
						|
 */
 | 
						|
	free_st_auto_list((*stp)->st_auto_list);
 | 
						|
	free_state(*stp);
 | 
						|
	*stp = 0;
 | 
						|
}
 | 
						|
 | 
						|
remove_settings(state, lvl)
 | 
						|
	struct state *state;
 | 
						|
{
 | 
						|
/* The state of all variables on this level are set to 'not set' and
 | 
						|
 * 'not maybe set'. (I think you have to read this twice.)
 | 
						|
 */
 | 
						|
	register struct auto_def *a = state->st_auto_list;
 | 
						|
 | 
						|
	while (a && a->ad_def->df_level == lvl) {
 | 
						|
		a->ad_set = a->ad_maybe_set = 0;
 | 
						|
		a = a->next;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/******** M E R G E ********/
 | 
						|
 | 
						|
/* modes for merging */
 | 
						|
#define	NORMAL		0
 | 
						|
#define	CASE_BREAK	1
 | 
						|
#define	USE_ONLY	2
 | 
						|
 | 
						|
struct auto_def *
 | 
						|
merge_autos(a1, a2, lvl, mode)
 | 
						|
	struct auto_def *a1, *a2;
 | 
						|
	int mode;
 | 
						|
{
 | 
						|
/* Returns a pointer to the result.
 | 
						|
 * a1 is left unchanged.
 | 
						|
 * a2 is used to create this result.
 | 
						|
 * The fields are set as follows:
 | 
						|
 *	a1_set + a2_set		-> set
 | 
						|
 *		+ a?_maybe_set	-> maybe set
 | 
						|
 *		ELSE		-> NOT set && NOT maybe set
 | 
						|
 *	*	+ a?_used	-> used
 | 
						|
 *
 | 
						|
 * For mode == CASE_BREAK:
 | 
						|
 * First a2 is taken as the result, then
 | 
						|
 * variables NOT set in a2 and set or maybe set in a1 become 'maybe set'
 | 
						|
 *
 | 
						|
 * For mode == USE_ONLY:
 | 
						|
 * Start with a2 as the result.
 | 
						|
 * Variables used in a1 become used in a2.
 | 
						|
 * The rest of the result is not changed.
 | 
						|
 */
 | 
						|
	register struct auto_def *a;
 | 
						|
 | 
						|
	while (a1 && a1->ad_def->df_level > lvl) {
 | 
						|
		a1 = a1->next;
 | 
						|
	}
 | 
						|
	while (a2 && a2->ad_def->df_level > lvl) {
 | 
						|
		a = a2;
 | 
						|
		a2 = a2->next;
 | 
						|
		free_auto_def(a);
 | 
						|
	}
 | 
						|
	a = a2;	/* pointer to the result */
 | 
						|
	while (a1) {
 | 
						|
		ASSERT(a2);
 | 
						|
		ASSERT(a1->ad_idf == a2->ad_idf);
 | 
						|
		if (a1->ad_used)
 | 
						|
			a2->ad_used = 1;
 | 
						|
 | 
						|
		if (mode != USE_ONLY) {
 | 
						|
			if (	(	!a2->ad_set
 | 
						|
				&&	(a1->ad_set || a1->ad_maybe_set)
 | 
						|
				)
 | 
						|
			||	(	mode == NORMAL
 | 
						|
				&&	!a1->ad_set
 | 
						|
				&&	(a2->ad_set || a2->ad_maybe_set)
 | 
						|
				)
 | 
						|
			) {
 | 
						|
				a2->ad_set = 0;
 | 
						|
				a2->ad_maybe_set = 1;
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		a1 = a1->next;
 | 
						|
		a2 = a2->next;
 | 
						|
	}
 | 
						|
	ASSERT(!a2);
 | 
						|
	return a;
 | 
						|
}
 | 
						|
 | 
						|
merge_states(st1, st2, lvl, mode)
 | 
						|
	struct state *st1, *st2;
 | 
						|
	int mode;
 | 
						|
{
 | 
						|
/* st2 becomes the result.
 | 
						|
 * st1 is left unchanged.
 | 
						|
 * The resulting state is the state the program gets in if st1 OR st2
 | 
						|
 * becomes the state. (E.g. the states at the end of an if-part and an
 | 
						|
 * end-part are merged by this function.)
 | 
						|
 */
 | 
						|
	if (st1->st_notreached) {
 | 
						|
		if (mode == NORMAL || st2->st_notreached) {
 | 
						|
			st2->st_auto_list =
 | 
						|
				merge_autos(st1->st_auto_list,
 | 
						|
					st2->st_auto_list, lvl, USE_ONLY);
 | 
						|
		}
 | 
						|
	}
 | 
						|
	else if (st2->st_notreached) {
 | 
						|
		register struct auto_def *tmp = st2->st_auto_list;
 | 
						|
 | 
						|
		st2->st_auto_list = copy_st_auto_list(st1->st_auto_list, lvl);
 | 
						|
		st2->st_notreached = 0;
 | 
						|
		st2->st_warned = 0;
 | 
						|
		st2->st_auto_list = merge_autos(tmp, st2->st_auto_list,
 | 
						|
							lvl, USE_ONLY);
 | 
						|
		free_st_auto_list(tmp);
 | 
						|
	}
 | 
						|
	else {
 | 
						|
		st2->st_auto_list =
 | 
						|
			merge_autos(st1->st_auto_list, st2->st_auto_list,
 | 
						|
				lvl, mode);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/******** L I N T   S T A C K   S E A R C H I N G ********/
 | 
						|
 | 
						|
/* The next four find-functions search the lint_stack for an entry.
 | 
						|
 * The letters mean : w: WHILE; d: DO; f: FOR; s: SWITCH; c: CASE.
 | 
						|
 */
 | 
						|
 | 
						|
struct lint_stack_entry *
 | 
						|
find_wdf()
 | 
						|
{
 | 
						|
	register struct lint_stack_entry *lse = top_ls;
 | 
						|
 | 
						|
	while (lse != &stack_bottom) {
 | 
						|
		switch (lse->ls_class) {
 | 
						|
		case WHILE:
 | 
						|
		case DO:
 | 
						|
		case FOR:
 | 
						|
			return lse;
 | 
						|
		}
 | 
						|
		lse = lse->ls_previous;
 | 
						|
	}
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
struct lint_stack_entry *
 | 
						|
find_wdfc()
 | 
						|
{
 | 
						|
	register struct lint_stack_entry *lse = top_ls;
 | 
						|
 | 
						|
	while (lse != &stack_bottom) {
 | 
						|
		switch (lse->ls_class) {
 | 
						|
		case WHILE:
 | 
						|
		case DO:
 | 
						|
		case FOR:
 | 
						|
		case CASE:
 | 
						|
			return lse;
 | 
						|
		}
 | 
						|
		lse = lse->ls_previous;
 | 
						|
	}
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
struct lint_stack_entry *
 | 
						|
find_cs()
 | 
						|
{
 | 
						|
	register struct lint_stack_entry *lse = top_ls;
 | 
						|
 | 
						|
	while (lse != &stack_bottom) {
 | 
						|
		switch (lse->ls_class) {
 | 
						|
		case CASE:
 | 
						|
		case SWITCH:
 | 
						|
			return lse;
 | 
						|
		}
 | 
						|
		lse = lse->ls_previous;
 | 
						|
	}
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
/******** A C T I O N S ********/
 | 
						|
 | 
						|
start_if_part(const)
 | 
						|
{
 | 
						|
/* Push a new stack entry on the lint_stack with class == IF
 | 
						|
 * copy the ls_current to the top of this stack
 | 
						|
 */
 | 
						|
	register struct lint_stack_entry *lse = new_lint_stack_entry();
 | 
						|
 | 
						|
	if (const)
 | 
						|
		hwarning("condition in if statement is constant");
 | 
						|
 | 
						|
	lse->ls_class = IF;
 | 
						|
	lse->ls_current = copy_state(top_ls->ls_current, level);
 | 
						|
	lse->ls_level = level;
 | 
						|
	lint_push(lse);
 | 
						|
}
 | 
						|
 | 
						|
start_else_part()
 | 
						|
{
 | 
						|
/* Move ls_current to LS_IF_STATE
 | 
						|
 * ls_current of the stack entry one below is copied to ls_current.
 | 
						|
 */
 | 
						|
	if (s_NOTREACHED) {
 | 
						|
		top_ls->ls_current->st_notreached = 1;
 | 
						|
		s_NOTREACHED = 0;
 | 
						|
	}
 | 
						|
	top_ls->LS_IF_STATE = top_ls->ls_current;
 | 
						|
	/* this is the reason why ls_current is a pointer */
 | 
						|
	top_ls->ls_current = copy_state(top_ls->ls_previous->ls_current,
 | 
						|
								level);
 | 
						|
	top_ls->ls_level = level;
 | 
						|
}
 | 
						|
 | 
						|
end_if_else_stmt()
 | 
						|
{
 | 
						|
	Free_state(&top_ls->ls_previous->ls_current);
 | 
						|
	merge_states(top_ls->LS_IF_STATE, top_ls->ls_current,
 | 
						|
					top_ls->ls_level, NORMAL);
 | 
						|
	Free_state(&top_ls->LS_IF_STATE);
 | 
						|
	top_ls->ls_previous->ls_current = top_ls->ls_current;
 | 
						|
	lint_pop();
 | 
						|
}
 | 
						|
 | 
						|
end_if_stmt()
 | 
						|
{
 | 
						|
/* No else-part met; merge ls_current with ls_current of previous
 | 
						|
 * stack entry
 | 
						|
 */
 | 
						|
	merge_states(top_ls->ls_current, top_ls->ls_previous->ls_current,
 | 
						|
						top_ls->ls_level, NORMAL);
 | 
						|
	Free_state(&top_ls->ls_current);
 | 
						|
	lint_pop();
 | 
						|
}
 | 
						|
 | 
						|
start_loop_stmt(looptype, const, cond)
 | 
						|
{
 | 
						|
/* If const, the condition is constant and given in cond */
 | 
						|
	register struct lint_stack_entry *lse = new_lint_stack_entry();
 | 
						|
 | 
						|
	lse->ls_class = looptype;
 | 
						|
	lse->ls_current = copy_state(top_ls->ls_current, level);
 | 
						|
	lse->ls_level = level;
 | 
						|
	if (const && !cond) {
 | 
						|
		/* while (0) | for (;0;) */
 | 
						|
		hwarning("condition in %s statement is constant",
 | 
						|
						symbol2str(looptype));
 | 
						|
		lse->ls_current->st_notreached = 1;
 | 
						|
	}
 | 
						|
	if (const && cond) {
 | 
						|
		/* while (1) | for (;;) | do */
 | 
						|
		/*	omitting the copy for LS_END will force this loop
 | 
						|
			to be treated as a do loop
 | 
						|
		*/
 | 
						|
		top_ls->ls_current->st_notreached = 1;
 | 
						|
	}
 | 
						|
	else {
 | 
						|
		lse->LS_END = copy_state(top_ls->ls_current, level);
 | 
						|
	}
 | 
						|
	lint_push(lse);
 | 
						|
}
 | 
						|
 | 
						|
end_loop_stmt()
 | 
						|
{
 | 
						|
	register struct lint_stack_entry *prev_ls = top_ls->ls_previous;
 | 
						|
 | 
						|
	lint_continue_stmt();
 | 
						|
	top_ls->LS_END->st_notreached = prev_ls->ls_current->st_notreached;
 | 
						|
	top_ls->LS_END->st_warned = prev_ls->ls_current->st_warned;
 | 
						|
	Free_state(&top_ls->ls_current);
 | 
						|
	Free_state(&prev_ls->ls_current);
 | 
						|
	prev_ls->ls_current = top_ls->LS_END;
 | 
						|
	lint_pop();
 | 
						|
}
 | 
						|
 | 
						|
end_do_stmt(const, cond)
 | 
						|
{
 | 
						|
	end_loop_stmt();
 | 
						|
	if (const)
 | 
						|
		hwarning("condition in do-while statement is constant");
 | 
						|
	if (const && cond && top_ls->ls_current->st_notreached) {
 | 
						|
		/* no break met; this is really an endless loop */
 | 
						|
	}
 | 
						|
	else {
 | 
						|
		top_ls->ls_current->st_notreached = 0;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
cont_break_merge(lse)
 | 
						|
	struct lint_stack_entry *lse;
 | 
						|
{
 | 
						|
	/* merge for continue and break statements */
 | 
						|
	if (lse->LS_END) {
 | 
						|
		merge_states(top_ls->ls_current, lse->LS_END,
 | 
						|
						lse->ls_level, NORMAL);
 | 
						|
	}
 | 
						|
	else {
 | 
						|
		lse->LS_END = copy_state(top_ls->ls_current, lse->ls_level);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
lint_continue_stmt()
 | 
						|
{
 | 
						|
	register struct lint_stack_entry *lse = find_wdf();
 | 
						|
 | 
						|
	if (!lse)
 | 
						|
		return;		/* not inside a loop statement */
 | 
						|
 | 
						|
	cont_break_merge(lse);
 | 
						|
	top_ls->ls_current->st_notreached = 1;
 | 
						|
}
 | 
						|
 | 
						|
start_switch_part(expr)
 | 
						|
	struct expr *expr;
 | 
						|
{
 | 
						|
/* ls_current of a SWITCH entry has different meaning from ls_current of
 | 
						|
 * other entries. It keeps track of which variables are used in all
 | 
						|
 * following case parts. (Needed for variables declared in a compound
 | 
						|
 * switch-block.)
 | 
						|
 */
 | 
						|
	register struct lint_stack_entry *lse = new_lint_stack_entry();
 | 
						|
 | 
						|
	if (is_cp_cst(expr))
 | 
						|
		hwarning("value in switch statement is constant");
 | 
						|
 | 
						|
	lse->ls_class = SWITCH;
 | 
						|
	lse->ls_current = copy_state(top_ls->ls_current, level);
 | 
						|
	lse->ls_level = level;
 | 
						|
	lse->LS_CASE = copy_state(top_ls->ls_current, level);
 | 
						|
	lse->ls_current->st_notreached = 1;
 | 
						|
	top_ls->ls_current->st_notreached = 1;
 | 
						|
	lint_push(lse);
 | 
						|
}
 | 
						|
 | 
						|
end_switch_stmt()
 | 
						|
{
 | 
						|
	if (top_ls->ls_class == CASE) {
 | 
						|
		/* no break after last case or default */
 | 
						|
		lint_break_stmt();	/* introduce break */
 | 
						|
	}
 | 
						|
 | 
						|
	if (!top_ls->LS_DEFAULT_MET) {
 | 
						|
		top_ls->ls_current->st_notreached = 0;
 | 
						|
		if (top_ls->LS_BREAK) {
 | 
						|
			merge_states(top_ls->ls_current, top_ls->LS_BREAK,
 | 
						|
						top_ls->ls_level, NORMAL);
 | 
						|
			Free_state(&top_ls->ls_current);
 | 
						|
		}
 | 
						|
		else {
 | 
						|
			top_ls->LS_BREAK = top_ls->ls_current;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	else {
 | 
						|
		Free_state(&top_ls->ls_current);
 | 
						|
	}
 | 
						|
 | 
						|
	if (top_ls->LS_BREAK) {
 | 
						|
		merge_states(top_ls->LS_CASE, top_ls->LS_BREAK,
 | 
						|
						top_ls->ls_level, CASE_BREAK);
 | 
						|
		Free_state(&top_ls->LS_CASE);
 | 
						|
	}
 | 
						|
	else {
 | 
						|
		top_ls->LS_BREAK = top_ls->LS_CASE;
 | 
						|
	}
 | 
						|
 | 
						|
	top_ls->LS_BREAK->st_notreached =
 | 
						|
			top_ls->ls_previous->ls_current->st_notreached;
 | 
						|
				/* yack */
 | 
						|
	Free_state(&top_ls->ls_previous->ls_current);
 | 
						|
 | 
						|
	if (!top_ls->LS_DEFAULT_MET)
 | 
						|
		top_ls->LS_BREAK->st_notreached = 0;
 | 
						|
	top_ls->ls_previous->ls_current = top_ls->LS_BREAK;
 | 
						|
 | 
						|
	lint_pop();
 | 
						|
}
 | 
						|
 | 
						|
lint_case_stmt(dflt)
 | 
						|
{
 | 
						|
/* A default statement is just a special case statement */
 | 
						|
 | 
						|
	register struct lint_stack_entry *lse;
 | 
						|
	register struct lint_stack_entry *cs_entry = find_cs();
 | 
						|
 | 
						|
	if (!cs_entry)
 | 
						|
		return;		/* not inside switch */
 | 
						|
	if (cs_entry != top_ls) {
 | 
						|
		warning("%s statement in strange context",
 | 
						|
			dflt ? "default" : "case");
 | 
						|
		return;
 | 
						|
	}
 | 
						|
	if (cs_entry->ls_class == SWITCH) {
 | 
						|
		if (dflt) {
 | 
						|
			cs_entry->LS_DEFAULT_MET = 1;
 | 
						|
		}
 | 
						|
		lse = new_lint_stack_entry();
 | 
						|
		lse->ls_class = CASE;
 | 
						|
		lse->ls_current = copy_state(top_ls->ls_current, level);
 | 
						|
		remove_settings(lse->ls_current, level);
 | 
						|
		lse->ls_level = level;
 | 
						|
		lint_push(lse);
 | 
						|
	}
 | 
						|
	else {
 | 
						|
		ASSERT(cs_entry->ls_class == CASE);
 | 
						|
		if (dflt) {
 | 
						|
			cs_entry->ls_previous->LS_DEFAULT_MET = 1;
 | 
						|
		}
 | 
						|
		merge_states(top_ls->ls_current, top_ls->ls_previous->LS_CASE,
 | 
						|
				top_ls->ls_previous->ls_level, NORMAL);
 | 
						|
		merge_states(top_ls->ls_current,
 | 
						|
				top_ls->ls_previous->ls_current,
 | 
						|
				top_ls->ls_previous->ls_level, NORMAL);
 | 
						|
		Free_state(&top_ls->ls_current);
 | 
						|
		top_ls->ls_current =
 | 
						|
			copy_state(top_ls->ls_previous->ls_current,
 | 
						|
					top_ls->ls_previous->ls_level);
 | 
						|
		remove_settings(top_ls->ls_current, top_ls->ls_level);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
lint_break_stmt()
 | 
						|
{
 | 
						|
	register struct lint_stack_entry *lse = find_wdfc();
 | 
						|
 | 
						|
	if (!lse)
 | 
						|
		return;
 | 
						|
 | 
						|
	switch (lse->ls_class) {
 | 
						|
	case WHILE:
 | 
						|
	case FOR:
 | 
						|
	case DO:
 | 
						|
		/* loop break */
 | 
						|
		lse->ls_previous->ls_current->st_notreached = 0;
 | 
						|
		cont_break_merge(lse);
 | 
						|
		break;
 | 
						|
 | 
						|
	case CASE:
 | 
						|
		/* case break */
 | 
						|
		if (!top_ls->ls_current->st_notreached) {
 | 
						|
			lse->ls_previous->ls_previous->ls_current->st_notreached = 0;
 | 
						|
		}
 | 
						|
		merge_states(lse->ls_current, lse->ls_previous->ls_current,
 | 
						|
					lse->ls_previous->ls_level, NORMAL);
 | 
						|
		if (lse->ls_previous->LS_BREAK) {
 | 
						|
			merge_states(top_ls->ls_current, lse->ls_previous->LS_BREAK,
 | 
						|
					lse->ls_previous->ls_level, NORMAL);
 | 
						|
		}
 | 
						|
		else {
 | 
						|
			lse->ls_previous->LS_BREAK = copy_state(top_ls->ls_current,
 | 
						|
						 lse->ls_previous->ls_level);
 | 
						|
		}
 | 
						|
		if (lse == top_ls) {
 | 
						|
			Free_state(&lse->ls_current);
 | 
						|
			lint_pop();
 | 
						|
		}
 | 
						|
		break;
 | 
						|
	default:
 | 
						|
		NOTREACHED();
 | 
						|
		/*NOTREACHED*/
 | 
						|
	}
 | 
						|
	top_ls->ls_current->st_notreached = 1;
 | 
						|
}
 | 
						|
 | 
						|
lint_start_function()
 | 
						|
{
 | 
						|
	lint_return_stmt(-1);	/* initialization */
 | 
						|
	lint_comment_function();
 | 
						|
}
 | 
						|
 | 
						|
lint_end_function()
 | 
						|
{
 | 
						|
	extern struct outdef OutDef;
 | 
						|
	register int fund = func_type->tp_fund;
 | 
						|
 | 
						|
	if (	OutDef.od_valreturned == NOVALRETURNED
 | 
						|
	&&	!func_notypegiven
 | 
						|
	&&	fund != VOID
 | 
						|
	) {
 | 
						|
		warning("function %s declared %s%s but no value returned",
 | 
						|
			func_name,
 | 
						|
			(func_type->tp_unsigned && fund != POINTER) ?
 | 
						|
				"unsigned " : "",
 | 
						|
			 symbol2str(fund)
 | 
						|
		);
 | 
						|
	}
 | 
						|
	/* write the function definition record */
 | 
						|
	outdef();
 | 
						|
 | 
						|
	/* At this stage it is possible that stack_bottom.ls_current is
 | 
						|
	 * pointing to a state with a list of auto_defs.
 | 
						|
	 * These auto_defs must be freed and the state must be filled
 | 
						|
	 * with zeros.
 | 
						|
	 */
 | 
						|
	ASSERT(top_ls == &stack_bottom);
 | 
						|
	if (top_ls->ls_current->st_auto_list != 0)
 | 
						|
		free_st_auto_list(top_ls->ls_current->st_auto_list);
 | 
						|
	top_ls->ls_current->st_auto_list = 0;
 | 
						|
	top_ls->ls_current->st_notreached = 0;
 | 
						|
	top_ls->ls_current->st_warned = 0;
 | 
						|
}
 | 
						|
 | 
						|
lint_return_stmt(e)
 | 
						|
	int e;
 | 
						|
{
 | 
						|
/* The statics of this function are initialized by calling it with e = -1. */
 | 
						|
 | 
						|
	static int ret_e;
 | 
						|
				/*-1	no return met yet
 | 
						|
				 * 0	return; met
 | 
						|
				 * 1	return with expression met
 | 
						|
				 */
 | 
						|
	static int warned;
 | 
						|
 | 
						|
	switch (e) {
 | 
						|
	case -1:
 | 
						|
		ret_e = -1;
 | 
						|
		warned = 0;
 | 
						|
		return;
 | 
						|
	case 0:
 | 
						|
		if (top_ls->ls_current->st_notreached)
 | 
						|
			break;
 | 
						|
		if (ret_e == 1 && !warned) {
 | 
						|
			warning("function %s does not always return a value",
 | 
						|
				func_name);
 | 
						|
			warned = 1;
 | 
						|
		}
 | 
						|
		else
 | 
						|
			ret_e = 0;
 | 
						|
		break;
 | 
						|
	case 1:
 | 
						|
		if (top_ls->ls_current->st_notreached)
 | 
						|
			break;
 | 
						|
		if (ret_e == 0 && !warned) {
 | 
						|
			warning("function %s does not always return a value",
 | 
						|
				func_name);
 | 
						|
			warned = 1;
 | 
						|
		}
 | 
						|
		else
 | 
						|
			ret_e = 1;
 | 
						|
		break;
 | 
						|
	}
 | 
						|
	if (!top_ls->ls_current->st_notreached)
 | 
						|
		set_od_valreturned(e);
 | 
						|
	top_ls->ls_current->st_notreached = 1;
 | 
						|
}
 | 
						|
 | 
						|
lint_jump_stmt(idf)
 | 
						|
	struct idf *idf;
 | 
						|
{
 | 
						|
	top_ls->ls_current->st_notreached = 1;
 | 
						|
	if (!idf->id_def)
 | 
						|
		return;
 | 
						|
	idf->id_def->df_used = 1;
 | 
						|
}
 | 
						|
 | 
						|
lint_label()
 | 
						|
{
 | 
						|
/*	When meeting a label, we should take the intersection of all
 | 
						|
	settings at all goto's leading this way, but this cannot reasonably
 | 
						|
	be done.  So we assume that the user knows what he is doing and set
 | 
						|
	all automatic variables to set.
 | 
						|
*/
 | 
						|
	register struct auto_def *a = top_ls->ls_current->st_auto_list;
 | 
						|
 | 
						|
	while (a) {
 | 
						|
		a->ad_maybe_set = 0;
 | 
						|
		a->ad_set = 1;
 | 
						|
		a = a->next;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
lint_statement()
 | 
						|
{
 | 
						|
/* Check if this statement can be reached
 | 
						|
 */
 | 
						|
	if (s_NOTREACHED) {
 | 
						|
		top_ls->ls_current->st_notreached = 1;
 | 
						|
		s_NOTREACHED = 0;
 | 
						|
	}
 | 
						|
	if (DOT == '{' || DOT == ';')
 | 
						|
		return;
 | 
						|
	if (top_ls->ls_current->st_warned)
 | 
						|
		return;
 | 
						|
	if (top_ls->ls_current->st_notreached) {
 | 
						|
		if (DOT != CASE && DOT != DEFAULT && AHEAD != ':') {
 | 
						|
			if (DOT != BREAK || !loptions['b'])
 | 
						|
				warning("statement cannot be reached");
 | 
						|
			top_ls->ls_current->st_warned = 1;
 | 
						|
		}
 | 
						|
		else {
 | 
						|
			top_ls->ls_current->st_notreached = 0;
 | 
						|
			top_ls->ls_current->st_warned = 0;
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
lint_push(lse)
 | 
						|
	struct lint_stack_entry *lse;
 | 
						|
{
 | 
						|
	lse->ls_previous = top_ls;
 | 
						|
	top_ls->next = lse;
 | 
						|
	top_ls = lse;
 | 
						|
}
 | 
						|
 | 
						|
lint_pop()
 | 
						|
{
 | 
						|
	top_ls = top_ls->ls_previous;
 | 
						|
	free_lint_stack_entry(top_ls->next);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* FOR DEBUGGING */
 | 
						|
 | 
						|
print_lint_stack()
 | 
						|
{
 | 
						|
	register struct lint_stack_entry *lse = top_ls;
 | 
						|
 | 
						|
	while (lse) {
 | 
						|
		print("  |-------------- level %d ------------\n",
 | 
						|
					lse->ls_level);
 | 
						|
		print("  |cur: ");
 | 
						|
		if (lse->ls_current) {
 | 
						|
			print_autos(lse->ls_current->st_auto_list);
 | 
						|
			print("  |st_notreached == %d\n",
 | 
						|
				lse->ls_current->st_notreached);
 | 
						|
		}
 | 
						|
		else
 | 
						|
			print("\n");
 | 
						|
		print("  |class == %s\n",
 | 
						|
			lse->ls_class ? symbol2str(lse->ls_class) : "{");
 | 
						|
		switch (lse->ls_class) {
 | 
						|
		case SWITCH:
 | 
						|
			print("  |LS_BREAK: ");
 | 
						|
			if (lse->LS_BREAK) {
 | 
						|
				print_autos(lse->LS_BREAK->st_auto_list);
 | 
						|
				print("  |st_notreached == %d\n",
 | 
						|
					lse->LS_BREAK->st_notreached);
 | 
						|
			}
 | 
						|
			else
 | 
						|
				print("\n");
 | 
						|
			print("  |LS_CASE:  ");
 | 
						|
			if (lse->LS_CASE) {
 | 
						|
				print_autos(lse->LS_CASE->st_auto_list);
 | 
						|
				print("  |st_notreached == %d\n",
 | 
						|
					lse->LS_CASE->st_notreached);
 | 
						|
			}
 | 
						|
			else
 | 
						|
				print("\n");
 | 
						|
			break;
 | 
						|
		case DO:
 | 
						|
		case WHILE:
 | 
						|
		case FOR:
 | 
						|
			print("  |LS_END:  ");
 | 
						|
			if (lse->LS_END) {
 | 
						|
				print_autos(lse->LS_END->st_auto_list);
 | 
						|
				print("  |st_notreached == %d\n",
 | 
						|
					lse->LS_END->st_notreached);
 | 
						|
			}
 | 
						|
			else
 | 
						|
				print("\n");
 | 
						|
			break;
 | 
						|
		case IF:
 | 
						|
			print("  |LS_IF_STATE: ");
 | 
						|
			if (lse->LS_IF_STATE) {
 | 
						|
				print_autos(lse->LS_IF_STATE->st_auto_list);
 | 
						|
				print("  |st_notreached == %d\n",
 | 
						|
					lse->LS_IF_STATE->st_notreached);
 | 
						|
			}
 | 
						|
			else
 | 
						|
				print("\n");
 | 
						|
			break;
 | 
						|
		default:
 | 
						|
			break;
 | 
						|
		}
 | 
						|
		lse = lse->ls_previous;
 | 
						|
	}
 | 
						|
	print("  |--------------\n\n");
 | 
						|
}
 | 
						|
 | 
						|
static
 | 
						|
print_autos(a)
 | 
						|
	register struct auto_def *a;
 | 
						|
{
 | 
						|
	while (a) {
 | 
						|
		print("%s", a->ad_idf->id_text);
 | 
						|
		print("(l=%d)", a->ad_def->df_level);
 | 
						|
		print("(U%dS%dM%d) ", a->ad_used, a->ad_set, a->ad_maybe_set);
 | 
						|
		a = a->next;
 | 
						|
	}
 | 
						|
	print("\n");
 | 
						|
}
 | 
						|
#endif	LINT
 |