014be56fb0
exactly equivalent and standard strchr() and strrchr() functions instead.
584 lines
12 KiB
C
584 lines
12 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".
|
|
*/
|
|
/* $Id$ */
|
|
/* Lint outdef construction */
|
|
|
|
#include "lint.h"
|
|
|
|
#ifdef LINT
|
|
|
|
#include <alloc.h>
|
|
#include "interface.h"
|
|
#ifdef ANSI
|
|
#include <flt_arith.h>
|
|
#endif /* ANSI */
|
|
#include "arith.h"
|
|
#include "assert.h"
|
|
#include "type.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();
|
|
|
|
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:
|
|
* 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;
|
|
}
|
|
|
|
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();
|
|
|
|
/* 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 = (type->tp_unsigned ? uint_type : int_type);
|
|
break;
|
|
case FLOAT:
|
|
type = double_type;
|
|
break;
|
|
}
|
|
|
|
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_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 removed, 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:
|
|
/* remove '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 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);
|
|
}
|
|
|
|
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;
|
|
|
|
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 #<num>, 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 FLOAT:
|
|
case DOUBLE:
|
|
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 */
|