/* * (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 outdef construction */ #include "lint.h" #ifdef LINT #include #include "arith.h" #include "assert.h" #include "type.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 "expr.h" #include "l_lint.h" #include "l_comment.h" #include "l_outdef.h" #include "l_class.h" extern char *bts2str(); extern char *symbol2str(); int stat_number = 9999; /* static scope number */ struct outdef OutDef, OutCall; 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_ext_def(idf, sc) struct idf *idf; { /* At this place the following fields of the outputdefinition can be * filled: * name, stat_number, class, file, line, type. * For variable definitions and declarations this will be all. * For functions the fields nrargs and argtps are filled after parsing * the arguments. * The returns-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; } 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; } 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 tp_entries 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(); /* Do the conversions on the formals that could not be done in declare_idf(). It is, unfortunately, impossible not to do them, since the corresponding actuals will have been converted to generate proper code and we do not want to duplicate the whole of expression handling for lint. */ switch (type->tp_fund) { case CHAR: case SHORT: type = int_type; break; case FLOAT: type = double_type; break; } if (f_FORMAT && nrargs == f_FORMATn) { if ( 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_VARARGSn > nrargs) { warning("VARARGS%d function has only %d argument%s", f_VARARGSn, nrargs, nrargs == 1 ? "" : "s" ); f_VARARGSn = nrargs; } if (f_FORMAT) { warning("FORMAT%d function has only %d argument%s", f_FORMATn, nrargs, nrargs == 1 ? "" : "s" ); f_FORMAT = 0; } OutDef.od_nrargs = nrargs; } 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); } output_def(od) struct outdef *od; { /* As the types are output the tp_entries are removed, because they * are then not needed anymore. */ if (od->od_class == XXDF) return; if (LINTLIB) { switch (od->od_class) { case EFDF: od->od_class = LFDF; break; case EVDF: od->od_class = LVDF; break; case SFDF: /* remove tp_entries */ 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 EFDF: case SFDF: case LFDF: 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); } 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); } } 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; default: NOTREACHED(); /*NOTREACHED*/ } } 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); } 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: printf("%s %s", symbol2str(tp->tp_fund), tp->tp_idf->id_text); break; case CHAR: case INT: case SHORT: case LONG: case FLOAT: case DOUBLE: case VOID: case ERRONEOUS: if (tp->tp_unsigned) printf("unsigned "); printf("%s", symbol2str(tp->tp_fund)); break; default: NOTREACHED(); /*NOTREACHED*/ } } 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. */ } 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; if (def->df_sc == IMPLICIT && !idf->id_def->df_used) { /* IFDC, first time */ implicit_func_decl(idf, ex->ex_file, ex->ex_line); } 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 tp_entries */ while (ex->ex_class == Oper && ex->OP_OPER == PARCOMMA) { fill_arg(ex->OP_RIGHT); ex = ex->OP_LEFT; } fill_arg(ex); } OutCall.od_valused = used; /* USED, IGNORED or VOIDED */ } fill_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_class == String) { arg->ar_class = ArgString; arg->CAS_VALUE = e->SG_VALUE; arg->CAS_LEN = e->SG_LEN - 1; /* SG_LEN includes the \0 */ } else { arg->ar_class = ArgExpr; } arg->next = OutCall.od_arg; OutCall.od_arg = arg; OutCall.od_nrargs++; } #endif LINT