|
@@ -0,0 +1,1102 @@
|
|
|
+/*
|
|
|
+ * (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_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[];
|
|
|
+extern int s_NOTREACHED;
|
|
|
+
|
|
|
+/* 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_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;
|
|
|
+
|
|
|
+ if (a1 && a1->ad_def->df_level > level) {
|
|
|
+ crash("(check_autos) corrupt level in st_auto_list");
|
|
|
+ /*NOTREACHED*/
|
|
|
+ }
|
|
|
+ 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;
|
|
|
+ extern int f_ARGSUSED;
|
|
|
+
|
|
|
+ if (level != L_FORMAL1) {
|
|
|
+ crash("(check_args_used) invalid level %d", level);
|
|
|
+ /*NOTREACHED*/
|
|
|
+ }
|
|
|
+ while (se) {
|
|
|
+ register struct def *def = se->se_idf->id_def;
|
|
|
+
|
|
|
+ if (def && !def->df_used && !loptions['v'] && !f_ARGSUSED) {
|
|
|
+ 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) {
|
|
|
+ if (!a2) {
|
|
|
+ crash("(merge_autos) a1 longer than a2");
|
|
|
+ /*NOTREACHED*/
|
|
|
+ }
|
|
|
+ if (a1->ad_idf != a2->ad_idf) {
|
|
|
+ crash("(merge_autos) identifiers should be the same %s %s",
|
|
|
+ a1->ad_idf->id_text, a2->ad_idf->id_text);
|
|
|
+ /*NOTREACHED*/
|
|
|
+ }
|
|
|
+
|
|
|
+ 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;
|
|
|
+ }
|
|
|
+ if (a2) {
|
|
|
+ crash("(merge_autos) a2 longer than a1");
|
|
|
+ /*NOTREACHED*/
|
|
|
+ }
|
|
|
+ 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()
|
|
|
+{
|
|
|
+/* 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();
|
|
|
+
|
|
|
+ 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("constant do condition");
|
|
|
+ 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()
|
|
|
+{
|
|
|
+/* 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();
|
|
|
+
|
|
|
+ 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:
|
|
|
+ /* bad break */
|
|
|
+ crash("find_wdfc() returned invalid entry");
|
|
|
+ /*NOTREACHED*/
|
|
|
+ }
|
|
|
+ top_ls->ls_current->st_notreached = 1;
|
|
|
+}
|
|
|
+
|
|
|
+lint_start_function()
|
|
|
+{
|
|
|
+ lint_return_stmt(-1); /* initialization */
|
|
|
+ move_ARG2f();
|
|
|
+ move_VAR2f();
|
|
|
+}
|
|
|
+
|
|
|
+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.
|
|
|
+ */
|
|
|
+ if (top_ls != &stack_bottom) {
|
|
|
+ crash("(lint_end_function) top_ls != &stack_bottom");
|
|
|
+ /*NOTREACHED*/
|
|
|
+ }
|
|
|
+ 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;
|
|
|
+
|
|
|
+ hwarning("all auto variables assumed initialized at label");
|
|
|
+ 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
|