/* * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. * See the copyright notice in the ACK home directory, in the file "Copyright". */ /* $Id$ */ /* Lint outdef construction */ #include "lint.h" #ifdef LINT #include #include "interface.h" #ifdef ANSI #include #endif /* ANSI */ #include "arith.h" #include "assert.h" #include "type.h" #include "proto.h" #include "declar.h" #include "decspecs.h" #include "LLlex.h" #include "Lpars.h" #include "stack.h" #include "def.h" #include "struct.h" #include "field.h" #include "idf.h" #include "level.h" #include "label.h" #include "code.h" #include "expr.h" #include "l_lint.h" #include "l_comment.h" #include "l_outdef.h" #include "l_class.h" extern char *bts2str(); extern char *symbol2str(); extern char *strchr(); int stat_number = 9999; /* static scope number */ struct outdef OutDef; PRIVATE struct outdef OutCall; PRIVATE local_EFDC(); PRIVATE output_def(); PRIVATE outargs(); PRIVATE outarg(); PRIVATE outargstring(); PRIVATE outargtype(); PRIVATE add_expr_arg(); PRIVATE def2decl(); lint_declare_idf(idf, sc) struct idf *idf; int sc; { register struct def *def = idf->id_def; register int is_function = def->df_type->tp_fund == FUNCTION; if (level == L_GLOBAL) { lint_ext_def(idf, sc); if (is_function) def2decl(sc); if (sc != TYPEDEF) outdef(); } else if (level >= L_LOCAL && sc != STATIC && is_function) { local_EFDC(idf); } } lint_non_function_decl(ds, dc) struct decspecs *ds; struct declarator *dc; { register struct def *def = dc->dc_idf->id_def; register int is_function = def->df_type->tp_fund == FUNCTION; if (is_function) def2decl(ds->ds_sc); if (def->df_sc != TYPEDEF) outdef(); } lint_ext_def(idf, sc) struct idf *idf; { /* At this place the following fields of the output definition can be * filled: * od_name, od_statnr, od_class, od_file, od_line, od_type. * For variable definitions and declarations this will be all. * For functions the fields od_nrargs and od_arg are filled after parsing * the arguments. * The od_valreturned field is known at the end of the function definition. * sc indicates the storage class defined by the declaration specifier. */ register struct def *def = idf->id_def; register struct type *type = def->df_type; OutDef.od_name = idf->id_text; OutDef.od_statnr = (sc == STATIC ? stat_number : 0); switch (type->tp_fund) { case ERRONEOUS: OutDef.od_class = XXDF; break; case FUNCTION: /* For the moment assume it will be a definition. * If no compound_statement follows, it is a declaration, * in which case the class will be adjusted by def2decl(). */ OutDef.od_class = (sc == STATIC ? SFDF : EFDF); break; default: /* a variable */ OutDef.od_class = sc == EXTERN ? EVDC : sc == STATIC ? SVDF : EVDF; break; } OutDef.od_file = def->df_file; OutDef.od_line = def->df_line; OutDef.od_type = (type->tp_fund == FUNCTION ? type->tp_up : type); OutDef.od_valreturned = NORETURN; } PRIVATE def2decl(sc) int sc; { /* It was assumed we were parsing a function definition. * There was no compound statement following, so actually it was a * declaration. This function updates the class. */ OutDef.od_class = (sc == STATIC ? XXDF : EFDC); } set_od_valreturned(n) { OutDef.od_valreturned = n; } PRIVATE local_EFDC(idf) struct idf *idf; { struct outdef od; od.od_class = EFDC; od.od_statnr = 0; od.od_name = idf->id_text; od.od_file = idf->id_def->df_file; od.od_line = idf->id_def->df_line; od.od_type = idf->id_def->df_type->tp_up; output_def(&od); /* The other fields are not used for this class. */ } lint_formals() { /* Make a list of 'struct argument's containing the types of the formal * parameters of the function definition just parsed. */ register struct stack_entry *se = stack_level_of(L_FORMAL1)->sl_entry; register struct argument **hook = &OutDef.od_arg; register int nrargs = 0; while (se) { register struct type *type = se->se_idf->id_def->df_type; register struct argument *arg = new_argument(); if (f_FORMAT && nrargs == f_FORMATn) { if ( !f_FORMATvar && ( type->tp_fund != POINTER || type->tp_up->tp_fund != CHAR ) ) { warning("format parameter %d is not pointer to char", nrargs); } arg->ar_type = string_type; arg->ar_class = ArgString; arg->CAS_VALUE = f_FORMAT; arg->CAS_LEN = strlen(f_FORMAT); f_FORMAT = 0; } else { arg->ar_type = type; arg->ar_class = ArgFormal; } *hook = arg; hook = &arg->next; nrargs++; se = se->next; } if (f_FORMAT) { /* f_FORMAT has not been consumed, perhaps due to a varargs-like construction; add erroneous ArgFormals until f_FORMATn, then an ArgString, if necessary. */ if (!f_FORMATvar) { warning("FORMAT%d function has only %d argument%s", f_FORMATn, nrargs, nrargs == 1 ? "" : "s" ); } while (nrargs < f_FORMATn) { register struct argument *arg = new_argument(); arg->ar_type = error_type; arg->ar_class = ArgFormal; *hook = arg; hook = &arg->next; nrargs++; } if (nrargs == f_FORMATn) { register struct argument *arg = new_argument(); arg->ar_type = string_type; arg->ar_class = ArgString; arg->CAS_VALUE = f_FORMAT; arg->CAS_LEN = strlen(f_FORMAT); f_FORMAT = 0; *hook = arg; hook = &arg->next; nrargs++; } /* life is full of duplicated code; this is no good */ } if (f_VARARGSn > nrargs) { warning("VARARGS%d function has only %d argument%s", f_VARARGSn, nrargs, nrargs == 1 ? "" : "s" ); f_VARARGSn = nrargs; } OutDef.od_nrargs = nrargs; } output_proto(idf, def) struct idf *idf; struct def *def; { /* fund == FUNCTION && sc != STATIC */ register struct proto *pl = def->df_type->tp_proto; register int nrargs = 0; if (!pl) return; OutDef.od_name = idf->id_text; OutDef.od_statnr = 0; OutDef.od_class = PFDF; OutDef.od_file = def->df_file; OutDef.od_line = def->df_line; OutDef.od_type = def->df_type->tp_up; OutDef.od_valreturned = NORETURN;/*???*/ while (pl) { register struct type *type = pl->pl_type; register struct argument *arg = new_argument(); if (type) { arg->ar_type = type; arg->ar_class = ArgFormal; } else { arg->ar_class = ArgEllipsis; } arg->next = OutDef.od_arg; OutDef.od_arg = arg; nrargs++; pl = pl->next; } OutDef.od_nrargs = nrargs; outdef(); } output_use(idf) struct idf *idf; { /* Output the usage-definition of the variable described by idf. */ OutDef.od_name = idf->id_text; OutDef.od_statnr = (idf->id_def->df_sc == STATIC ? stat_number : 0); OutDef.od_class = VU; OutDef.od_file = FileName; OutDef.od_line = LineNumber; OutDef.od_type = idf->id_def->df_type; outdef(); } outdef() { output_def(&OutDef); } outcall() { output_def(&OutCall); } PRIVATE output_def(od) struct outdef *od; { /* As the types are output the 'struct argument's are freed, because they * are then not needed anymore. */ if (od->od_class == XXDF || !od->od_name || od->od_name[0] == '#') return; if (LINTLIB) { switch (od->od_class) { case EFDF: od->od_class = LFDF; break; case EVDF: od->od_class = LVDF; break; case SFDF: /* free the 'struct argument's */ while (od->od_arg) { register struct argument *tmp = od->od_arg; od->od_arg = od->od_arg->next; free_argument(tmp); } return; default: return; } } printf("%s:%d:%c", od->od_name, od->od_statnr, od->od_class); switch (od->od_class) { case LFDF: case PFDF: case EFDF: case SFDF: if (f_VARARGSn != -1) { printf(":%d", -1 - f_VARARGSn); outargs(od->od_arg, f_VARARGSn); } else { printf(":%d", od->od_nrargs); outargs(od->od_arg, od->od_nrargs); } od->od_arg = 0; printf(":%d", od->od_valreturned); break; case FC: printf(":%d", od->od_nrargs); outargs(od->od_arg, od->od_nrargs); od->od_arg = 0; printf(":%d", od->od_valused); break; case EVDF: case SVDF: case LVDF: case EFDC: case EVDC: case IFDC: case VU: break; default: NOTREACHED(); /*NOTREACHED*/ } printf(":"); outargtype(od->od_type); printf(":%u:%s\n", od->od_line, od->od_file); } PRIVATE outargs(arg, n) struct argument *arg; { /* Output the n arguments in the argument list and remove them */ register struct argument *tmp; while (n--) { ASSERT(arg); outarg(arg); tmp = arg; arg = arg->next; free_argument(tmp); } /* remove the remaining entries */ while (arg) { tmp = arg; arg = arg->next; free_argument(tmp); } } PRIVATE outarg(arg) struct argument *arg; { printf(":"); switch (arg->ar_class) { case ArgConst: if (arg->CAA_VALUE >= 0) { /* constant non-negative actual parameter */ printf("+"); } outargtype(arg->ar_type); break; case ArgString: outargstring(arg); break; case ArgFormal: case ArgExpr: outargtype(arg->ar_type); if (arg->ar_type->tp_fund == FUNCTION) { /* UGLY PATCH !!! ??? */ /* function names as operands are sometimes FUNCTION and sometimes POINTER to FUNCTION, depending on opaque circumstances. E.g., in f(main, main); the first main is PtF and the second is F. */ printf("*"); } break; case ArgEllipsis: printf("."); /* one is enough for computers */ break; default: NOTREACHED(); /*NOTREACHED*/ } } PRIVATE outargstring(arg) struct argument *arg; { char buff[1000]; register char *p; bts2str(arg->CAS_VALUE, arg->CAS_LEN, buff); for (p = &buff[0]; *p; p++) { if (*p == '"' || *p == ':') *p = ' '; } printf("\"%s\"", buff); } PRIVATE outargtype(tp) struct type *tp; { switch (tp->tp_fund) { case POINTER: outargtype(tp->tp_up); printf("*"); break; case ARRAY: outargtype(tp->tp_up); printf("*"); /* compatible with [] */ break; case FUNCTION: outargtype(tp->tp_up); printf("()"); break; case STRUCT: case UNION: case ENUM: /* watch out for anonymous identifiers; the count field does not have to be the same for all compilation units. Remove it, so that pass 2 does not see it. The only problem with this is that pass2 will not see a difference between two non-tagged types declared on the same line. */ printf("%s ", symbol2str(tp->tp_fund)); if (is_anon_idf(tp->tp_idf)) { /* skip the #, replace it by '#anonymous id' */ printf("#anonymous id%s", strchr(tp->tp_idf->id_text, ' ') ); } else { printf(tp->tp_idf->id_text); } break; case CHAR: case INT: case SHORT: case LONG: case ULONG: case FLOAT: case DOUBLE: case LNGDBL: case VOID: case ERRONEOUS: if (tp->tp_unsigned) { printf("unsigned "); } printf("%s", symbol2str(tp->tp_fund)); break; default: NOTREACHED(); /*NOTREACHED*/ } } #ifdef IMPLICIT PRIVATE implicit_func_decl(idf, file, line) struct idf *idf; char *file; unsigned int line; { struct outdef od; od.od_class = IFDC; od.od_statnr = 0; od.od_name = idf->id_text; od.od_file = file; od.od_line = line; od.od_type = idf->id_def->df_type->tp_up; output_def(&od); /* The other fields are not used for this class. */ } #endif /* IMPLICIT */ fill_outcall(ex, used) struct expr *ex; int used; { register struct idf *idf = ex->OP_LEFT->VL_IDF; register struct def *def = idf->id_def; #ifdef IMPLICIT if (def->df_sc == IMPLICIT && !idf->id_def->df_used) { /* IFDC, first time */ implicit_func_decl(idf, ex->ex_file, ex->ex_line); } #endif /* IMPLICIT */ OutCall.od_type = def->df_type->tp_up; OutCall.od_statnr = (def->df_sc == STATIC ? stat_number : 0); OutCall.od_class = FC; OutCall.od_name = idf->id_text; OutCall.od_file = ex->ex_file; OutCall.od_line = ex->ex_line; OutCall.od_arg = (struct argument *)0; OutCall.od_nrargs = 0; if ((ex = ex->OP_RIGHT) != 0) { /* function call with arguments: */ /* store types of argument expressions in 'struct argument's */ while (ex->ex_class == Oper && ex->OP_OPER == PARCOMMA) { add_expr_arg(ex->OP_RIGHT); ex = ex->OP_LEFT; } add_expr_arg(ex); } OutCall.od_valused = used; /* USED, IGNORED or VOIDED */ } PRIVATE add_expr_arg(e) struct expr *e; { register struct argument *arg; arg = new_argument(); arg->ar_type = e->ex_type; if (is_cp_cst(e)) { arg->ar_class = ArgConst; arg->CAA_VALUE = e->VL_VALUE; } else if ( e->ex_type == string_type && e->ex_class == Value && e->VL_CLASS == Label ) { /* it may be a string; let's look it up */ register struct string_cst *sc = str_list; while (sc) { if (sc->sc_dlb == e->VL_LBL) break; sc = sc->next; } if (sc) { /* it was a string */ arg->ar_class = ArgString; arg->CAS_VALUE = sc->sc_value; arg->CAS_LEN = sc->sc_len - 1; /* included the \0 */ } else { arg->ar_class = ArgExpr; } } else { arg->ar_class = ArgExpr; } arg->next = OutCall.od_arg; OutCall.od_arg = arg; OutCall.od_nrargs++; } #endif /* LINT */