Index: head/usr.bin/xlint/lint1/decl.c =================================================================== --- head/usr.bin/xlint/lint1/decl.c (revision 286613) +++ head/usr.bin/xlint/lint1/decl.c (revision 286614) @@ -1,3052 +1,3052 @@ /* $NetBSD: decl.c,v 1.33 2004/06/20 22:20:16 jmc Exp $ */ /* * Copyright (c) 1996 Christopher G. Demetriou. All Rights Reserved. * Copyright (c) 1994, 1995 Jochen Pohl * All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Jochen Pohl for * The NetBSD Project. * 4. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #if defined(__RCSID) && !defined(lint) __RCSID("$NetBSD: decl.c,v 1.33 2004/06/20 22:20:16 jmc Exp $"); #endif __FBSDID("$FreeBSD$"); #include #include #include #include #include "lint1.h" const char *unnamed = ""; /* shared type structures for arithmtic types and void */ static type_t *typetab; /* value of next enumerator during declaration of enum types */ int enumval; /* * pointer to top element of a stack which contains informations local * to nested declarations */ dinfo_t *dcs; static type_t *tdeferr(type_t *, tspec_t); static void settdsym(type_t *, sym_t *); static tspec_t mrgtspec(tspec_t, tspec_t); static void align(int, int); static sym_t *newtag(sym_t *, scl_t, int, int); static int eqargs(type_t *, type_t *, int *); static int mnoarg(type_t *, int *); static int chkosdef(sym_t *, sym_t *); static int chkptdecl(sym_t *, sym_t *); static sym_t *nsfunc(sym_t *, sym_t *); static void osfunc(sym_t *, sym_t *); static void ledecl(sym_t *); static int chkinit(sym_t *); static void chkausg(int, sym_t *); static void chkvusg(int, sym_t *); static void chklusg(sym_t *); static void chktusg(sym_t *); static void chkglvar(sym_t *); static void glchksz(sym_t *); /* * initializes all global vars used in declarations */ void initdecl(void) { int i; /* declaration stack */ if ((dcs = calloc(1, sizeof (dinfo_t))) == NULL) nomem(); dcs->d_ctx = EXTERN; dcs->d_ldlsym = &dcs->d_dlsyms; /* type information and classification */ inittyp(); /* shared type structures */ if ((typetab = calloc(NTSPEC, sizeof (type_t))) == NULL) nomem(); for (i = 0; i < NTSPEC; i++) typetab[i].t_tspec = NOTSPEC; typetab[CHAR].t_tspec = CHAR; typetab[SCHAR].t_tspec = SCHAR; typetab[UCHAR].t_tspec = UCHAR; typetab[SHORT].t_tspec = SHORT; typetab[USHORT].t_tspec = USHORT; typetab[INT].t_tspec = INT; typetab[UINT].t_tspec = UINT; typetab[LONG].t_tspec = LONG; typetab[ULONG].t_tspec = ULONG; typetab[QUAD].t_tspec = QUAD; typetab[UQUAD].t_tspec = UQUAD; typetab[FLOAT].t_tspec = FLOAT; typetab[DOUBLE].t_tspec = DOUBLE; typetab[LDOUBLE].t_tspec = LDOUBLE; typetab[VOID].t_tspec = VOID; /* * Next two are not real types. They are only used by the parser * to return keywords "signed" and "unsigned" */ typetab[SIGNED].t_tspec = SIGNED; typetab[UNSIGN].t_tspec = UNSIGN; } /* * Returns a shared type structure vor arithmetic types and void. * - * It's important do duplicate this structure (using duptyp() or tdupdyp()) + * It's important to duplicate this structure (using duptyp() or tdupdyp()) * if it is to be modified (adding qualifiers or anything else). */ type_t * gettyp(tspec_t t) { return (&typetab[t]); } type_t * duptyp(const type_t *tp) { type_t *ntp; ntp = getblk(sizeof (type_t)); STRUCT_ASSIGN(*ntp, *tp); return (ntp); } /* * Use tduptyp() instead of duptyp() inside expressions (if the * allocated memory should be freed after the expr). */ type_t * tduptyp(const type_t *tp) { type_t *ntp; ntp = tgetblk(sizeof (type_t)); STRUCT_ASSIGN(*ntp, *tp); return (ntp); } /* * Returns 1 if the argument is void or an incomplete array, * struct, union or enum type. */ int incompl(type_t *tp) { tspec_t t; if ((t = tp->t_tspec) == VOID) { return (1); } else if (t == ARRAY) { return (tp->t_aincompl); } else if (t == STRUCT || t == UNION) { return (tp->t_str->sincompl); } else if (t == ENUM) { return (tp->t_enum->eincompl); } return (0); } /* * Set the flag for (in)complete array, struct, union or enum * types. */ void setcompl(type_t *tp, int ic) { tspec_t t; if ((t = tp->t_tspec) == ARRAY) { tp->t_aincompl = ic; } else if (t == STRUCT || t == UNION) { tp->t_str->sincompl = ic; } else { if (t != ENUM) LERROR("setcompl()"); tp->t_enum->eincompl = ic; } } /* * Remember the storage class of the current declaration in dcs->d_scl * (the top element of the declaration stack) and detect multiple * storage classes. */ void addscl(scl_t sc) { if (sc == INLINE) { if (dcs->d_inline) /* duplicate '%s' */ warning(10, "inline"); dcs->d_inline = 1; return; } if (dcs->d_type != NULL || dcs->d_atyp != NOTSPEC || dcs->d_smod != NOTSPEC || dcs->d_lmod != NOTSPEC) { /* storage class after type is obsolescent */ warning(83); } if (dcs->d_scl == NOSCL) { dcs->d_scl = sc; } else { /* * multiple storage classes. An error will be reported in * deftyp(). */ dcs->d_mscl = 1; } } /* * Remember the type, modifier or typedef name returned by the parser * in *dcs (top element of decl stack). This information is used in * deftyp() to build the type used for all declarators in this * declaration. * * Is tp->t_typedef 1, the type comes from a previously defined typename. * Otherwise it comes from a type specifier (int, long, ...) or a * struct/union/enum tag. */ void addtype(type_t *tp) { tspec_t t; if (tp->t_typedef) { if (dcs->d_type != NULL || dcs->d_atyp != NOTSPEC || dcs->d_lmod != NOTSPEC || dcs->d_smod != NOTSPEC) { /* * something like "typedef int a; int a b;" * This should not happen with current grammar. */ LERROR("addtype()"); } dcs->d_type = tp; return; } t = tp->t_tspec; if (t == STRUCT || t == UNION || t == ENUM) { /* * something like "int struct a ..." * struct/union/enum with anything else is not allowed */ if (dcs->d_type != NULL || dcs->d_atyp != NOTSPEC || dcs->d_lmod != NOTSPEC || dcs->d_smod != NOTSPEC) { /* * remember that an error must be reported in * deftyp(). */ dcs->d_terr = 1; dcs->d_atyp = dcs->d_lmod = dcs->d_smod = NOTSPEC; } dcs->d_type = tp; return; } if (dcs->d_type != NULL && !dcs->d_type->t_typedef) { /* * something like "struct a int" * struct/union/enum with anything else is not allowed */ dcs->d_terr = 1; return; } if (t == LONG && dcs->d_lmod == LONG) { /* "long long" or "long ... long" */ t = QUAD; dcs->d_lmod = NOTSPEC; if (!quadflg) /* %s C does not support 'long long' */ (void)c99ism(265, tflag ? "traditional" : "c89"); } if (dcs->d_type != NULL && dcs->d_type->t_typedef) { /* something like "typedef int a; a long ..." */ dcs->d_type = tdeferr(dcs->d_type, t); return; } /* now it can be only a combination of arithmetic types and void */ if (t == SIGNED || t == UNSIGN) { /* remember specifiers "signed" and "unsigned" in dcs->d_smod */ if (dcs->d_smod != NOTSPEC) /* * more than one "signed" and/or "unsigned"; print * an error in deftyp() */ dcs->d_terr = 1; dcs->d_smod = t; } else if (t == SHORT || t == LONG || t == QUAD) { /* * remember specifiers "short", "long" and "long long" in * dcs->d_lmod */ if (dcs->d_lmod != NOTSPEC) /* more than one, print error in deftyp() */ dcs->d_terr = 1; dcs->d_lmod = t; } else { /* * remember specifiers "void", "char", "int", "float" or * "double" int dcs->d_atyp */ if (dcs->d_atyp != NOTSPEC) /* more than one, print error in deftyp() */ dcs->d_terr = 1; dcs->d_atyp = t; } } /* * called if a list of declaration specifiers contains a typedef name * and other specifiers (except struct, union, enum, typedef name) */ static type_t * tdeferr(type_t *td, tspec_t t) { tspec_t t2; t2 = td->t_tspec; switch (t) { case SIGNED: case UNSIGN: if (t2 == CHAR || t2 == SHORT || t2 == INT || t2 == LONG || t2 == QUAD) { if (!tflag) /* modifying typedef with ... */ warning(5, ttab[t].tt_name); td = duptyp(gettyp(mrgtspec(t2, t))); td->t_typedef = 1; return (td); } break; case SHORT: if (t2 == INT || t2 == UINT) { /* modifying typedef with ... */ warning(5, "short"); td = duptyp(gettyp(t2 == INT ? SHORT : USHORT)); td->t_typedef = 1; return (td); } break; case LONG: if (t2 == INT || t2 == UINT || t2 == LONG || t2 == ULONG || t2 == FLOAT || t2 == DOUBLE) { /* modifying typedef with ... */ warning(5, "long"); if (t2 == INT) { td = gettyp(LONG); } else if (t2 == UINT) { td = gettyp(ULONG); } else if (t2 == LONG) { td = gettyp(QUAD); } else if (t2 == ULONG) { td = gettyp(UQUAD); } else if (t2 == FLOAT) { td = gettyp(DOUBLE); } else if (t2 == DOUBLE) { td = gettyp(LDOUBLE); } td = duptyp(td); td->t_typedef = 1; return (td); } break; /* LINTED (enumeration values not handled in switch) */ case NOTSPEC: case USHORT: case UCHAR: case SCHAR: case CHAR: case FUNC: case ARRAY: case PTR: case ENUM: case UNION: case STRUCT: case VOID: case LDOUBLE: case DOUBLE: case FLOAT: case UQUAD: case QUAD: case ULONG: case UINT: case INT: break; } /* Anything other is not accepted. */ dcs->d_terr = 1; return (td); } /* * Remember the symbol of a typedef name (2nd arg) in a struct, union * or enum tag if the typedef name is the first defined for this tag. * * If the tag is unnamed, the typdef name is used for identification * of this tag in lint2. Although its possible that more than one typedef * name is defined for one tag, the first name defined should be unique * if the tag is unnamed. */ static void settdsym(type_t *tp, sym_t *sym) { tspec_t t; if ((t = tp->t_tspec) == STRUCT || t == UNION) { if (tp->t_str->stdef == NULL) tp->t_str->stdef = sym; } else if (t == ENUM) { if (tp->t_enum->etdef == NULL) tp->t_enum->etdef = sym; } } /* * Remember a qualifier which is part of the declaration specifiers * (and not the declarator) in the top element of the declaration stack. * Also detect multiple qualifiers of the same kind. * The remembered qualifier is used by deftyp() to construct the type * for all declarators. */ void addqual(tqual_t q) { if (q == CONST) { if (dcs->d_const) { /* duplicate "%s" */ warning(10, "const"); } dcs->d_const = 1; } else { if (q != VOLATILE) LERROR("addqual()"); if (dcs->d_volatile) { /* duplicate "%s" */ warning(10, "volatile"); } dcs->d_volatile = 1; } } /* * Go to the next declaration level (structs, nested structs, blocks, * argument declaration lists ...) */ void pushdecl(scl_t sc) { dinfo_t *di; if (dflag) (void)printf("pushdecl(%d)\n", (int)sc); /* put a new element on the declaration stack */ if ((di = calloc(1, sizeof (dinfo_t))) == NULL) nomem(); di->d_nxt = dcs; dcs = di; di->d_ctx = sc; di->d_ldlsym = &di->d_dlsyms; } /* * Go back to previous declaration level */ void popdecl(void) { dinfo_t *di; if (dflag) (void)printf("popdecl(%d)\n", (int)dcs->d_ctx); if (dcs->d_nxt == NULL) LERROR("popdecl()"); di = dcs; dcs = di->d_nxt; switch (di->d_ctx) { case EXTERN: /* there is nothing after external declarations */ LERROR("popdecl()"); /* NOTREACHED */ case MOS: case MOU: case ENUMCON: /* * Symbols declared in (nested) structs or enums are * part of the next level (they are removed from the * symbol table if the symbols of the outher level are * removed) */ if ((*dcs->d_ldlsym = di->d_dlsyms) != NULL) dcs->d_ldlsym = di->d_ldlsym; break; case ARG: /* * All symbols in dcs->d_dlsyms are introduced in old style * argument declarations (it's not clean, but possible). * They are appended to the list of symbols declared in * an old style argument identifier list or a new style * parameter type list. */ if (di->d_dlsyms != NULL) { *di->d_ldlsym = dcs->d_fpsyms; dcs->d_fpsyms = di->d_dlsyms; } break; case ABSTRACT: /* * casts and sizeof * Append all symbols declared in the abstract declaration * to the list of symbols declared in the surrounding decl. * or block. * XXX I'm not sure whether they should be removed from the * symbol table now or later. */ if ((*dcs->d_ldlsym = di->d_dlsyms) != NULL) dcs->d_ldlsym = di->d_ldlsym; break; case AUTO: /* check usage of local vars */ chkusage(di); /* FALLTHROUGH */ case PARG: /* usage of arguments will be checked by funcend() */ rmsyms(di->d_dlsyms); break; default: LERROR("popdecl()"); } free(di); } /* * Set flag d_asm in all declaration stack elements up to the * outermost one. * * This is used to mark compound statements which have, possibly in * nested compound statements, asm statements. For these compound * statements no warnings about unused or unitialized variables are * printed. * * There is no need to clear d_asm in dinfo structs with context AUTO, * because these structs are freed at the end of the compound statement. * But it must be cleard in the outermost dinfo struct, which has * context EXTERN. This could be done in clrtyp() and would work for * C, but not for C++ (due to mixed statements and declarations). Thus * we clear it in glclup(), which is used to do some cleanup after * global declarations/definitions. */ void setasm(void) { dinfo_t *di; for (di = dcs; di != NULL; di = di->d_nxt) di->d_asm = 1; } /* * Clean all elements of the top element of declaration stack which * will be used by the next declaration */ void clrtyp(void) { dcs->d_atyp = dcs->d_smod = dcs->d_lmod = NOTSPEC; dcs->d_scl = NOSCL; dcs->d_type = NULL; dcs->d_const = dcs->d_volatile = 0; dcs->d_inline = 0; dcs->d_mscl = dcs->d_terr = 0; dcs->d_nedecl = 0; dcs->d_notyp = 0; } /* * Create a type structure from the informations gathered in * the declaration stack. * Complain about storage classes which are not possible in current * context. */ void deftyp(void) { tspec_t t, s, l; type_t *tp; scl_t scl; t = dcs->d_atyp; /* CHAR, INT, FLOAT, DOUBLE, VOID */ s = dcs->d_smod; /* SIGNED, UNSIGNED */ l = dcs->d_lmod; /* SHORT, LONG, QUAD */ tp = dcs->d_type; scl = dcs->d_scl; if (t == NOTSPEC && s == NOTSPEC && l == NOTSPEC && tp == NULL) dcs->d_notyp = 1; if (tp != NULL && (t != NOTSPEC || s != NOTSPEC || l != NOTSPEC)) { /* should never happen */ LERROR("deftyp()"); } if (tp == NULL) { switch (t) { case NOTSPEC: t = INT; /* FALLTHROUGH */ case INT: if (s == NOTSPEC) s = SIGNED; break; case CHAR: if (l != NOTSPEC) { dcs->d_terr = 1; l = NOTSPEC; } break; case FLOAT: if (l == LONG) { l = NOTSPEC; t = DOUBLE; if (!tflag) /* use 'double' instead of ... */ warning(6); } break; case DOUBLE: if (l == LONG) { l = NOTSPEC; t = LDOUBLE; if (tflag) /* 'long double' is illegal in ... */ warning(266); } break; case VOID: break; default: LERROR("deftyp()"); } if (t != INT && t != CHAR && (s != NOTSPEC || l != NOTSPEC)) { dcs->d_terr = 1; l = s = NOTSPEC; } if (l != NOTSPEC) t = l; dcs->d_type = gettyp(mrgtspec(t, s)); } if (dcs->d_mscl) { /* only one storage class allowed */ error(7); } if (dcs->d_terr) { /* illegal type combination */ error(4); } if (dcs->d_ctx == EXTERN) { if (scl == REG || scl == AUTO) { /* illegal storage class */ error(8); scl = NOSCL; } } else if (dcs->d_ctx == ARG || dcs->d_ctx == PARG) { if (scl != NOSCL && scl != REG) { /* only "register" valid ... */ error(9); scl = NOSCL; } } dcs->d_scl = scl; if (dcs->d_const && dcs->d_type->t_const) { if (!dcs->d_type->t_typedef) LERROR("deftyp()"); /* typedef already qualified with "%s" */ warning(68, "const"); } if (dcs->d_volatile && dcs->d_type->t_volatile) { if (!dcs->d_type->t_typedef) LERROR("deftyp()"); /* typedef already qualified with "%s" */ warning(68, "volatile"); } if (dcs->d_const || dcs->d_volatile) { dcs->d_type = duptyp(dcs->d_type); dcs->d_type->t_const |= dcs->d_const; dcs->d_type->t_volatile |= dcs->d_volatile; } } /* * Merge type specifiers (char, ..., long long, signed, unsigned). */ static tspec_t mrgtspec(tspec_t t, tspec_t s) { if (s == SIGNED || s == UNSIGN) { if (t == CHAR) { t = s == SIGNED ? SCHAR : UCHAR; } else if (t == SHORT) { t = s == SIGNED ? SHORT : USHORT; } else if (t == INT) { t = s == SIGNED ? INT : UINT; } else if (t == LONG) { t = s == SIGNED ? LONG : ULONG; } else if (t == QUAD) { t = s == SIGNED ? QUAD : UQUAD; } } return (t); } /* * Return the length of a type in bit. * * Printing a message if the outhermost dimension of an array is 0 must * be done by the caller. All other problems are reported by length() * if name is not NULL. */ int length(type_t *tp, const char *name) { int elem, elsz; elem = 1; while (tp && tp->t_tspec == ARRAY) { elem *= tp->t_dim; tp = tp->t_subt; } if (tp == NULL) return -1; switch (tp->t_tspec) { case FUNC: /* compiler takes size of function */ LERROR("%s", msgs[12]); /* NOTREACHED */ case STRUCT: case UNION: if (incompl(tp) && name != NULL) { /* incomplete structure or union %s: %s */ error(31, tp->t_str->stag->s_name, name); } elsz = tp->t_str->size; break; case ENUM: if (incompl(tp) && name != NULL) { /* incomplete enum type: %s */ warning(13, name); } /* FALLTHROUGH */ default: elsz = size(tp->t_tspec); if (elsz <= 0) LERROR("length()"); break; } return (elem * elsz); } /* - * Get the alignment of the given Type in bits. + * Get the alignment of the given type in bits. */ int getbound(type_t *tp) { int a; tspec_t t; while (tp && tp->t_tspec == ARRAY) tp = tp->t_subt; if (tp == NULL) return -1; if ((t = tp->t_tspec) == STRUCT || t == UNION) { a = tp->t_str->align; } else if (t == FUNC) { /* compiler takes alignment of function */ error(14); a = LINT_ALIGN(1) * CHAR_BIT; } else { if ((a = size(t)) == 0) { a = CHAR_BIT; } else if (a > LINT_ALIGN(1) * CHAR_BIT) { a = LINT_ALIGN(1) * CHAR_BIT; } } if (a < CHAR_BIT || a > LINT_ALIGN(1) * CHAR_BIT) LERROR("getbound()"); return (a); } /* * Concatenate two lists of symbols by s_nxt. Used by declarations of * struct/union/enum elements and parameters. */ sym_t * lnklst(sym_t *l1, sym_t *l2) { sym_t *l; if ((l = l1) == NULL) return (l2); while (l1->s_nxt != NULL) l1 = l1->s_nxt; l1->s_nxt = l2; return (l); } /* * Check if the type of the given symbol is valid and print an error * message if it is not. * * Invalid types are: * - arrays of incomlete types or functions * - functions returning arrays or functions * - void types other than type of function or pointer */ void chktyp(sym_t *sym) { tspec_t to, t; type_t **tpp, *tp; tpp = &sym->s_type; to = NOTSPEC; while ((tp = *tpp) != NULL) { t = tp->t_tspec; /* * If this is the type of an old style function definition, * a better warning is printed in funcdef(). */ if (t == FUNC && !tp->t_proto && !(to == NOTSPEC && sym->s_osdef)) { if (sflag && hflag) /* function declaration is not a prototype */ warning(287); } if (to == FUNC) { if (t == FUNC || t == ARRAY) { /* function returns illegal type */ error(15); if (t == FUNC) { *tpp = incref(*tpp, PTR); } else { *tpp = incref((*tpp)->t_subt, PTR); } return; } else if (tp->t_const || tp->t_volatile) { if (sflag) { /* XXX oder better !tflag ? */ /* function cannot return const... */ warning(228); } } } if (to == ARRAY) { if (t == FUNC) { /* array of function is illegal */ error(16); *tpp = gettyp(INT); return; } else if (t == ARRAY && tp->t_dim == 0) { /* null dimension */ error(17); return; } else if (t == VOID) { /* illegal use of void */ error(18); *tpp = gettyp(INT); #if 0 /* errors are produced by length() */ } else if (incompl(tp)) { /* array of incomplete type */ if (sflag) { error(301); } else { warning(301); } #endif } } else if (to == NOTSPEC && t == VOID) { if (dcs->d_ctx == PARG) { if (sym->s_scl != ABSTRACT) { if (sym->s_name == unnamed) LERROR("chktyp()"); /* void param cannot have name: %s */ error(61, sym->s_name); *tpp = gettyp(INT); } } else if (dcs->d_ctx == ABSTRACT) { /* ok */ } else if (sym->s_scl != TYPEDEF) { /* void type for %s */ error(19, sym->s_name); *tpp = gettyp(INT); } } if (t == VOID && to != PTR) { if (tp->t_const || tp->t_volatile) { /* inappropriate qualifiers with "void" */ warning(69); tp->t_const = tp->t_volatile = 0; } } tpp = &tp->t_subt; to = t; } } /* * Process the declarator of a struct/union element. */ sym_t * decl1str(sym_t *dsym) { type_t *tp; tspec_t t; int sz, len; int o = 0; /* Appease gcc */ scl_t sc; if ((sc = dsym->s_scl) != MOS && sc != MOU) LERROR("decl1str()"); if (dcs->d_rdcsym != NULL) { if ((sc = dcs->d_rdcsym->s_scl) != MOS && sc != MOU) /* should be ensured by storesym() */ LERROR("decl1str()"); if (dsym->s_styp == dcs->d_rdcsym->s_styp) { /* duplicate member name: %s */ error(33, dsym->s_name); rmsym(dcs->d_rdcsym); } } chktyp(dsym); t = (tp = dsym->s_type)->t_tspec; if (dsym->s_field) { /* * bit field * - * only unsigned und signed int are protable bit-field types - *(at least in ANSI C, in traditional C only unsigned int) + * only unsigned and signed int are portable bit-field types + * (at least in ANSI C, in traditional C only unsigned int) */ if (t == CHAR || t == UCHAR || t == SCHAR || t == SHORT || t == USHORT || t == ENUM) { if (bitfieldtype_ok == 0) { if (sflag) { char buf[64]; /* * bit-field type '%s' invalid in * ANSI C */ warning(273, tyname(buf, sizeof(buf), tp)); } else if (pflag) { /* nonportable bit-field type */ warning(34); } } } else if (t == INT && dcs->d_smod == NOTSPEC) { if (pflag && bitfieldtype_ok == 0) { /* nonportable bit-field type */ warning(34); } } else if (t != INT && t != UINT) { /* * Non-integer types are always illegal for * bitfields, regardless of BITFIELDTYPE. * Integer types not dealt with above are * okay only if BITFIELDTYPE is in effect. */ if (bitfieldtype_ok == 0 || isityp(t) == 0) { /* illegal bit-field type */ error(35); sz = tp->t_flen; dsym->s_type = tp = duptyp(gettyp(t = INT)); if ((tp->t_flen = sz) > size(t)) tp->t_flen = size(t); } } if ((len = tp->t_flen) < 0 || len > size(t)) { /* illegal bit-field size */ error(36); tp->t_flen = size(t); } else if (len == 0 && dsym->s_name != unnamed) { /* zero size bit-field */ error(37); tp->t_flen = size(t); } if (dsym->s_scl == MOU) { /* illegal use of bit-field */ error(41); dsym->s_type->t_isfield = 0; dsym->s_field = 0; } } else if (t == FUNC) { /* function illegal in structure or union */ error(38); dsym->s_type = tp = incref(tp, t = PTR); } /* * bit-fields of length 0 are not warned about because length() * does not return the length of the bit-field but the length * of the type the bit-field is packed in (its ok) */ if ((sz = length(dsym->s_type, dsym->s_name)) == 0) { if (t == ARRAY && dsym->s_type->t_dim == 0) { /* illegal zero sized structure member: %s */ c99ism(39, dsym->s_name); } } if (dcs->d_ctx == MOU) { o = dcs->d_offset; dcs->d_offset = 0; } if (dsym->s_field) { align(getbound(tp), tp->t_flen); dsym->s_value.v_quad = (dcs->d_offset / size(t)) * size(t); tp->t_foffs = dcs->d_offset - (int)dsym->s_value.v_quad; dcs->d_offset += tp->t_flen; } else { align(getbound(tp), 0); dsym->s_value.v_quad = dcs->d_offset; dcs->d_offset += sz; } if (dcs->d_ctx == MOU) { if (o > dcs->d_offset) dcs->d_offset = o; } chkfdef(dsym, 0); /* * Clear the BITFIELDTYPE indicator after processing each * structure element. */ bitfieldtype_ok = 0; return (dsym); } /* * Aligns next structure element as required. * * al contains the required alignment, len the length of a bit-field. */ static void align(int al, int len) { int no; /* * The alignment of the current element becomes the alignment of * the struct/union if it is larger than the current alignment * of the struct/union. */ if (al > dcs->d_stralign) dcs->d_stralign = al; no = (dcs->d_offset + (al - 1)) & ~(al - 1); if (len == 0 || dcs->d_offset + len > no) dcs->d_offset = no; } /* * Remember the width of the field in its type structure. */ sym_t * bitfield(sym_t *dsym, int len) { if (dsym == NULL) { dsym = getblk(sizeof (sym_t)); dsym->s_name = unnamed; dsym->s_kind = FMOS; dsym->s_scl = MOS; dsym->s_type = gettyp(UINT); dsym->s_blklev = -1; } dsym->s_type = duptyp(dsym->s_type); dsym->s_type->t_isfield = 1; dsym->s_type->t_flen = len; dsym->s_field = 1; return (dsym); } /* * Collect informations about a sequence of asterisks and qualifiers * in a list of type pqinf_t. * Qualifiers refer always to the left asterisk. The rightmost asterisk * will be at the top of the list. */ pqinf_t * mergepq(pqinf_t *p1, pqinf_t *p2) { pqinf_t *p; if (p2->p_pcnt != 0) { /* left '*' at the end of the list */ for (p = p2; p->p_nxt != NULL; p = p->p_nxt) continue; p->p_nxt = p1; return (p2); } else { if (p2->p_const) { if (p1->p_const) { /* duplicate %s */ warning(10, "const"); } p1->p_const = 1; } if (p2->p_volatile) { if (p1->p_volatile) { /* duplicate %s */ warning(10, "volatile"); } p1->p_volatile = 1; } free(p2); return (p1); } } /* * Followint 3 functions extend the type of a declarator with * pointer, function and array types. * - * The current type is the Type built by deftyp() (dcs->d_type) and + * The current type is the type built by deftyp() (dcs->d_type) and * pointer, function and array types already added for this * declarator. The new type extension is inserted between both. */ sym_t * addptr(sym_t *decl, pqinf_t *pi) { type_t **tpp, *tp; pqinf_t *npi; tpp = &decl->s_type; while (*tpp && *tpp != dcs->d_type) tpp = &(*tpp)->t_subt; if (*tpp == NULL) return decl; while (pi != NULL) { *tpp = tp = getblk(sizeof (type_t)); tp->t_tspec = PTR; tp->t_const = pi->p_const; tp->t_volatile = pi->p_volatile; *(tpp = &tp->t_subt) = dcs->d_type; npi = pi->p_nxt; free(pi); pi = npi; } return (decl); } /* * If a dimension was specified, dim is 1, otherwise 0 * n is the specified dimension */ sym_t * addarray(sym_t *decl, int dim, int n) { type_t **tpp, *tp; tpp = &decl->s_type; while (*tpp && *tpp != dcs->d_type) tpp = &(*tpp)->t_subt; if (*tpp == NULL) return decl; *tpp = tp = getblk(sizeof (type_t)); tp->t_tspec = ARRAY; tp->t_subt = dcs->d_type; tp->t_dim = n; if (n < 0) { /* negative array dimension */ error(20, n); n = 0; } else if (n == 0 && dim) { /* zero array dimension */ c99ism(322, dim); } else if (n == 0 && !dim) { /* is incomplete type */ setcompl(tp, 1); } return (decl); } sym_t * addfunc(sym_t *decl, sym_t *args) { type_t **tpp, *tp; if (dcs->d_proto) { if (tflag) /* function prototypes are illegal in traditional C */ warning(270); args = nsfunc(decl, args); } else { osfunc(decl, args); } /* * The symbols are removed from the symbol table by popdecl() after * addfunc(). To be able to restore them if this is a function * definition, a pointer to the list of all symbols is stored in * dcs->d_nxt->d_fpsyms. Also a list of the arguments (concatenated * by s_nxt) is stored in dcs->d_nxt->d_fargs. * (dcs->d_nxt must be used because *dcs is the declaration stack * element created for the list of params and is removed after * addfunc()) */ if (dcs->d_nxt->d_ctx == EXTERN && decl->s_type == dcs->d_nxt->d_type) { dcs->d_nxt->d_fpsyms = dcs->d_dlsyms; dcs->d_nxt->d_fargs = args; } tpp = &decl->s_type; while (*tpp && *tpp != dcs->d_nxt->d_type) tpp = &(*tpp)->t_subt; if (*tpp == NULL) return decl; *tpp = tp = getblk(sizeof (type_t)); tp->t_tspec = FUNC; tp->t_subt = dcs->d_nxt->d_type; if ((tp->t_proto = dcs->d_proto) != 0) tp->t_args = args; tp->t_vararg = dcs->d_vararg; return (decl); } /* * Called for new style function declarations. */ /* ARGSUSED */ static sym_t * nsfunc(sym_t *decl, sym_t *args) { sym_t *arg, *sym; scl_t sc; int n; /* * Declarations of structs/unions/enums in param lists are legal, * but senseless. */ for (sym = dcs->d_dlsyms; sym != NULL; sym = sym->s_dlnxt) { sc = sym->s_scl; if (sc == STRTAG || sc == UNIONTAG || sc == ENUMTAG) { /* dubious tag declaration: %s %s */ warning(85, scltoa(sc), sym->s_name); } } n = 1; for (arg = args; arg != NULL; arg = arg->s_nxt) { if (arg->s_type->t_tspec == VOID) { if (n > 1 || arg->s_nxt != NULL) { /* "void" must be sole parameter */ error(60); arg->s_type = gettyp(INT); } } n++; } /* return NULL if first param is VOID */ return (args != NULL && args->s_type->t_tspec != VOID ? args : NULL); } /* * Called for old style function declarations. */ static void osfunc(sym_t *decl, sym_t *args) { /* * Remember list of params only if this is really seams to be * a function definition. */ if (dcs->d_nxt->d_ctx == EXTERN && decl->s_type == dcs->d_nxt->d_type) { /* * We assume that this becomes a function definition. If * we are wrong, its corrected in chkfdef(). */ if (args != NULL) { decl->s_osdef = 1; decl->s_args = args; } } else { if (args != NULL) /* function prototype parameters must have types */ warning(62); } } /* * Lists of Identifiers in functions declarations are allowed only if * its also a function definition. If this is not the case, print a * error message. */ void chkfdef(sym_t *sym, int msg) { if (sym->s_osdef) { if (msg) { /* incomplete or misplaced function definition */ error(22); } sym->s_osdef = 0; sym->s_args = NULL; } } /* * Process the name in a declarator. * If the symbol does already exists, a new one is created. * The symbol becomes one of the storage classes EXTERN, STATIC, AUTO or * TYPEDEF. * s_def and s_reg are valid after dname(). */ sym_t * dname(sym_t *sym) { scl_t sc = NOSCL; if (sym->s_scl == NOSCL) { dcs->d_rdcsym = NULL; } else if (sym->s_defarg) { sym->s_defarg = 0; dcs->d_rdcsym = NULL; } else { dcs->d_rdcsym = sym; sym = pushdown(sym); } switch (dcs->d_ctx) { case MOS: case MOU: /* Parent setzen */ sym->s_styp = dcs->d_tagtyp->t_str; sym->s_def = DEF; sym->s_value.v_tspec = INT; sc = dcs->d_ctx; break; case EXTERN: /* * static and external symbols without "extern" are * considered to be tentative defined, external * symbols with "extern" are declared, and typedef names * are defined. Tentative defined and declared symbols * may become defined if an initializer is present or * this is a function definition. */ if ((sc = dcs->d_scl) == NOSCL) { sc = EXTERN; sym->s_def = TDEF; } else if (sc == STATIC) { sym->s_def = TDEF; } else if (sc == TYPEDEF) { sym->s_def = DEF; } else if (sc == EXTERN) { sym->s_def = DECL; } else { LERROR("dname()"); } break; case PARG: sym->s_arg = 1; /* FALLTHROUGH */ case ARG: if ((sc = dcs->d_scl) == NOSCL) { sc = AUTO; } else if (sc == REG) { sym->s_reg = 1; sc = AUTO; } else { LERROR("dname()"); } sym->s_def = DEF; break; case AUTO: if ((sc = dcs->d_scl) == NOSCL) { /* * XXX somewhat ugly because we dont know whether * this is AUTO or EXTERN (functions). If we are * wrong it must be corrected in decl1loc(), where * we have the necessary type information. */ sc = AUTO; sym->s_def = DEF; } else if (sc == AUTO || sc == STATIC || sc == TYPEDEF) { sym->s_def = DEF; } else if (sc == REG) { sym->s_reg = 1; sc = AUTO; sym->s_def = DEF; } else if (sc == EXTERN) { sym->s_def = DECL; } else { LERROR("dname()"); } break; default: LERROR("dname()"); } sym->s_scl = sc; sym->s_type = dcs->d_type; dcs->d_fpsyms = NULL; return (sym); } /* * Process a name in the list of formal params in an old style function * definition. */ sym_t * iname(sym_t *sym) { if (sym->s_scl != NOSCL) { if (blklev == sym->s_blklev) { /* redeclaration of formal parameter %s */ error(21, sym->s_name); if (!sym->s_defarg) LERROR("iname()"); } sym = pushdown(sym); } sym->s_type = gettyp(INT); sym->s_scl = AUTO; sym->s_def = DEF; sym->s_defarg = sym->s_arg = 1; return (sym); } /* * Create the type of a tag. * * tag points to the symbol table entry of the tag * kind is the kind of the tag (STRUCT/UNION/ENUM) * decl is 1 if the type of the tag will be completed in this declaration * (the following token is T_LBRACE) * semi is 1 if the following token is T_SEMI */ type_t * mktag(sym_t *tag, tspec_t kind, int decl, int semi) { scl_t scl = NOSCL; type_t *tp; if (kind == STRUCT) { scl = STRTAG; } else if (kind == UNION) { scl = UNIONTAG; } else if (kind == ENUM) { scl = ENUMTAG; } else { LERROR("mktag()"); } if (tag != NULL) { if (tag->s_scl != NOSCL) { tag = newtag(tag, scl, decl, semi); } else { /* a new tag, no empty declaration */ dcs->d_nxt->d_nedecl = 1; if (scl == ENUMTAG && !decl) { if (!tflag && (sflag || pflag)) /* forward reference to enum type */ warning(42); } } if (tag->s_scl == NOSCL) { tag->s_scl = scl; tag->s_type = tp = getblk(sizeof (type_t)); } else { tp = tag->s_type; } } else { tag = getblk(sizeof (sym_t)); tag->s_name = unnamed; UNIQUE_CURR_POS(tag->s_dpos); tag->s_kind = FTAG; tag->s_scl = scl; tag->s_blklev = -1; tag->s_type = tp = getblk(sizeof (type_t)); dcs->d_nxt->d_nedecl = 1; } if (tp->t_tspec == NOTSPEC) { tp->t_tspec = kind; if (kind != ENUM) { tp->t_str = getblk(sizeof (str_t)); tp->t_str->align = CHAR_BIT; tp->t_str->stag = tag; } else { tp->t_isenum = 1; tp->t_enum = getblk(sizeof (enum_t)); tp->t_enum->etag = tag; } - /* ist unvollstaendiger Typ */ + /* is incomplete type */ setcompl(tp, 1); } return (tp); } /* * Checks all possible cases of tag redeclarations. * decl is 1 if T_LBRACE follows * semi is 1 if T_SEMI follows */ static sym_t * newtag(sym_t *tag, scl_t scl, int decl, int semi) { if (tag->s_blklev < blklev) { if (semi) { /* "struct a;" */ if (!tflag) { if (!sflag) /* decl. introduces new type ... */ warning(44, scltoa(scl), tag->s_name); tag = pushdown(tag); } else if (tag->s_scl != scl) { /* base type is really "%s %s" */ warning(45, scltoa(tag->s_scl), tag->s_name); } dcs->d_nxt->d_nedecl = 1; } else if (decl) { /* "struct a { ... } " */ if (hflag) /* redefinition hides earlier one: %s */ warning(43, tag->s_name); tag = pushdown(tag); dcs->d_nxt->d_nedecl = 1; } else if (tag->s_scl != scl) { /* base type is really "%s %s" */ warning(45, scltoa(tag->s_scl), tag->s_name); /* declaration introduces new type in ANSI C: %s %s */ if (!sflag) warning(44, scltoa(scl), tag->s_name); tag = pushdown(tag); dcs->d_nxt->d_nedecl = 1; } } else { if (tag->s_scl != scl) { /* (%s) tag redeclared */ error(46, scltoa(tag->s_scl)); prevdecl(-1, tag); tag = pushdown(tag); dcs->d_nxt->d_nedecl = 1; } else if (decl && !incompl(tag->s_type)) { /* (%s) tag redeclared */ error(46, scltoa(tag->s_scl)); prevdecl(-1, tag); tag = pushdown(tag); dcs->d_nxt->d_nedecl = 1; } else if (semi || decl) { dcs->d_nxt->d_nedecl = 1; } } return (tag); } const char * scltoa(scl_t sc) { const char *s; switch (sc) { case EXTERN: s = "extern"; break; case STATIC: s = "static"; break; case AUTO: s = "auto"; break; case REG: s = "register"; break; case TYPEDEF: s = "typedef"; break; case STRTAG: s = "struct"; break; case UNIONTAG: s = "union"; break; case ENUMTAG: s = "enum"; break; default: LERROR("tagttoa()"); } return (s); } /* * Completes the type of a tag in a struct/union/enum declaration. * tp points to the type of the, tag, fmem to the list of members/enums. */ type_t * compltag(type_t *tp, sym_t *fmem) { tspec_t t; str_t *sp; int n; sym_t *mem; /* from now a complete type */ setcompl(tp, 0); if ((t = tp->t_tspec) != ENUM) { align(dcs->d_stralign, 0); sp = tp->t_str; sp->align = dcs->d_stralign; sp->size = dcs->d_offset; sp->memb = fmem; if (sp->size == 0) { /* zero sized %s */ (void)c99ism(47, ttab[t].tt_name); } else { n = 0; for (mem = fmem; mem != NULL; mem = mem->s_nxt) { if (mem->s_name != unnamed) n++; } if (n == 0) { /* %s has no named members */ warning(65, t == STRUCT ? "structure" : "union"); } } } else { tp->t_enum->elem = fmem; } return (tp); } /* * Processes the name of an enumerator in en enum declaration. * * sym points to the enumerator * val is the value of the enumerator * impl is 1 if the value of the enumerator was not explicit specified. */ sym_t * ename(sym_t *sym, int val, int impl) { if (sym->s_scl) { if (sym->s_blklev == blklev) { /* no hflag, because this is illegal!!! */ if (sym->s_arg) { /* enumeration constant hides parameter: %s */ warning(57, sym->s_name); } else { /* redeclaration of %s */ error(27, sym->s_name); /* * inside blocks it should not too complicated * to find the position of the previous * declaration */ if (blklev == 0) prevdecl(-1, sym); } } else { if (hflag) /* redefinition hides earlier one: %s */ warning(43, sym->s_name); } sym = pushdown(sym); } sym->s_scl = ENUMCON; sym->s_type = dcs->d_tagtyp; sym->s_value.v_tspec = INT; sym->s_value.v_quad = val; if (impl && val - 1 == INT_MAX) { /* overflow in enumeration values: %s */ warning(48, sym->s_name); } enumval = val + 1; return (sym); } /* * Process a single external declarator. */ void decl1ext(sym_t *dsym, int initflg) { int warn, rval, redec; sym_t *rdsym; chkfdef(dsym, 1); chktyp(dsym); if (initflg && !(initerr = chkinit(dsym))) dsym->s_def = DEF; /* * Declarations of functions are marked as "tentative" in dname(). * This is wrong because there are no tentative function * definitions. */ if (dsym->s_type->t_tspec == FUNC && dsym->s_def == TDEF) dsym->s_def = DECL; if (dcs->d_inline) { if (dsym->s_type->t_tspec == FUNC) { dsym->s_inline = 1; } else { /* variable declared inline: %s */ warning(268, dsym->s_name); } } /* Write the declaration into the output file */ if (plibflg && llibflg && dsym->s_type->t_tspec == FUNC && dsym->s_type->t_proto) { /* - * With both LINTLIBRARY and PROTOLIB the prototyp is + * With both LINTLIBRARY and PROTOLIB the prototype is * written as a function definition to the output file. */ rval = dsym->s_type->t_subt->t_tspec != VOID; outfdef(dsym, &dsym->s_dpos, rval, 0, NULL); } else { outsym(dsym, dsym->s_scl, dsym->s_def); } if ((rdsym = dcs->d_rdcsym) != NULL) { /* * If the old symbol stems from an old style function definition * we have remembered the params in rdsmy->s_args and compare * them with the params of the prototype. */ if (rdsym->s_osdef && dsym->s_type->t_proto) { redec = chkosdef(rdsym, dsym); } else { redec = 0; } if (!redec && !isredec(dsym, (warn = 0, &warn))) { if (warn) { /* redeclaration of %s */ (*(sflag ? error : warning))(27, dsym->s_name); prevdecl(-1, rdsym); } /* - * Overtake the rememberd params if the new symbol + * Overtake the remembered params if the new symbol * is not a prototype. */ if (rdsym->s_osdef && !dsym->s_type->t_proto) { dsym->s_osdef = rdsym->s_osdef; dsym->s_args = rdsym->s_args; STRUCT_ASSIGN(dsym->s_dpos, rdsym->s_dpos); } /* * Remember the position of the declaration if the * old symbol was a prototype and the new is not. * Also remember the position if the old symbol * was defined and the new is not. */ if (rdsym->s_type->t_proto && !dsym->s_type->t_proto) { STRUCT_ASSIGN(dsym->s_dpos, rdsym->s_dpos); } else if (rdsym->s_def == DEF && dsym->s_def != DEF) { STRUCT_ASSIGN(dsym->s_dpos, rdsym->s_dpos); } /* * Copy informations about usage of the name into * the new symbol. */ cpuinfo(dsym, rdsym); /* Once a name is defined, it remains defined. */ if (rdsym->s_def == DEF) dsym->s_def = DEF; /* once a function is inline, it remains inline */ if (rdsym->s_inline) dsym->s_inline = 1; compltyp(dsym, rdsym); } rmsym(rdsym); } if (dsym->s_scl == TYPEDEF) { dsym->s_type = duptyp(dsym->s_type); dsym->s_type->t_typedef = 1; settdsym(dsym->s_type, dsym); } } /* * Copies informations about usage into a new symbol table entry of * the same symbol. */ void cpuinfo(sym_t *sym, sym_t *rdsym) { sym->s_spos = rdsym->s_spos; sym->s_upos = rdsym->s_upos; sym->s_set = rdsym->s_set; sym->s_used = rdsym->s_used; } /* * Prints an error and returns 1 if a symbol is redeclared/redefined. * Otherwise returns 0 and, in some cases of minor problems, prints * a warning. */ int isredec(sym_t *dsym, int *warn) { sym_t *rsym; if ((rsym = dcs->d_rdcsym)->s_scl == ENUMCON) { /* redeclaration of %s */ error(27, dsym->s_name); prevdecl(-1, rsym); return (1); } if (rsym->s_scl == TYPEDEF) { /* typedef redeclared: %s */ error(89, dsym->s_name); prevdecl(-1, rsym); return (1); } if (dsym->s_scl == TYPEDEF) { /* redeclaration of %s */ error(27, dsym->s_name); prevdecl(-1, rsym); return (1); } if (rsym->s_def == DEF && dsym->s_def == DEF) { /* redefinition of %s */ error(28, dsym->s_name); prevdecl(-1, rsym); return(1); } if (!eqtype(rsym->s_type, dsym->s_type, 0, 0, warn)) { /* redeclaration of %s */ error(27, dsym->s_name); prevdecl(-1, rsym); return(1); } if (rsym->s_scl == EXTERN && dsym->s_scl == EXTERN) return(0); if (rsym->s_scl == STATIC && dsym->s_scl == STATIC) return(0); if (rsym->s_scl == STATIC && dsym->s_def == DECL) return(0); if (rsym->s_scl == EXTERN && rsym->s_def == DEF) { /* - * All cases except "int a = 1; static int a;" are catched + * All cases except "int a = 1; static int a;" are caught * above with or without a warning */ /* redeclaration of %s */ error(27, dsym->s_name); prevdecl(-1, rsym); return(1); } if (rsym->s_scl == EXTERN) { /* previously declared extern, becomes static: %s */ warning(29, dsym->s_name); prevdecl(-1, rsym); return(0); } /* * Now its on of: * "static a; int a;", "static a; int a = 1;", "static a = 1; int a;" */ /* redeclaration of %s; ANSI C requires "static" */ if (sflag) { warning(30, dsym->s_name); prevdecl(-1, rsym); } dsym->s_scl = STATIC; return (0); } /* * Checks if two types are compatible. Returns 0 if not, otherwise 1. * * ignqual ignore qualifiers of type; used for function params * promot promote left type; used for comparison of params of * old style function definitions with params of prototypes. * *warn set to 1 if an old style function declaration is not * compatible with a prototype */ int eqtype(type_t *tp1, type_t *tp2, int ignqual, int promot, int *warn) { tspec_t t; while (tp1 != NULL && tp2 != NULL) { t = tp1->t_tspec; if (promot) { if (t == FLOAT) { t = DOUBLE; } else if (t == CHAR || t == SCHAR) { t = INT; } else if (t == UCHAR) { t = tflag ? UINT : INT; } else if (t == SHORT) { t = INT; } else if (t == USHORT) { /* CONSTCOND */ t = INT_MAX < USHRT_MAX || tflag ? UINT : INT; } } if (t != tp2->t_tspec) return (0); if (tp1->t_const != tp2->t_const && !ignqual && !tflag) return (0); if (tp1->t_volatile != tp2->t_volatile && !ignqual && !tflag) return (0); if (t == STRUCT || t == UNION) return (tp1->t_str == tp2->t_str); if (t == ARRAY && tp1->t_dim != tp2->t_dim) { if (tp1->t_dim != 0 && tp2->t_dim != 0) return (0); } /* dont check prototypes for traditional */ if (t == FUNC && !tflag) { if (tp1->t_proto && tp2->t_proto) { if (!eqargs(tp1, tp2, warn)) return (0); } else if (tp1->t_proto) { if (!mnoarg(tp1, warn)) return (0); } else if (tp2->t_proto) { if (!mnoarg(tp2, warn)) return (0); } } tp1 = tp1->t_subt; tp2 = tp2->t_subt; ignqual = promot = 0; } return (tp1 == tp2); } /* * Compares the parameter types of two prototypes. */ static int eqargs(type_t *tp1, type_t *tp2, int *warn) { sym_t *a1, *a2; if (tp1->t_vararg != tp2->t_vararg) return (0); a1 = tp1->t_args; a2 = tp2->t_args; while (a1 != NULL && a2 != NULL) { if (eqtype(a1->s_type, a2->s_type, 1, 0, warn) == 0) return (0); a1 = a1->s_nxt; a2 = a2->s_nxt; } return (a1 == a2); } /* * mnoarg() (matches functions with no argument type information) * returns 1 if all parameters of a prototype are compatible with * and old style function declaration. * This is the case if following conditions are met: * 1. the prototype must have a fixed number of parameters * 2. no parameter is of type float * 3. no parameter is converted to another type if integer promotion * is applied on it */ static int mnoarg(type_t *tp, int *warn) { sym_t *arg; tspec_t t; if (tp->t_vararg) { if (warn != NULL) *warn = 1; } for (arg = tp->t_args; arg != NULL; arg = arg->s_nxt) { if ((t = arg->s_type->t_tspec) == FLOAT || t == CHAR || t == SCHAR || t == UCHAR || t == SHORT || t == USHORT) { if (warn != NULL) *warn = 1; } } return (1); } /* * Compares a prototype declaration with the remembered arguments of * a previous old style function definition. */ static int chkosdef(sym_t *rdsym, sym_t *dsym) { sym_t *args, *pargs, *arg, *parg; int narg, nparg, n; int warn, msg; args = rdsym->s_args; pargs = dsym->s_type->t_args; msg = 0; narg = nparg = 0; for (arg = args; arg != NULL; arg = arg->s_nxt) narg++; for (parg = pargs; parg != NULL; parg = parg->s_nxt) nparg++; if (narg != nparg) { /* prototype does not match old-style definition */ error(63); msg = 1; goto end; } arg = args; parg = pargs; n = 1; while (narg--) { warn = 0; /* * If it does not match due to promotion and sflag is * not set we print only a warning. */ if (!eqtype(arg->s_type, parg->s_type, 1, 1, &warn) || warn) { /* prototype does not match old-style def., arg #%d */ error(299, n); msg = 1; } arg = arg->s_nxt; parg = parg->s_nxt; n++; } end: if (msg) /* old style definition */ prevdecl(300, rdsym); return (msg); } /* - * Complets a type by copying the dimension and prototype information + * Completes a type by copying the dimension and prototype information * from a second compatible type. * * Following lines are legal: * "typedef a[]; a b; a b[10]; a c; a c[20];" * "typedef ft(); ft f; f(int); ft g; g(long);" * This means that, if a type is completed, the type structure must * be duplicated. */ void compltyp(sym_t *dsym, sym_t *ssym) { type_t **dstp, *src; type_t *dst; dstp = &dsym->s_type; src = ssym->s_type; while ((dst = *dstp) != NULL) { if (src == NULL || dst->t_tspec != src->t_tspec) LERROR("compltyp()"); if (dst->t_tspec == ARRAY) { if (dst->t_dim == 0 && src->t_dim != 0) { *dstp = dst = duptyp(dst); dst->t_dim = src->t_dim; - /* now a complete Typ */ + /* now a complete type */ setcompl(dst, 0); } } else if (dst->t_tspec == FUNC) { if (!dst->t_proto && src->t_proto) { *dstp = dst = duptyp(dst); dst->t_proto = 1; dst->t_args = src->t_args; } } dstp = &dst->t_subt; src = src->t_subt; } } /* * Completes the declaration of a single argument. */ sym_t * decl1arg(sym_t *sym, int initflg) { tspec_t t; chkfdef(sym, 1); chktyp(sym); if (dcs->d_rdcsym != NULL && dcs->d_rdcsym->s_blklev == blklev) { /* redeclaration of formal parameter %s */ error(237, sym->s_name); rmsym(dcs->d_rdcsym); sym->s_arg = 1; } if (!sym->s_arg) { /* declared argument %s is missing */ error(53, sym->s_name); sym->s_arg = 1; } if (initflg) { /* cannot initialize parameter: %s */ error(52, sym->s_name); initerr = 1; } if ((t = sym->s_type->t_tspec) == ARRAY) { sym->s_type = incref(sym->s_type->t_subt, PTR); } else if (t == FUNC) { if (tflag) /* a function is declared as an argument: %s */ warning(50, sym->s_name); sym->s_type = incref(sym->s_type, PTR); } else if (t == FLOAT) { if (tflag) sym->s_type = gettyp(DOUBLE); } if (dcs->d_inline) /* argument declared inline: %s */ warning(269, sym->s_name); /* * Arguments must have complete types. lengths() prints the needed * error messages (null dimension is impossible because arrays are * converted to pointers). */ if (sym->s_type->t_tspec != VOID) (void)length(sym->s_type, sym->s_name); setsflg(sym); return (sym); } /* * Does some checks for lint directives which apply to functions. * Processes arguments in old style function definitions which default * to int. - * Checks compatiblility of old style function definition with previous + * Checks compatibility of old style function definition with previous * prototype. */ void cluparg(void) { sym_t *args, *arg, *pargs, *parg; int narg, nparg, n, msg; tspec_t t; args = funcsym->s_args; pargs = funcsym->s_type->t_args; /* check for illegal combinations of lint directives */ if (prflstrg != -1 && scflstrg != -1) { /* can't be used together: ** PRINTFLIKE ** ** SCANFLIKE ** */ warning(289); prflstrg = scflstrg = -1; } if (nvararg != -1 && (prflstrg != -1 || scflstrg != -1)) { /* dubious use of ** VARARGS ** with ** %s ** */ warning(288, prflstrg != -1 ? "PRINTFLIKE" : "SCANFLIKE"); nvararg = -1; } /* * check if the argument of a lint directive is compatible with the * number of arguments. */ narg = 0; for (arg = dcs->d_fargs; arg != NULL; arg = arg->s_nxt) narg++; if (nargusg > narg) { /* argument number mismatch with directive: ** %s ** */ warning(283, "ARGSUSED"); nargusg = 0; } if (nvararg > narg) { /* argument number mismatch with directive: ** %s ** */ warning(283, "VARARGS"); nvararg = 0; } if (prflstrg > narg) { /* argument number mismatch with directive: ** %s ** */ warning(283, "PRINTFLIKE"); prflstrg = -1; } else if (prflstrg == 0) { prflstrg = -1; } if (scflstrg > narg) { /* argument number mismatch with directive: ** %s ** */ warning(283, "SCANFLIKE"); scflstrg = -1; } else if (scflstrg == 0) { scflstrg = -1; } if (prflstrg != -1 || scflstrg != -1) { narg = prflstrg != -1 ? prflstrg : scflstrg; arg = dcs->d_fargs; for (n = 1; n < narg; n++) arg = arg->s_nxt; if (arg->s_type->t_tspec != PTR || ((t = arg->s_type->t_subt->t_tspec) != CHAR && t != UCHAR && t != SCHAR)) { /* arg. %d must be 'char *' for PRINTFLIKE/SCANFLIKE */ warning(293, narg); prflstrg = scflstrg = -1; } } /* - * print a warning for each argument off an old style function + * print a warning for each argument of an old style function * definition which defaults to int */ for (arg = args; arg != NULL; arg = arg->s_nxt) { if (arg->s_defarg) { /* argument type defaults to int: %s */ warning(32, arg->s_name); arg->s_defarg = 0; setsflg(arg); } } /* * If this is an old style function definition and a prototyp * exists, compare the types of arguments. */ if (funcsym->s_osdef && funcsym->s_type->t_proto) { /* * If the number of arguments does not macht, we need not * continue. */ narg = nparg = 0; msg = 0; for (parg = pargs; parg != NULL; parg = parg->s_nxt) nparg++; for (arg = args; arg != NULL; arg = arg->s_nxt) narg++; if (narg != nparg) { /* parameter mismatch: %d declared, %d defined */ error(51, nparg, narg); msg = 1; } else { parg = pargs; arg = args; while (narg--) { msg |= chkptdecl(arg, parg); parg = parg->s_nxt; arg = arg->s_nxt; } } if (msg) /* prototype declaration */ prevdecl(285, dcs->d_rdcsym); /* from now the prototype is valid */ funcsym->s_osdef = 0; funcsym->s_args = NULL; } } /* * Checks compatibility of an old style function definition with a previous * prototype declaration. * Returns 1 if the position of the previous declaration should be reported. */ static int chkptdecl(sym_t *arg, sym_t *parg) { type_t *tp, *ptp; int warn, msg; tp = arg->s_type; ptp = parg->s_type; msg = 0; warn = 0; if (!eqtype(tp, ptp, 1, 1, &warn)) { if (eqtype(tp, ptp, 1, 0, &warn)) { /* type does not match prototype: %s */ msg = gnuism(58, arg->s_name); } else { /* type does not match prototype: %s */ error(58, arg->s_name); msg = 1; } } else if (warn) { /* type does not match prototype: %s */ (*(sflag ? error : warning))(58, arg->s_name); msg = 1; } return (msg); } /* * Completes a single local declaration/definition. */ void decl1loc(sym_t *dsym, int initflg) { /* Correct a mistake done in dname(). */ if (dsym->s_type->t_tspec == FUNC) { dsym->s_def = DECL; if (dcs->d_scl == NOSCL) dsym->s_scl = EXTERN; } if (dsym->s_type->t_tspec == FUNC) { if (dsym->s_scl == STATIC) { /* dubious static function at block level: %s */ warning(93, dsym->s_name); dsym->s_scl = EXTERN; } else if (dsym->s_scl != EXTERN && dsym->s_scl != TYPEDEF) { /* function has illegal storage class: %s */ error(94, dsym->s_name); dsym->s_scl = EXTERN; } } /* * functions may be declared inline at local scope, although * this has no effect for a later definition of the same * function. * XXX it should have an effect if tflag is set. this would * also be the way gcc behaves. */ if (dcs->d_inline) { if (dsym->s_type->t_tspec == FUNC) { dsym->s_inline = 1; } else { /* variable declared inline: %s */ warning(268, dsym->s_name); } } chkfdef(dsym, 1); chktyp(dsym); if (dcs->d_rdcsym != NULL && dsym->s_scl == EXTERN) ledecl(dsym); if (dsym->s_scl == EXTERN) { /* * XXX wenn die statische Variable auf Ebene 0 erst * spaeter definiert wird, haben wir die Brille auf. */ if (dsym->s_xsym == NULL) { outsym(dsym, EXTERN, dsym->s_def); } else { outsym(dsym, dsym->s_xsym->s_scl, dsym->s_def); } } if (dcs->d_rdcsym != NULL) { if (dcs->d_rdcsym->s_blklev == 0) { switch (dsym->s_scl) { case AUTO: /* automatic hides external declaration: %s */ if (hflag) warning(86, dsym->s_name); break; case STATIC: /* static hides external declaration: %s */ if (hflag) warning(87, dsym->s_name); break; case TYPEDEF: /* typedef hides external declaration: %s */ if (hflag) warning(88, dsym->s_name); break; case EXTERN: /* * Warnings and errors are printed in ledecl() */ break; default: LERROR("decl1loc()"); } } else if (dcs->d_rdcsym->s_blklev == blklev) { /* no hflag, because its illegal! */ if (dcs->d_rdcsym->s_arg) { /* * if !tflag, a "redeclaration of %s" error * is produced below */ if (tflag) { if (hflag) /* decl. hides parameter: %s */ warning(91, dsym->s_name); rmsym(dcs->d_rdcsym); } } } else if (dcs->d_rdcsym->s_blklev < blklev) { if (hflag) /* declaration hides earlier one: %s */ warning(95, dsym->s_name); } if (dcs->d_rdcsym->s_blklev == blklev) { /* redeclaration of %s */ error(27, dsym->s_name); rmsym(dcs->d_rdcsym); } } if (initflg && !(initerr = chkinit(dsym))) { dsym->s_def = DEF; setsflg(dsym); } if (dsym->s_scl == TYPEDEF) { dsym->s_type = duptyp(dsym->s_type); dsym->s_type->t_typedef = 1; settdsym(dsym->s_type, dsym); } /* * Before we can check the size we must wait for an initialisation * which may follow. */ } /* * Processes (re)declarations of external Symbols inside blocks. */ static void ledecl(sym_t *dsym) { int eqt, warn; sym_t *esym; /* look for a symbol with the same name */ esym = dcs->d_rdcsym; while (esym != NULL && esym->s_blklev != 0) { while ((esym = esym->s_link) != NULL) { if (esym->s_kind != FVFT) continue; if (strcmp(dsym->s_name, esym->s_name) == 0) break; } } if (esym == NULL) return; if (esym->s_scl != EXTERN && esym->s_scl != STATIC) { /* gcc accepts this without a warning, pcc prints an error. */ /* redeclaration of %s */ warning(27, dsym->s_name); prevdecl(-1, esym); return; } warn = 0; eqt = eqtype(esym->s_type, dsym->s_type, 0, 0, &warn); if (!eqt || warn) { if (esym->s_scl == EXTERN) { /* inconsistent redeclaration of extern: %s */ warning(90, dsym->s_name); prevdecl(-1, esym); } else { /* inconsistent redeclaration of static: %s */ warning(92, dsym->s_name); prevdecl(-1, esym); } } if (eqt) { /* * Remember the external symbol so we can update usage * information at the end of the block. */ dsym->s_xsym = esym; } } /* * Print an error or a warning if the symbol can't be initialized due * to type/storage class. Return value is 1 if an error has been * detected. */ static int chkinit(sym_t *sym) { int err; err = 0; if (sym->s_type->t_tspec == FUNC) { /* cannot initialize function: %s */ error(24, sym->s_name); err = 1; } else if (sym->s_scl == TYPEDEF) { /* cannot initialize typedef: %s */ error(25, sym->s_name); err = 1; } else if (sym->s_scl == EXTERN && sym->s_def == DECL) { /* cannot initialize "extern" declaration: %s */ if (dcs->d_ctx == EXTERN) { warning(26, sym->s_name); } else { error(26, sym->s_name); err = 1; } } return (err); } /* * Create a symbol for an abstract declaration. */ sym_t * aname(void) { sym_t *sym; if (dcs->d_ctx != ABSTRACT && dcs->d_ctx != PARG) LERROR("aname()"); sym = getblk(sizeof (sym_t)); sym->s_name = unnamed; sym->s_def = DEF; sym->s_scl = ABSTRACT; sym->s_blklev = -1; if (dcs->d_ctx == PARG) sym->s_arg = 1; sym->s_type = dcs->d_type; dcs->d_rdcsym = NULL; dcs->d_vararg = 0; return (sym); } /* * Removes anything which has nothing to do on global level. */ void globclup(void) { while (dcs->d_nxt != NULL) popdecl(); cleanup(); blklev = 0; mblklev = 0; /* - * remove all informations about pending lint directives without + * remove all information about pending lint directives without * warnings. */ glclup(1); } /* * Process an abstract type declaration */ sym_t * decl1abs(sym_t *sym) { chkfdef(sym, 1); chktyp(sym); return (sym); } /* * Checks size after declarations of variables and their initialisation. */ void chksz(sym_t *dsym) { /* * check size only for symbols which are defined and no function and * not typedef name */ if (dsym->s_def != DEF) return; if (dsym->s_scl == TYPEDEF) return; if (dsym->s_type->t_tspec == FUNC) return; if (length(dsym->s_type, dsym->s_name) == 0 && dsym->s_type->t_tspec == ARRAY && dsym->s_type->t_dim == 0) { /* empty array declaration: %s */ if (tflag) { warning(190, dsym->s_name); } else { error(190, dsym->s_name); } } } /* * Mark an object as set if it is not already */ void setsflg(sym_t *sym) { if (!sym->s_set) { sym->s_set = 1; UNIQUE_CURR_POS(sym->s_spos); } } /* * Mark an object as used if it is not already */ void setuflg(sym_t *sym, int fcall, int szof) { if (!sym->s_used) { sym->s_used = 1; UNIQUE_CURR_POS(sym->s_upos); } /* * for function calls another record is written * * XXX Should symbols used in sizeof() treated as used or not? * Probably not, because there is no sense to declare an * external variable only to get their size. */ if (!fcall && !szof && sym->s_kind == FVFT && sym->s_scl == EXTERN) outusg(sym); } /* * Prints warnings for a list of variables and labels (concatenated * with s_dlnxt) if these are not used or only set. */ void chkusage(dinfo_t *di) { sym_t *sym; int mknowarn; /* for this warnings LINTED has no effect */ mknowarn = nowarn; nowarn = 0; for (sym = di->d_dlsyms; sym != NULL; sym = sym->s_dlnxt) chkusg1(di->d_asm, sym); nowarn = mknowarn; } /* * Prints a warning for a single variable or label if it is not used or * only set. */ void chkusg1(int novar, sym_t *sym) { pos_t cpos; if (sym->s_blklev == -1) return; STRUCT_ASSIGN(cpos, curr_pos); if (sym->s_kind == FVFT) { if (sym->s_arg) { chkausg(novar, sym); } else { chkvusg(novar, sym); } } else if (sym->s_kind == FLAB) { chklusg(sym); } else if (sym->s_kind == FTAG) { chktusg(sym); } STRUCT_ASSIGN(curr_pos, cpos); } static void chkausg(int novar, sym_t *arg) { if (!arg->s_set) LERROR("chkausg()"); if (novar) return; if (!arg->s_used && vflag) { STRUCT_ASSIGN(curr_pos, arg->s_dpos); /* argument %s unused in function %s */ warning(231, arg->s_name, funcsym->s_name); } } static void chkvusg(int novar, sym_t *sym) { scl_t sc; sym_t *xsym; if (blklev == 0 || sym->s_blklev == 0) LERROR("chkvusg()"); /* errors in expressions easily cause lots of these warnings */ if (nerr != 0) return; /* * XXX Only variables are checkd, although types should * probably also be checked */ if ((sc = sym->s_scl) != EXTERN && sc != STATIC && sc != AUTO && sc != REG) { return; } if (novar) return; if (sc == EXTERN) { if (!sym->s_used && !sym->s_set) { STRUCT_ASSIGN(curr_pos, sym->s_dpos); /* %s unused in function %s */ warning(192, sym->s_name, funcsym->s_name); } } else { if (sym->s_set && !sym->s_used) { STRUCT_ASSIGN(curr_pos, sym->s_spos); /* %s set but not used in function %s */ warning(191, sym->s_name, funcsym->s_name); } else if (!sym->s_used) { STRUCT_ASSIGN(curr_pos, sym->s_dpos); /* %s unused in function %s */ warning(192, sym->s_name, funcsym->s_name); } } if (sc == EXTERN) { /* * information about usage is taken over into the symbol * tabel entry at level 0 if the symbol was locally declared * as an external symbol. * * XXX This is wrong for symbols declared static at level 0 * if the usage information stems from sizeof(). This is * because symbols at level 0 only used in sizeof() are * considered to not be used. */ if ((xsym = sym->s_xsym) != NULL) { if (sym->s_used && !xsym->s_used) { xsym->s_used = 1; STRUCT_ASSIGN(xsym->s_upos, sym->s_upos); } if (sym->s_set && !xsym->s_set) { xsym->s_set = 1; STRUCT_ASSIGN(xsym->s_spos, sym->s_spos); } } } } static void chklusg(sym_t *lab) { if (blklev != 1 || lab->s_blklev != 1) LERROR("chklusg()"); if (lab->s_set && !lab->s_used) { STRUCT_ASSIGN(curr_pos, lab->s_spos); /* label %s unused in function %s */ warning(192, lab->s_name, funcsym->s_name); } else if (!lab->s_set) { STRUCT_ASSIGN(curr_pos, lab->s_upos); /* undefined label %s */ warning(23, lab->s_name); } } static void chktusg(sym_t *sym) { if (!incompl(sym->s_type)) return; /* complain always about incomplete tags declared inside blocks */ if (!zflag || dcs->d_ctx != EXTERN) return; STRUCT_ASSIGN(curr_pos, sym->s_dpos); switch (sym->s_type->t_tspec) { case STRUCT: /* struct %s never defined */ warning(233, sym->s_name); break; case UNION: /* union %s never defined */ warning(234, sym->s_name); break; case ENUM: /* enum %s never defined */ warning(235, sym->s_name); break; default: LERROR("chktusg()"); } } /* * Called after the entire translation unit has been parsed. * Changes tentative definitions in definitions. * Performs some tests on global Symbols. Detected Problems are: * - defined variables of incomplete type * - constant variables which are not initialized * - static symbols which are never used */ void chkglsyms(void) { sym_t *sym; pos_t cpos; if (blklev != 0 || dcs->d_nxt != NULL) norecover(); STRUCT_ASSIGN(cpos, curr_pos); for (sym = dcs->d_dlsyms; sym != NULL; sym = sym->s_dlnxt) { if (sym->s_blklev == -1) continue; if (sym->s_kind == FVFT) { chkglvar(sym); } else if (sym->s_kind == FTAG) { chktusg(sym); } else { if (sym->s_kind != FMOS) LERROR("chkglsyms()"); } } STRUCT_ASSIGN(curr_pos, cpos); } static void chkglvar(sym_t *sym) { if (sym->s_scl == TYPEDEF || sym->s_scl == ENUMCON) return; if (sym->s_scl != EXTERN && sym->s_scl != STATIC) LERROR("chkglvar()"); glchksz(sym); if (sym->s_scl == STATIC) { if (sym->s_type->t_tspec == FUNC) { if (sym->s_used && sym->s_def != DEF) { STRUCT_ASSIGN(curr_pos, sym->s_upos); /* static func. called but not def.. */ error(225, sym->s_name); } } if (!sym->s_used) { STRUCT_ASSIGN(curr_pos, sym->s_dpos); if (sym->s_type->t_tspec == FUNC) { if (sym->s_def == DEF) { if (!sym->s_inline) /* static function %s unused */ warning(236, sym->s_name); } else { /* static function %s decl. but ... */ warning(290, sym->s_name); } } else if (!sym->s_set) { /* static variable %s unused */ warning(226, sym->s_name); } else { /* static variable %s set but not used */ warning(307, sym->s_name); } } if (!tflag && sym->s_def == TDEF && sym->s_type->t_const) { STRUCT_ASSIGN(curr_pos, sym->s_dpos); /* const object %s should have initializer */ warning(227, sym->s_name); } } } static void glchksz(sym_t *sym) { if (sym->s_def == TDEF) { if (sym->s_type->t_tspec == FUNC) /* * this can happen if a syntax error occurred * after a function declaration */ return; STRUCT_ASSIGN(curr_pos, sym->s_dpos); if (length(sym->s_type, sym->s_name) == 0 && sym->s_type->t_tspec == ARRAY && sym->s_type->t_dim == 0) { /* empty array declaration: %s */ if (tflag || (sym->s_scl == EXTERN && !sflag)) { warning(190, sym->s_name); } else { error(190, sym->s_name); } } } } /* * Prints information about location of previous definition/declaration. */ void prevdecl(int msg, sym_t *psym) { pos_t cpos; if (!rflag) return; STRUCT_ASSIGN(cpos, curr_pos); STRUCT_ASSIGN(curr_pos, psym->s_dpos); if (msg != -1) { message(msg, psym->s_name); } else if (psym->s_def == DEF || psym->s_def == TDEF) { /* previous definition of %s */ message(261, psym->s_name); } else { /* previous declaration of %s */ message(260, psym->s_name); } STRUCT_ASSIGN(curr_pos, cpos); } Index: head/usr.bin/xlint/lint1/func.c =================================================================== --- head/usr.bin/xlint/lint1/func.c (revision 286613) +++ head/usr.bin/xlint/lint1/func.c (revision 286614) @@ -1,1288 +1,1288 @@ /* $NetBSD: func.c,v 1.22 2005/09/24 15:30:35 perry Exp $ */ /* * Copyright (c) 1994, 1995 Jochen Pohl * All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Jochen Pohl for * The NetBSD Project. * 4. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #if defined(__RCSID) && !defined(lint) __RCSID("$NetBSD: func.c,v 1.16 2002/01/03 04:25:15 thorpej Exp $"); #endif __FBSDID("$FreeBSD$"); #include #include #include "lint1.h" #include "cgram.h" /* * Contains a pointer to the symbol table entry of the current function * definition. */ sym_t *funcsym; /* Is set as long as a statement can be reached. Must be set at level 0. */ int reached = 1; /* * Is set as long as NOTREACHED is in effect. * Is reset everywhere where reached can become 0. */ int rchflg; /* * In conjunction with reached controls printing of "fallthrough on ..." * warnings. * Reset by each statement and set by FALLTHROUGH, switch (switch1()) * and case (label()). * * Control statements if, for, while and switch do not reset ftflg because * this must be done by the controlled statement. At least for if this is * important because ** FALLTHROUGH ** after "if (expr) stmnt" is evaluated * before the following token, which causes reduction of above, is read. * This means that ** FALLTHROUGH ** after "if ..." would always be ignored. */ int ftflg; /* Top element of stack for control statements */ cstk_t *cstk; /* * Number of arguments which will be checked for usage in following * function definition. -1 stands for all arguments. * * The position of the last ARGSUSED comment is stored in aupos. */ int nargusg = -1; pos_t aupos; /* * Number of arguments of the following function definition whose types * shall be checked by lint2. -1 stands for all arguments. * * The position of the last VARARGS comment is stored in vapos. */ int nvararg = -1; pos_t vapos; /* * Both prflstr and scflstrg contain the number of the argument which * shall be used to check the types of remaining arguments (for PRINTFLIKE * and SCANFLIKE). * * prflpos and scflpos are the positions of the last PRINTFLIKE or * SCANFLIKE comment. */ int prflstrg = -1; int scflstrg = -1; pos_t prflpos; pos_t scflpos; /* * Are both plibflg and llibflg set, prototypes are written as function * definitions to the output file. */ int plibflg; /* * Nonzero means that no warnings about constants in conditional * context are printed. */ int ccflg; /* * llibflg is set if a lint library shall be created. The effect of * llibflg is that all defined symbols are treated as used. * (The LINTLIBRARY comment also resets vflag.) */ int llibflg; /* * Nonzero if warnings are suppressed by a LINTED directive */ int nowarn; /* * Nonzero if bitfield type errors are suppressed by a BITFIELDTYPE * directive. */ int bitfieldtype_ok; /* * Nonzero if complaints about use of "long long" are suppressed in * the next statement or declaration. */ int quadflg; /* * Puts a new element at the top of the stack used for control statements. */ void pushctrl(int env) { cstk_t *ci; if ((ci = calloc(1, sizeof (cstk_t))) == NULL) nomem(); ci->c_env = env; ci->c_nxt = cstk; cstk = ci; } /* * Removes the top element of the stack used for control statements. */ void popctrl(int env) { cstk_t *ci; clst_t *cl; if (cstk == NULL || cstk->c_env != env) LERROR("popctrl()"); cstk = (ci = cstk)->c_nxt; while ((cl = ci->c_clst) != NULL) { ci->c_clst = cl->cl_nxt; free(cl); } if (ci->c_swtype != NULL) free(ci->c_swtype); free(ci); } /* * Prints a warning if a statement cannot be reached. */ void chkreach(void) { if (!reached && !rchflg) { /* statement not reached */ warning(193); reached = 1; } } /* * Called after a function declaration which introduces a function definition * and before an (optional) old style argument declaration list. * * Puts all symbols declared in the Prototype or in an old style argument * list back to the symbol table. * * Does the usual checking of storage class, type (return value), * redeclaration etc.. */ void funcdef(sym_t *fsym) { int n, warn; sym_t *arg, *sym, *rdsym; funcsym = fsym; /* * Put all symbols declared in the argument list back to the * symbol table. */ for (sym = dcs->d_fpsyms; sym != NULL; sym = sym->s_dlnxt) { if (sym->s_blklev != -1) { if (sym->s_blklev != 1) LERROR("funcdef()"); inssym(1, sym); } } /* * In osfunc() we did not know whether it is an old style function * definition or only an old style declaration, if there are no * arguments inside the argument list ("f()"). */ if (!fsym->s_type->t_proto && fsym->s_args == NULL) fsym->s_osdef = 1; chktyp(fsym); /* * chktyp() checks for almost all possible errors, but not for * incomplete return values (these are allowed in declarations) */ if (fsym->s_type->t_subt->t_tspec != VOID && incompl(fsym->s_type->t_subt)) { /* cannot return incomplete type */ error(67); } fsym->s_def = DEF; if (fsym->s_scl == TYPEDEF) { fsym->s_scl = EXTERN; /* illegal storage class */ error(8); } if (dcs->d_inline) fsym->s_inline = 1; /* * Arguments in new style function declarations need a name. * (void is already removed from the list of arguments) */ n = 1; for (arg = fsym->s_type->t_args; arg != NULL; arg = arg->s_nxt) { if (arg->s_scl == ABSTRACT) { if (arg->s_name != unnamed) LERROR("funcdef()"); /* formal parameter lacks name: param #%d */ error(59, n); } else { if (arg->s_name == unnamed) LERROR("funcdef()"); } n++; } /* * We must also remember the position. s_dpos is overwritten * if this is an old style definition and we had already a * prototype. */ STRUCT_ASSIGN(dcs->d_fdpos, fsym->s_dpos); if ((rdsym = dcs->d_rdcsym) != NULL) { if (!isredec(fsym, (warn = 0, &warn))) { /* * Print nothing if the newly defined function * is defined in old style. A better warning will * be printed in cluparg(). */ if (warn && !fsym->s_osdef) { /* redeclaration of %s */ (*(sflag ? error : warning))(27, fsym->s_name); prevdecl(-1, rdsym); } /* copy usage information */ cpuinfo(fsym, rdsym); /* * If the old symbol was a prototype and the new * one is none, overtake the position of the * declaration of the prototype. */ if (fsym->s_osdef && rdsym->s_type->t_proto) STRUCT_ASSIGN(fsym->s_dpos, rdsym->s_dpos); /* complete the type */ compltyp(fsym, rdsym); /* once a function is inline it remains inline */ if (rdsym->s_inline) fsym->s_inline = 1; } /* remove the old symbol from the symbol table */ rmsym(rdsym); } if (fsym->s_osdef && !fsym->s_type->t_proto) { if (sflag && hflag && strcmp(fsym->s_name, "main") != 0) /* function definition is not a prototype */ warning(286); } if (dcs->d_notyp) /* return value is implicitly declared to be int */ fsym->s_rimpl = 1; reached = 1; } /* * Called at the end of a function definition. */ void funcend(void) { sym_t *arg; int n; if (reached) { cstk->c_noretval = 1; if (funcsym->s_type->t_subt->t_tspec != VOID && !funcsym->s_rimpl) { /* func. %s falls off bottom without returning value */ warning(217, funcsym->s_name); } } /* * This warning is printed only if the return value was implicitly * declared to be int. Otherwise the wrong return statement * has already printed a warning. */ if (cstk->c_noretval && cstk->c_retval && funcsym->s_rimpl) /* function %s has return (e); and return; */ warning(216, funcsym->s_name); /* Print warnings for unused arguments */ arg = dcs->d_fargs; n = 0; while (arg != NULL && (nargusg == -1 || n < nargusg)) { chkusg1(dcs->d_asm, arg); arg = arg->s_nxt; n++; } nargusg = -1; /* * write the information about the function definition to the * output file * inline functions explicitly declared extern are written as * declarations only. */ if (dcs->d_scl == EXTERN && funcsym->s_inline) { outsym(funcsym, funcsym->s_scl, DECL); } else { outfdef(funcsym, &dcs->d_fdpos, cstk->c_retval, funcsym->s_osdef, dcs->d_fargs); } /* * remove all symbols declared during argument declaration from * the symbol table */ if (dcs->d_nxt != NULL || dcs->d_ctx != EXTERN) LERROR("funcend()"); rmsyms(dcs->d_fpsyms); /* must be set on level 0 */ reached = 1; } /* * Process a label. * * typ type of the label (T_NAME, T_DEFAULT or T_CASE). * sym symbol table entry of label if typ == T_NAME * tn expression if typ == T_CASE */ void label(int typ, sym_t *sym, tnode_t *tn) { cstk_t *ci; clst_t *cl; val_t *v; val_t nv; tspec_t t; switch (typ) { case T_NAME: if (sym->s_set) { /* label %s redefined */ error(194, sym->s_name); } else { setsflg(sym); } break; case T_CASE: /* find the stack entry for the innermost switch statement */ for (ci = cstk; ci != NULL && !ci->c_switch; ci = ci->c_nxt) continue; if (ci == NULL) { /* case not in switch */ error(195); tn = NULL; } else if (tn != NULL && tn->tn_op != CON) { /* non-constant case expression */ error(197); tn = NULL; } else if (tn != NULL && !isityp(tn->tn_type->t_tspec)) { /* non-integral case expression */ error(198); tn = NULL; } if (tn != NULL) { if (ci->c_swtype == NULL) LERROR("label()"); if (reached && !ftflg) { if (hflag) /* fallthrough on case statement */ warning(220); } t = tn->tn_type->t_tspec; if (t == LONG || t == ULONG || t == QUAD || t == UQUAD) { if (tflag) /* case label must be of type ... */ warning(203); } /* * get the value of the expression and convert it * to the type of the switch expression */ v = constant(tn, 1); (void) memset(&nv, 0, sizeof nv); cvtcon(CASE, 0, ci->c_swtype, &nv, v); free(v); /* look if we had this value already */ for (cl = ci->c_clst; cl != NULL; cl = cl->cl_nxt) { if (cl->cl_val.v_quad == nv.v_quad) break; } if (cl != NULL && isutyp(nv.v_tspec)) { /* duplicate case in switch, %lu */ error(200, (u_long)nv.v_quad); } else if (cl != NULL) { /* duplicate case in switch, %ld */ error(199, (long)nv.v_quad); } else { /* * append the value to the list of * case values */ cl = xcalloc(1, sizeof (clst_t)); STRUCT_ASSIGN(cl->cl_val, nv); cl->cl_nxt = ci->c_clst; ci->c_clst = cl; } } tfreeblk(); break; case T_DEFAULT: /* find the stack entry for the innermost switch statement */ for (ci = cstk; ci != NULL && !ci->c_switch; ci = ci->c_nxt) continue; if (ci == NULL) { /* default outside switch */ error(201); } else if (ci->c_default) { /* duplicate default in switch */ error(202); } else { if (reached && !ftflg) { if (hflag) /* fallthrough on default statement */ warning(284); } ci->c_default = 1; } break; }; reached = 1; } /* * T_IF T_LPARN expr T_RPARN */ void if1(tnode_t *tn) { if (tn != NULL) tn = cconv(tn); if (tn != NULL) tn = promote(NOOP, 0, tn); expr(tn, 0, 1, 1); pushctrl(T_IF); } /* * if_without_else * if_without_else T_ELSE */ void if2(void) { cstk->c_rchif = reached ? 1 : 0; reached = 1; } /* * if_without_else * if_without_else T_ELSE stmnt */ void if3(int els) { if (els) { reached |= cstk->c_rchif; } else { reached = 1; } popctrl(T_IF); } /* * T_SWITCH T_LPARN expr T_RPARN */ void switch1(tnode_t *tn) { tspec_t t; type_t *tp; if (tn != NULL) tn = cconv(tn); if (tn != NULL) tn = promote(NOOP, 0, tn); if (tn != NULL && !isityp(tn->tn_type->t_tspec)) { /* switch expression must have integral type */ error(205); tn = NULL; } if (tn != NULL && tflag) { t = tn->tn_type->t_tspec; if (t == LONG || t == ULONG || t == QUAD || t == UQUAD) { /* switch expr. must be of type `int' in trad. C */ warning(271); } } /* * Remember the type of the expression. Because its possible * that (*tp) is allocated on tree memory the type must be * duplicated. This is not too complicated because it is * only an integer type. */ if ((tp = calloc(1, sizeof (type_t))) == NULL) nomem(); if (tn != NULL) { tp->t_tspec = tn->tn_type->t_tspec; if ((tp->t_isenum = tn->tn_type->t_isenum) != 0) tp->t_enum = tn->tn_type->t_enum; } else { tp->t_tspec = INT; } expr(tn, 1, 0, 1); pushctrl(T_SWITCH); cstk->c_switch = 1; cstk->c_swtype = tp; reached = rchflg = 0; ftflg = 1; } /* * switch_expr stmnt */ void switch2(void) { int nenum = 0, nclab = 0; sym_t *esym; clst_t *cl; if (cstk->c_swtype == NULL) LERROR("switch2()"); /* * If the switch expression was of type enumeration, count the case * labels and the number of enumerators. If both counts are not * equal print a warning. */ if (cstk->c_swtype->t_isenum) { nenum = nclab = 0; if (cstk->c_swtype->t_enum == NULL) LERROR("switch2()"); for (esym = cstk->c_swtype->t_enum->elem; esym != NULL; esym = esym->s_nxt) { nenum++; } for (cl = cstk->c_clst; cl != NULL; cl = cl->cl_nxt) nclab++; if (hflag && eflag && nenum != nclab && !cstk->c_default) { /* enumeration value(s) not handled in switch */ warning(206); } } if (cstk->c_break) { /* - * end of switch alway reached (c_break is only set if the + * end of switch always reached (c_break is only set if the * break statement can be reached). */ reached = 1; } else if (!cstk->c_default && (!hflag || !cstk->c_swtype->t_isenum || nenum != nclab)) { /* * there are possible values which are not handled in * switch */ reached = 1; } /* * otherwise the end of the switch expression is reached * if the end of the last statement inside it is reached. */ popctrl(T_SWITCH); } /* * T_WHILE T_LPARN expr T_RPARN */ void while1(tnode_t *tn) { if (!reached) { /* loop not entered at top */ warning(207); reached = 1; } if (tn != NULL) tn = cconv(tn); if (tn != NULL) tn = promote(NOOP, 0, tn); if (tn != NULL && !issclt(tn->tn_type->t_tspec)) { /* controlling expressions must have scalar type */ error(204); tn = NULL; } pushctrl(T_WHILE); cstk->c_loop = 1; if (tn != NULL && tn->tn_op == CON) { if (isityp(tn->tn_type->t_tspec)) { cstk->c_infinite = tn->tn_val->v_quad != 0; } else { cstk->c_infinite = tn->tn_val->v_ldbl != 0.0; } } expr(tn, 0, 1, 1); } /* * while_expr stmnt * while_expr error */ void while2(void) { /* * The end of the loop can be reached if it is no endless loop * or there was a break statement which was reached. */ reached = !cstk->c_infinite || cstk->c_break; rchflg = 0; popctrl(T_WHILE); } /* * T_DO */ void do1(void) { if (!reached) { /* loop not entered at top */ warning(207); reached = 1; } pushctrl(T_DO); cstk->c_loop = 1; } /* * do stmnt do_while_expr * do error */ void do2(tnode_t *tn) { /* * If there was a continue statement the expression controlling the * loop is reached. */ if (cstk->c_cont) reached = 1; if (tn != NULL) tn = cconv(tn); if (tn != NULL) tn = promote(NOOP, 0, tn); if (tn != NULL && !issclt(tn->tn_type->t_tspec)) { /* controlling expressions must have scalar type */ error(204); tn = NULL; } if (tn != NULL && tn->tn_op == CON) { if (isityp(tn->tn_type->t_tspec)) { cstk->c_infinite = tn->tn_val->v_quad != 0; } else { cstk->c_infinite = tn->tn_val->v_ldbl != 0.0; } } expr(tn, 0, 1, 1); /* * The end of the loop is only reached if it is no endless loop * or there was a break statement which could be reached. */ reached = !cstk->c_infinite || cstk->c_break; rchflg = 0; popctrl(T_DO); } /* * T_FOR T_LPARN opt_expr T_SEMI opt_expr T_SEMI opt_expr T_RPARN */ void for1(tnode_t *tn1, tnode_t *tn2, tnode_t *tn3) { /* * If there is no initialisation expression it is possible that * it is intended not to enter the loop at top. */ if (tn1 != NULL && !reached) { /* loop not entered at top */ warning(207); reached = 1; } pushctrl(T_FOR); cstk->c_loop = 1; /* * Store the tree memory for the reinitialisation expression. * Also remember this expression itself. We must check it at * the end of the loop to get "used but not set" warnings correct. */ cstk->c_fexprm = tsave(); cstk->c_f3expr = tn3; STRUCT_ASSIGN(cstk->c_fpos, curr_pos); STRUCT_ASSIGN(cstk->c_cfpos, csrc_pos); if (tn1 != NULL) expr(tn1, 0, 0, 1); if (tn2 != NULL) tn2 = cconv(tn2); if (tn2 != NULL) tn2 = promote(NOOP, 0, tn2); if (tn2 != NULL && !issclt(tn2->tn_type->t_tspec)) { /* controlling expressions must have scalar type */ error(204); tn2 = NULL; } if (tn2 != NULL) expr(tn2, 0, 1, 1); if (tn2 == NULL) { cstk->c_infinite = 1; } else if (tn2->tn_op == CON) { if (isityp(tn2->tn_type->t_tspec)) { cstk->c_infinite = tn2->tn_val->v_quad != 0; } else { cstk->c_infinite = tn2->tn_val->v_ldbl != 0.0; } } /* Checking the reinitialisation expression is done in for2() */ reached = 1; } /* * for_exprs stmnt * for_exprs error */ void for2(void) { pos_t cpos, cspos; tnode_t *tn3; if (cstk->c_cont) reached = 1; STRUCT_ASSIGN(cpos, curr_pos); STRUCT_ASSIGN(cspos, csrc_pos); /* Restore the tree memory for the reinitialisation expression */ trestor(cstk->c_fexprm); tn3 = cstk->c_f3expr; STRUCT_ASSIGN(curr_pos, cstk->c_fpos); STRUCT_ASSIGN(csrc_pos, cstk->c_cfpos); /* simply "statement not reached" would be confusing */ if (!reached && !rchflg) { /* end-of-loop code not reached */ warning(223); reached = 1; } if (tn3 != NULL) { expr(tn3, 0, 0, 1); } else { tfreeblk(); } STRUCT_ASSIGN(curr_pos, cpos); STRUCT_ASSIGN(csrc_pos, cspos); /* An endless loop without break will never terminate */ reached = cstk->c_break || !cstk->c_infinite; rchflg = 0; popctrl(T_FOR); } /* * T_GOTO identifier T_SEMI * T_GOTO error T_SEMI */ void dogoto(sym_t *lab) { setuflg(lab, 0, 0); chkreach(); reached = rchflg = 0; } /* * T_BREAK T_SEMI */ void dobreak(void) { cstk_t *ci; ci = cstk; while (ci != NULL && !ci->c_loop && !ci->c_switch) ci = ci->c_nxt; if (ci == NULL) { /* break outside loop or switch */ error(208); } else { if (reached) ci->c_break = 1; } if (bflag) chkreach(); reached = rchflg = 0; } /* * T_CONTINUE T_SEMI */ void docont(void) { cstk_t *ci; for (ci = cstk; ci != NULL && !ci->c_loop; ci = ci->c_nxt) continue; if (ci == NULL) { /* continue outside loop */ error(209); } else { ci->c_cont = 1; } chkreach(); reached = rchflg = 0; } /* * T_RETURN T_SEMI * T_RETURN expr T_SEMI */ void doreturn(tnode_t *tn) { tnode_t *ln, *rn; cstk_t *ci; op_t op; for (ci = cstk; ci->c_nxt != NULL; ci = ci->c_nxt) continue; if (tn != NULL) { ci->c_retval = 1; } else { ci->c_noretval = 1; } if (tn != NULL && funcsym->s_type->t_subt->t_tspec == VOID) { /* void function %s cannot return value */ error(213, funcsym->s_name); tfreeblk(); tn = NULL; } else if (tn == NULL && funcsym->s_type->t_subt->t_tspec != VOID) { /* * Assume that the function has a return value only if it * is explicitly declared. */ if (!funcsym->s_rimpl) /* function %s expects to return value */ warning(214, funcsym->s_name); } if (tn != NULL) { /* Create a temporary node for the left side */ ln = tgetblk(sizeof (tnode_t)); ln->tn_op = NAME; ln->tn_type = tduptyp(funcsym->s_type->t_subt); ln->tn_type->t_const = 0; ln->tn_lvalue = 1; ln->tn_sym = funcsym; /* better than nothing */ tn = build(RETURN, ln, tn); if (tn != NULL) { rn = tn->tn_right; while ((op = rn->tn_op) == CVT || op == PLUS) rn = rn->tn_left; if (rn->tn_op == AMPER && rn->tn_left->tn_op == NAME && rn->tn_left->tn_sym->s_scl == AUTO) { /* %s returns pointer to automatic object */ warning(302, funcsym->s_name); } } expr(tn, 1, 0, 1); } else { chkreach(); } reached = rchflg = 0; } /* * Do some cleanup after a global declaration or definition. * Especially remove informations about unused lint comments. */ void glclup(int silent) { pos_t cpos; STRUCT_ASSIGN(cpos, curr_pos); if (nargusg != -1) { if (!silent) { STRUCT_ASSIGN(curr_pos, aupos); /* must precede function definition: %s */ warning(282, "ARGSUSED"); } nargusg = -1; } if (nvararg != -1) { if (!silent) { STRUCT_ASSIGN(curr_pos, vapos); /* must precede function definition: %s */ warning(282, "VARARGS"); } nvararg = -1; } if (prflstrg != -1) { if (!silent) { STRUCT_ASSIGN(curr_pos, prflpos); /* must precede function definition: %s */ warning(282, "PRINTFLIKE"); } prflstrg = -1; } if (scflstrg != -1) { if (!silent) { STRUCT_ASSIGN(curr_pos, scflpos); /* must precede function definition: %s */ warning(282, "SCANFLIKE"); } scflstrg = -1; } STRUCT_ASSIGN(curr_pos, cpos); dcs->d_asm = 0; } /* * ARGSUSED comment * * Only the first n arguments of the following function are checked * for usage. A missing argument is taken to be 0. */ void argsused(int n) { if (n == -1) n = 0; if (dcs->d_ctx != EXTERN) { /* must be outside function: ** %s ** */ warning(280, "ARGSUSED"); return; } if (nargusg != -1) { /* duplicate use of ** %s ** */ warning(281, "ARGSUSED"); } nargusg = n; STRUCT_ASSIGN(aupos, curr_pos); } /* * VARARGS comment * * Makes that lint2 checks only the first n arguments for compatibility * to the function definition. A missing argument is taken to be 0. */ void varargs(int n) { if (n == -1) n = 0; if (dcs->d_ctx != EXTERN) { /* must be outside function: ** %s ** */ warning(280, "VARARGS"); return; } if (nvararg != -1) { /* duplicate use of ** %s ** */ warning(281, "VARARGS"); } nvararg = n; STRUCT_ASSIGN(vapos, curr_pos); } /* * PRINTFLIKE comment * * Check all arguments until the (n-1)-th as usual. The n-th argument is * used the check the types of remaining arguments. */ void printflike(int n) { if (n == -1) n = 0; if (dcs->d_ctx != EXTERN) { /* must be outside function: ** %s ** */ warning(280, "PRINTFLIKE"); return; } if (prflstrg != -1) { /* duplicate use of ** %s ** */ warning(281, "PRINTFLIKE"); } prflstrg = n; STRUCT_ASSIGN(prflpos, curr_pos); } /* * SCANFLIKE comment * * Check all arguments until the (n-1)-th as usual. The n-th argument is * used the check the types of remaining arguments. */ void scanflike(int n) { if (n == -1) n = 0; if (dcs->d_ctx != EXTERN) { /* must be outside function: ** %s ** */ warning(280, "SCANFLIKE"); return; } if (scflstrg != -1) { /* duplicate use of ** %s ** */ warning(281, "SCANFLIKE"); } scflstrg = n; STRUCT_ASSIGN(scflpos, curr_pos); } /* * Set the linenumber for a CONSTCOND comment. At this and the following * line no warnings about constants in conditional contexts are printed. */ /* ARGSUSED */ void constcond(int n) { ccflg = 1; } /* * Suppress printing of "fallthrough on ..." warnings until next * statement. */ /* ARGSUSED */ void fallthru(int n) { ftflg = 1; } /* * Stop warnings about statements which cannot be reached. Also tells lint * that the following statements cannot be reached (e.g. after exit()). */ /* ARGSUSED */ void notreach(int n) { reached = 0; rchflg = 1; } /* ARGSUSED */ void lintlib(int n) { if (dcs->d_ctx != EXTERN) { /* must be outside function: ** %s ** */ warning(280, "LINTLIBRARY"); return; } llibflg = 1; vflag = 0; } /* * Suppress most warnings at the current and the following line. */ /* ARGSUSED */ void linted(int n) { #ifdef DEBUG printf("%s, %d: nowarn = 1\n", curr_pos.p_file, curr_pos.p_line); #endif nowarn = 1; } /* * Suppress bitfield type errors on the current line. */ /* ARGSUSED */ void bitfieldtype(int n) { #ifdef DEBUG printf("%s, %d: bitfieldtype_ok = 1\n", curr_pos.p_file, curr_pos.p_line); #endif bitfieldtype_ok = 1; } /* * PROTOTLIB in conjunction with LINTLIBRARY can be used to handle * prototypes like function definitions. This is done if the argument * to PROTOLIB is nonzero. Otherwise prototypes are handled normaly. */ void protolib(int n) { if (dcs->d_ctx != EXTERN) { /* must be outside function: ** %s ** */ warning(280, "PROTOLIB"); return; } plibflg = n == 0 ? 0 : 1; } /* * Set quadflg to nonzero which means that the next statement/declaration * may use "long long" without an error or warning. */ /* ARGSUSED */ void longlong(int n) { quadflg = 1; } Index: head/usr.bin/xlint/lint1/init.c =================================================================== --- head/usr.bin/xlint/lint1/init.c (revision 286613) +++ head/usr.bin/xlint/lint1/init.c (revision 286614) @@ -1,656 +1,656 @@ /* $NetBSD: init.c,v 1.10 2002/01/31 19:36:54 tv Exp $ */ /* * Copyright (c) 1994, 1995 Jochen Pohl * All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Jochen Pohl for * The NetBSD Project. * 4. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #if defined(__RCSID) && !defined(lint) __RCSID("$NetBSD: init.c,v 1.10 2002/01/31 19:36:54 tv Exp $"); #endif __FBSDID("$FreeBSD$"); #include #include #include #include "lint1.h" /* * initerr is set as soon as a fatal error occurred in an initialisation. * The effect is that the rest of the initialisation is ignored (parsed * by yacc, expression trees built, but no initialisation takes place). */ int initerr; /* Pointer to the symbol which is to be initialized. */ sym_t *initsym; /* Points to the top element of the initialisation stack. */ istk_t *initstk; typedef struct namlist { const char *n_name; struct namlist *n_prev; struct namlist *n_next; } namlist_t; /* Points to a c9x named member; */ namlist_t *namedmem = NULL; static void popi2(void); static void popinit(int); static void pushinit(void); static void testinit(void); static void nextinit(int); static int strginit(tnode_t *); static void memberpop(void); #ifndef DEBUG #define DPRINTF(a) #else #define DPRINTF(a) printf a #endif void memberpush(sb) sbuf_t *sb; { namlist_t *nam = xcalloc(1, sizeof (namlist_t)); nam->n_name = sb->sb_name; DPRINTF(("memberpush = %s\n", nam->n_name)); if (namedmem == NULL) { nam->n_prev = nam->n_next = nam; namedmem = nam; } else { namedmem->n_prev->n_next = nam; nam->n_prev = namedmem->n_prev; nam->n_next = namedmem; namedmem->n_prev = nam; } #if 0 nam->n_next = namedmem; namedmem = nam; #endif } static void memberpop() { DPRINTF(("memberpop = %s\n", namedmem->n_name)); if (namedmem->n_next == namedmem) { free(namedmem); namedmem = NULL; } else { namlist_t *nam = namedmem; namedmem = namedmem->n_next; free(nam); } #if 0 namedmem = namedmem->n_next; free(nam); #endif } /* * Initialize the initialisation stack by putting an entry for the variable * which is to be initialized on it. */ void prepinit(void) { istk_t *istk; if (initerr) return; /* free memory used in last initialisation */ while ((istk = initstk) != NULL) { initstk = istk->i_nxt; free(istk); } /* * If the type which is to be initialized is an incomplete type, * it must be duplicated. */ if (initsym->s_type->t_tspec == ARRAY && incompl(initsym->s_type)) initsym->s_type = duptyp(initsym->s_type); istk = initstk = xcalloc(1, sizeof (istk_t)); istk->i_subt = initsym->s_type; istk->i_cnt = 1; } static void popi2(void) { #ifdef DEBUG char buf[64]; #endif istk_t *istk; sym_t *m; initstk = (istk = initstk)->i_nxt; if (initstk == NULL) LERROR("popi2()"); free(istk); istk = initstk; istk->i_cnt--; if (istk->i_cnt < 0) LERROR("popi2()"); DPRINTF(("popi2(): %d %s\n", istk->i_cnt, namedmem ? namedmem->n_name : "*null*")); if (istk->i_cnt >= 0 && namedmem != NULL) { DPRINTF(("popi2(): %d %s %s\n", istk->i_cnt, tyname(buf, sizeof(buf), istk->i_type), namedmem->n_name)); for (m = istk->i_type->t_str->memb; m != NULL; m = m->s_nxt) { if (m->s_field && m->s_name == unnamed) continue; if (strcmp(m->s_name, namedmem->n_name) == 0) { istk->i_subt = m->s_type; istk->i_cnt++; memberpop(); return; } } error(101, namedmem->n_name); memberpop(); istk->i_namedmem = 1; return; } /* * If the removed element was a structure member, we must go * to the next structure member. */ if (istk->i_cnt > 0 && istk->i_type->t_tspec == STRUCT && !istk->i_namedmem) { do { m = istk->i_mem = istk->i_mem->s_nxt; if (m == NULL) LERROR("popi2()"); } while (m->s_field && m->s_name == unnamed); istk->i_subt = m->s_type; } } static void popinit(int brace) { DPRINTF(("popinit(%d)\n", brace)); if (brace) { /* * Take all entries, including the first which requires * a closing brace, from the stack. */ do { brace = initstk->i_brace; popi2(); } while (!brace); } else { /* * Take all entries which cannot be used for further * initializers from the stack, but do this only if * they do not require a closing brace. */ while (!initstk->i_brace && initstk->i_cnt == 0 && !initstk->i_nolimit) { popi2(); } } } static void pushinit(void) { #ifdef DEBUG char buf[64]; #endif istk_t *istk; int cnt; sym_t *m; istk = initstk; /* Extend an incomplete array type by one element */ if (istk->i_cnt == 0) { DPRINTF(("pushinit(extend) %s\n", tyname(buf, sizeof(buf), istk->i_type))); /* * Inside of other aggregate types must not be an incomplete * type. */ if (istk->i_nxt->i_nxt != NULL) LERROR("pushinit()"); istk->i_cnt = 1; if (istk->i_type->t_tspec != ARRAY) LERROR("pushinit()"); istk->i_type->t_dim++; /* from now its a complete type */ setcompl(istk->i_type, 0); } if (istk->i_cnt <= 0) LERROR("pushinit()"); if (istk->i_type != NULL && issclt(istk->i_type->t_tspec)) LERROR("pushinit() 4"); initstk = xcalloc(1, sizeof (istk_t)); initstk->i_nxt = istk; initstk->i_type = istk->i_subt; if (initstk->i_type->t_tspec == FUNC) LERROR("pushinit()"); again: istk = initstk; DPRINTF(("pushinit(%s)\n", tyname(buf, sizeof(buf), istk->i_type))); switch (istk->i_type->t_tspec) { case ARRAY: if (namedmem) { DPRINTF(("pushinit ARRAY %s\n", namedmem->n_name)); free(istk); initstk = initstk->i_nxt; goto again; } if (incompl(istk->i_type) && istk->i_nxt->i_nxt != NULL) { /* initialisation of an incomplete type */ error(175); initerr = 1; return; } istk->i_subt = istk->i_type->t_subt; istk->i_nolimit = incompl(istk->i_type); istk->i_cnt = istk->i_type->t_dim; DPRINTF(("elements array %s[%d] %s\n", tyname(buf, sizeof(buf), istk->i_subt), istk->i_cnt, namedmem ? namedmem->n_name : "*none*")); break; case UNION: if (tflag) /* initialisation of union is illegal in trad. C */ warning(238); /* FALLTHROUGH */ case STRUCT: if (incompl(istk->i_type)) { /* initialisation of an incomplete type */ error(175); initerr = 1; return; } cnt = 0; DPRINTF(("2. member lookup %s %s\n", tyname(buf, sizeof(buf), istk->i_type), namedmem ? namedmem->n_name : "*none*")); for (m = istk->i_type->t_str->memb; m != NULL; m = m->s_nxt) { if (m->s_field && m->s_name == unnamed) continue; if (namedmem != NULL) { DPRINTF(("pushinit():[member:%s, looking:%s]\n", m->s_name, namedmem->n_name)); if (strcmp(m->s_name, namedmem->n_name) == 0) { cnt++; break; } else continue; } if (++cnt == 1) { istk->i_mem = m; istk->i_subt = m->s_type; } } if (namedmem != NULL) { istk->i_namedmem = 1; if (m == NULL) { error(101, namedmem->n_name); initerr = 1; } else { istk->i_mem = m; istk->i_subt = m->s_type; } memberpop(); cnt = istk->i_type->t_tspec == STRUCT ? 2 : 1; } if (cnt == 0) { /* cannot init. struct/union with no named member */ error(179); initerr = 1; return; } istk->i_cnt = istk->i_type->t_tspec == STRUCT ? cnt : 1; break; default: if (namedmem) { DPRINTF(("pushinit(): pop\n")); free(istk); initstk = initstk->i_nxt; goto again; } istk->i_cnt = 1; break; } } static void testinit(void) { istk_t *istk; istk = initstk; /* * If a closing brace is expected we have at least one initializer * too much. */ if (istk->i_cnt == 0 && !istk->i_nolimit && !istk->i_namedmem) { switch (istk->i_type->t_tspec) { case ARRAY: /* too many array initializers */ error(173); break; case STRUCT: case UNION: /* too many struct/union initializers */ error(172); break; default: /* too many initializers */ error(174); break; } initerr = 1; } } static void nextinit(int brace) { char buf[64]; DPRINTF(("nextinit(%d)\n", brace)); if (!brace) { if (initstk->i_type == NULL && !issclt(initstk->i_subt->t_tspec)) { /* {}-enclosed initializer required */ error(181); } /* * Make sure an entry with a scalar type is at the top * of the stack. */ if (!initerr) testinit(); while (!initerr && (initstk->i_type == NULL || !issclt(initstk->i_type->t_tspec))) { if (!initerr) pushinit(); } } else { if (initstk->i_type != NULL && issclt(initstk->i_type->t_tspec)) { /* invalid initializer */ error(176); initerr = 1; } if (!initerr) testinit(); if (!initerr) pushinit(); if (!initerr) initstk->i_brace = 1; } } void initlbr(void) { if (initerr) return; if ((initsym->s_scl == AUTO || initsym->s_scl == REG) && initstk->i_nxt == NULL) { if (tflag && !issclt(initstk->i_subt->t_tspec)) /* no automatic aggregate initialization in trad. C*/ warning(188); } /* * Remove all entries which cannot be used for further initializers * and do not expect a closing brace. */ popinit(0); nextinit(1); } void initrbr(void) { if (initerr) return; popinit(1); } void mkinit(tnode_t *tn) { ptrdiff_t offs; sym_t *sym; tspec_t lt, rt; tnode_t *ln; struct mbl *tmem; scl_t sc; #ifdef DEBUG char buf[64]; #endif DPRINTF(("mkinit(%s)\n", tyname(buf, sizeof(buf), tn->tn_type))); if (initerr || tn == NULL) goto end; sc = initsym->s_scl; /* * Do not test for automatic aggregate initialisation. If the * initializer starts with a brace we have the warning already. * If not, an error will be printed that the initializer must * be enclosed by braces. */ /* * Local initialisation of non-array-types with only one expression * without braces is done by ASSIGN */ if ((sc == AUTO || sc == REG) && initsym->s_type->t_tspec != ARRAY && initstk->i_nxt == NULL) { ln = getnnode(initsym, 0); ln->tn_type = tduptyp(ln->tn_type); ln->tn_type->t_const = 0; tn = build(ASSIGN, ln, tn); expr(tn, 0, 0, 1); goto end; } /* * Remove all entries which cannot be used for further initializers * and do not require a closing brace. */ popinit(0); /* Initialisations by strings are done in strginit(). */ if (strginit(tn)) goto end; nextinit(0); if (initerr || tn == NULL) goto end; initstk->i_cnt--; DPRINTF(("mkinit() cnt=%d tn=%p\n", initstk->i_cnt, tn)); /* Create a temporary node for the left side. */ ln = tgetblk(sizeof (tnode_t)); ln->tn_op = NAME; ln->tn_type = tduptyp(initstk->i_type); ln->tn_type->t_const = 0; ln->tn_lvalue = 1; ln->tn_sym = initsym; /* better than nothing */ tn = cconv(tn); lt = ln->tn_type->t_tspec; rt = tn->tn_type->t_tspec; if (!issclt(lt)) LERROR("mkinit()"); if (!typeok(INIT, 0, ln, tn)) goto end; /* - * Store the tree memory. This is nessesary because otherwise + * Store the tree memory. This is necessary because otherwise * expr() would free it. */ tmem = tsave(); expr(tn, 1, 0, 1); trestor(tmem); if (isityp(lt) && ln->tn_type->t_isfield && !isityp(rt)) { /* * Bit-fields can be initialized in trad. C only by integer * constants. */ if (tflag) /* bit-field initialisation is illegal in trad. C */ warning(186); } if (lt != rt || (initstk->i_type->t_isfield && tn->tn_op == CON)) tn = convert(INIT, 0, initstk->i_type, tn); if (tn != NULL && tn->tn_op != CON) { sym = NULL; offs = 0; if (conaddr(tn, &sym, &offs) == -1) { if (sc == AUTO || sc == REG) { /* non-constant initializer */ (void)gnuism(177); } else { /* non-constant initializer */ error(177); } } } end: /* * We only free the block, if we are not a compound declaration * We know that the only symbols that start with a digit are the * ones we allocate with mktempsym() for compound declarations */ if (!isdigit((unsigned char)initsym->s_name[0])) tfreeblk(); } static int strginit(tnode_t *tn) { tspec_t t; istk_t *istk; int len; strg_t *strg; if (tn->tn_op != STRING) return (0); istk = initstk; strg = tn->tn_strg; /* * Check if we have an array type which can be initialized by * the string. */ if (istk->i_subt != NULL && istk->i_subt->t_tspec == ARRAY) { t = istk->i_subt->t_subt->t_tspec; if (!((strg->st_tspec == CHAR && (t == CHAR || t == UCHAR || t == SCHAR)) || (strg->st_tspec == WCHAR && t == WCHAR))) { return (0); } /* Put the array at top of stack */ pushinit(); istk = initstk; } else if (istk->i_type != NULL && istk->i_type->t_tspec == ARRAY) { t = istk->i_type->t_subt->t_tspec; if (!((strg->st_tspec == CHAR && (t == CHAR || t == UCHAR || t == SCHAR)) || (strg->st_tspec == WCHAR && t == WCHAR))) { return (0); } /* * If the array is already partly initialized, we are * wrong here. */ if (istk->i_cnt != istk->i_type->t_dim) return (0); } else { return (0); } /* Get length without trailing NUL character. */ len = strg->st_len; if (istk->i_nolimit) { istk->i_nolimit = 0; istk->i_type->t_dim = len + 1; /* from now complete type */ setcompl(istk->i_type, 0); } else { if (istk->i_type->t_dim < len) { /* non-null byte ignored in string initializer */ warning(187); } } /* In every case the array is initialized completely. */ istk->i_cnt = 0; return (1); } Index: head/usr.bin/xlint/lint1/lint.h =================================================================== --- head/usr.bin/xlint/lint1/lint.h (revision 286613) +++ head/usr.bin/xlint/lint1/lint.h (revision 286614) @@ -1,118 +1,120 @@ /* $NetBSD: lint.h,v 1.2 1995/07/03 21:24:18 cgd Exp $ */ /* * Copyright (c) 1994, 1995 Jochen Pohl * All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Jochen Pohl for * The NetBSD Project. * 4. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ */ #include #include #include #include "param.h" /* * Type specifiers, used in type structures (type_t) and otherwere. */ typedef enum { NOTSPEC, SIGNED, /* keyword "signed", only used in the parser */ UNSIGN, /* keyword "unsigned", only used in the parser */ CHAR, /* char */ SCHAR, /* signed char */ UCHAR, /* unsigned char */ SHORT, /* (signed) short */ USHORT, /* unsigned short */ INT, /* (signed) int */ UINT, /* unsigned int */ LONG, /* (signed) long */ ULONG, /* unsigned long */ QUAD, /* (signed) long long */ UQUAD, /* unsigned long long */ FLOAT, /* float */ DOUBLE, /* double or, with tflag, long float */ LDOUBLE, /* long double */ VOID, /* void */ STRUCT, /* structure tag */ UNION, /* union tag */ ENUM, /* enum tag */ PTR, /* pointer */ ARRAY, /* array */ FUNC /* function */ #define NTSPEC ((int)FUNC + 1) } tspec_t; /* * size of types, name and classification */ typedef struct { int tt_sz; /* size in bits */ int tt_psz; /* size, different from tt_sz if pflag is set */ tspec_t tt_styp; /* signed counterpart */ tspec_t tt_utyp; /* unsigned counterpart */ u_int tt_isityp : 1; /* 1 if integer type */ u_int tt_isutyp : 1; /* 1 if unsigned integer type */ u_int tt_isftyp : 1; /* 1 if floating point type */ u_int tt_isatyp : 1; /* 1 if arithmetic type */ u_int tt_issclt : 1; /* 1 if scalar type */ - char *tt_name; /* Bezeichnung des Typs */ + char *tt_name; /* type name */ } ttab_t; #define size(t) (ttab[t].tt_sz) #define psize(t) (ttab[t].tt_psz) #define styp(t) (ttab[t].tt_styp) #define utyp(t) (ttab[t].tt_utyp) #define isityp(t) (ttab[t].tt_isityp) #define isutyp(t) (ttab[t].tt_isutyp) #define isftyp(t) (ttab[t].tt_isftyp) #define isatyp(t) (ttab[t].tt_isatyp) #define issclt(t) (ttab[t].tt_issclt) extern ttab_t ttab[]; typedef enum { NODECL, /* until now not declared */ DECL, /* declared */ TDEF, /* tentative defined */ DEF /* defined */ } def_t; /* * Following structure contains some data used for the output buffer. */ typedef struct ob { char *o_buf; /* buffer */ char *o_end; /* first byte after buffer */ size_t o_len; /* length of buffer */ char *o_nxt; /* next free byte in buffer */ } ob_t; #include "externs.h" Index: head/usr.bin/xlint/lint1/lint1.h =================================================================== --- head/usr.bin/xlint/lint1/lint1.h (revision 286613) +++ head/usr.bin/xlint/lint1/lint1.h (revision 286614) @@ -1,424 +1,424 @@ /* $NetBSD: lint1.h,v 1.16 2002/10/21 22:44:08 christos Exp $ */ /* * Copyright (c) 1996 Christopher G. Demetriou. All Rights Reserved. * Copyright (c) 1994, 1995 Jochen Pohl * All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Jochen Pohl for * The NetBSD Project. * 4. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include "lint.h" #include "op.h" /* XXX - works for most systems, but the whole ALIGN thing needs to go away */ #ifndef LINT_ALIGN #define LINT_ALIGN(x) (((x) + 15) & ~15) #endif /* * Describes the position of a declaration or anything else. */ typedef struct { int p_line; const char *p_file; int p_uniq; /* uniquifier */ } pos_t; /* Copies curr_pos, keeping things unique. */ #define UNIQUE_CURR_POS(pos) \ do { \ STRUCT_ASSIGN((pos), curr_pos); \ curr_pos.p_uniq++; \ if (curr_pos.p_file == csrc_pos.p_file) \ csrc_pos.p_uniq++; \ } while (0) /* * Strings cannot be referenced to simply by a pointer to its first * char. This is because strings can contain NUL characters other than the * trailing NUL. * * Strings are stored with a trailing NUL. */ typedef struct strg { tspec_t st_tspec; /* CHAR or WCHAR */ size_t st_len; /* length without trailing NUL */ union { u_char *_st_cp; wchar_t *_st_wcp; } st_u; } strg_t; #define st_cp st_u._st_cp #define st_wcp st_u._st_wcp /* * qualifiers (only for lex/yacc interface) */ typedef enum { CONST, VOLATILE } tqual_t; /* * Integer and floating point values are stored in this structure */ typedef struct { tspec_t v_tspec; int v_ansiu; /* set if an integer constant is unsigned in ANSI C */ union { int64_t _v_quad; /* integers */ ldbl_t _v_ldbl; /* floats */ } v_u; } val_t; #define v_quad v_u._v_quad #define v_ldbl v_u._v_ldbl /* * Structures of type str_t uniqely identify structures. This can't * be done in structures of type type_t, because these are copied * if they must be modified. So it would not be possible to check * if two structures are identical by comparing the pointers to * the type structures. * * The typename is used if the structure is unnamed to identify * the structure type in pass 2. */ typedef struct { u_int size; /* size in bit */ u_int align : 15; /* alignment in bit */ u_int sincompl : 1; /* set if incomplete type */ struct sym *memb; /* list of members */ struct sym *stag; /* symbol table entry of tag */ struct sym *stdef; /* symbol table entry of first typename */ } str_t; /* * same as above for enums */ typedef struct { u_int eincompl : 1; /* incomplete enum type */ struct sym *elem; /* list of enumerators */ struct sym *etag; /* symbol table entry of tag */ struct sym *etdef; /* symbol table entry of first typename */ } enum_t; /* * Types are represented by concatenation of structures of type type_t * via t_subt. */ typedef struct type { tspec_t t_tspec; /* type specifier */ u_int t_aincompl : 1; /* incomplete array type */ u_int t_const : 1; /* const modifier */ u_int t_volatile : 1; /* volatile modifier */ u_int t_proto : 1; /* function prototype (t_args valid) */ u_int t_vararg : 1; /* prototype with ... */ u_int t_typedef : 1; /* type defined with typedef */ u_int t_isfield : 1; /* type is bitfield */ u_int t_isenum : 1; /* type is (or was) enum (t_enum valid) */ union { int _t_dim; /* dimension */ str_t *_t_str; /* struct/union tag */ enum_t *_t_enum; /* enum tag */ struct sym *_t_args; /* arguments (if t_proto) */ } t_u; struct { u_int _t_flen : 8; /* length of bit-field */ u_int _t_foffs : 24; /* offset of bit-field */ } t_b; struct type *t_subt; /* element type (arrays), return value (functions), or type pointer points to */ } type_t; #define t_dim t_u._t_dim #define t_str t_u._t_str #define t_field t_u._t_field #define t_enum t_u._t_enum #define t_args t_u._t_args #define t_flen t_b._t_flen #define t_foffs t_b._t_foffs /* * types of symbols */ typedef enum { FVFT, /* variables, functions, type names, enums */ FMOS, /* members of structs or unions */ FTAG, /* tags */ FLAB /* labels */ } symt_t; /* * storage classes */ typedef enum { NOSCL, EXTERN, /* external symbols (indep. of decl_t) */ STATIC, /* static symbols (local and global) */ AUTO, /* automatic symbols (except register) */ REG, /* register */ TYPEDEF, /* typedef */ STRTAG, UNIONTAG, ENUMTAG, MOS, /* member of struct */ MOU, /* member of union */ ENUMCON, /* enumerator */ ABSTRACT, /* abstract symbol (sizeof, casts, unnamed argument) */ ARG, /* argument */ PARG, /* used in declaration stack during prototype declaration */ INLINE /* only used by the parser */ } scl_t; /* * symbol table entry */ typedef struct sym { const char *s_name; /* name */ const char *s_rename; /* renamed symbol's given name */ pos_t s_dpos; /* position of last (prototype)definition, prototypedeclaration, no-prototype-def., tentative definition or declaration, in this order */ pos_t s_spos; /* position of first initialisation */ pos_t s_upos; /* position of first use */ symt_t s_kind; /* type of symbol */ u_int s_keyw : 1; /* keyword */ u_int s_field : 1; /* bit-field */ u_int s_set : 1; /* variable set, label defined */ u_int s_used : 1; /* variable/label used */ u_int s_arg : 1; /* symbol is function argument */ u_int s_reg : 1; /* symbol is register variable */ u_int s_defarg : 1; /* undefined symbol in old style function definition */ u_int s_rimpl : 1; /* return value of function implicit decl. */ u_int s_osdef : 1; /* symbol stems from old style function def. */ u_int s_inline : 1; /* true if this is an inline function */ struct sym *s_xsym; /* for local declared external symbols pointer to external symbol with same name */ def_t s_def; /* declared, tentative defined, defined */ scl_t s_scl; /* storage class */ int s_blklev; /* level of declaration, -1 if not in symbol table */ type_t *s_type; /* type */ val_t s_value; /* value (if enumcon) */ union { str_t *_s_st; /* tag, if it is a struct/union member */ enum_t *_s_et; /* tag, if it is an enumerator */ tspec_t _s_tsp; /* type (only for keywords) */ tqual_t _s_tqu; /* qualifier (only for keywords) */ struct sym *_s_args; /* arguments in old style function definitions */ } u; struct sym *s_link; /* next symbol with same hash value */ struct sym **s_rlink; /* pointer to s_link of prev. symbol */ struct sym *s_nxt; /* next struct/union member, enumerator, argument */ struct sym *s_dlnxt; /* next symbol declared on same level */ } sym_t; #define s_styp u._s_st #define s_etyp u._s_et #define s_tspec u._s_tsp #define s_tqual u._s_tqu #define s_args u._s_args /* * Used to keep some informations about symbols before they are entered * into the symbol table. */ typedef struct sbuf { const char *sb_name; /* name of symbol */ size_t sb_len; /* length (without '\0') */ int sb_hash; /* hash value */ sym_t *sb_sym; /* symbol table entry */ struct sbuf *sb_nxt; /* for freelist */ } sbuf_t; /* * tree node */ typedef struct tnode { op_t tn_op; /* operator */ type_t *tn_type; /* type */ u_int tn_lvalue : 1; /* node is lvalue */ - u_int tn_cast : 1; /* if tn_op == CVT its an explizit cast */ + u_int tn_cast : 1; /* if tn_op == CVT its an explicit cast */ u_int tn_parn : 1; /* node parenthesized */ union { struct { struct tnode *_tn_left; /* (left) operand */ struct tnode *_tn_right; /* right operand */ } tn_s; sym_t *_tn_sym; /* symbol if op == NAME */ val_t *_tn_val; /* value if op == CON */ strg_t *_tn_strg; /* string if op == STRING */ } tn_u; } tnode_t; #define tn_left tn_u.tn_s._tn_left #define tn_right tn_u.tn_s._tn_right #define tn_sym tn_u._tn_sym #define tn_val tn_u._tn_val #define tn_strg tn_u._tn_strg /* * For nested declarations a stack exists, which holds all information * needed for the current level. dcs points to the top element of this * stack. * * ctx describes the context of the current declaration. Its value is * one of * EXTERN global declarations * MOS oder MOU declarations of struct or union members * ENUMCON declarations of enums * ARG declaration of arguments in old style function definitions * PARG declaration of arguments in function prototypes * AUTO declaration of local symbols * ABSTRACT abstract declarations (sizeof, casts) * */ typedef struct dinfo { tspec_t d_atyp; /* VOID, CHAR, INT, FLOAT or DOUBLE */ tspec_t d_smod; /* SIGNED or UNSIGN */ tspec_t d_lmod; /* SHORT, LONG or QUAD */ scl_t d_scl; /* storage class */ type_t *d_type; /* after deftyp() pointer to the type used for all declarators */ sym_t *d_rdcsym; /* redeclared symbol */ int d_offset; /* offset of next structure member */ int d_stralign; /* alignment required for current structure */ scl_t d_ctx; /* context of declaration */ u_int d_const : 1; /* const in declaration specifiers */ u_int d_volatile : 1; /* volatile in declaration specifiers */ u_int d_inline : 1; /* inline in declaration specifiers */ u_int d_mscl : 1; /* multiple storage classes */ u_int d_terr : 1; /* invalid type combination */ u_int d_nedecl : 1; /* 1 if at least a tag is declared */ u_int d_vararg : 1; /* ... in current function decl. */ u_int d_proto : 1; /* current funct. decl. is prototype */ u_int d_notyp : 1; /* set if no type specifier was present */ u_int d_asm : 1; /* set if d_ctx == AUTO and asm() present */ type_t *d_tagtyp; /* tag during member declaration */ sym_t *d_fargs; /* list of arguments during function def. */ pos_t d_fdpos; /* position of function definition */ sym_t *d_dlsyms; /* first symbol declared at this level */ sym_t **d_ldlsym; /* points to s_dlnxt in last symbol decl. at this level */ sym_t *d_fpsyms; /* symbols defined in prototype */ struct dinfo *d_nxt; /* next level */ } dinfo_t; /* * Type of stack which is used for initialisation of aggregate types. */ typedef struct istk { type_t *i_type; /* type of initialisation */ type_t *i_subt; /* type of next level */ u_int i_brace : 1; /* need } for pop */ u_int i_nolimit : 1; /* incomplete array type */ u_int i_namedmem : 1; /* has c9x named members */ sym_t *i_mem; /* next structure member */ int i_cnt; /* # of remaining elements */ struct istk *i_nxt; /* previous level */ } istk_t; /* * Used to collect information about pointers and qualifiers in * declarators. */ typedef struct pqinf { int p_pcnt; /* number of asterisks */ u_int p_const : 1; u_int p_volatile : 1; struct pqinf *p_nxt; } pqinf_t; /* * Case values are stored in a list of type clst_t. */ typedef struct clst { val_t cl_val; struct clst *cl_nxt; } clst_t; /* * Used to keep informations about nested control statements. */ typedef struct cstk { int c_env; /* type of statement (T_IF, ...) */ u_int c_loop : 1; /* continue && break are valid */ u_int c_switch : 1; /* case && break are valid */ u_int c_break : 1; /* loop/switch has break */ u_int c_cont : 1; /* loop has continue */ u_int c_default : 1; /* switch has default */ u_int c_infinite : 1; /* break condition always false (for (;;), while (1)) */ u_int c_rchif : 1; /* end of if-branch reached */ u_int c_noretval : 1; /* had "return;" */ u_int c_retval : 1; /* had "return (e);" */ type_t *c_swtype; /* type of switch expression */ clst_t *c_clst; /* list of case values */ struct mbl *c_fexprm; /* saved memory for end of loop expression in for() */ tnode_t *c_f3expr; /* end of loop expr in for() */ pos_t c_fpos; /* position of end of loop expr */ pos_t c_cfpos; /* same for csrc_pos */ struct cstk *c_nxt; /* outer control statement */ } cstk_t; typedef struct { size_t lo; size_t hi; } range_t; #include "externs1.h" #define ERR_SETSIZE 1024 #define __NERRBITS (sizeof(unsigned int)) typedef struct err_set { unsigned int errs_bits[(ERR_SETSIZE + __NERRBITS-1) / __NERRBITS]; } err_set; #define ERR_SET(n, p) \ ((p)->errs_bits[(n)/__NERRBITS] |= (1 << ((n) % __NERRBITS))) #define ERR_CLR(n, p) \ ((p)->errs_bits[(n)/__NERRBITS] &= ~(1 << ((n) % __NERRBITS))) #define ERR_ISSET(n, p) \ ((p)->errs_bits[(n)/__NERRBITS] & (1 << ((n) % __NERRBITS))) #define ERR_ZERO(p) (void)memset((p), 0, sizeof(*(p))) #define LERROR(fmt, args...) lerror(__FILE__, __LINE__, fmt, ##args) extern err_set msgset; Index: head/usr.bin/xlint/lint1/scan.l =================================================================== --- head/usr.bin/xlint/lint1/scan.l (revision 286613) +++ head/usr.bin/xlint/lint1/scan.l (revision 286614) @@ -1,1531 +1,1531 @@ %{ /* $NetBSD: scan.l,v 1.37 2007/02/06 00:08:31 he Exp $ */ /* * Copyright (c) 1996 Christopher G. Demetriou. All Rights Reserved. * Copyright (c) 1994, 1995 Jochen Pohl * All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Jochen Pohl for * The NetBSD Project. * 4. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #if defined(__RCSID) && !defined(lint) __RCSID("$NetBSD: scan.l,v 1.37 2007/02/06 00:08:31 he Exp $"); #endif __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include "lint1.h" #include "cgram.h" #define CHAR_MASK (~(~0 << CHAR_BIT)) /* Current position (its also updated when an included file is parsed) */ pos_t curr_pos = { 1, "", 0 }; /* * Current position in C source (not updated when an included file is * parsed). */ pos_t csrc_pos = { 1, "", 0 }; static void incline(void); static void badchar(int); static sbuf_t *allocsb(void); static void freesb(sbuf_t *); static int inpc(void); static int hash(const char *); static sym_t *search(sbuf_t *); static int name(void); static int keyw(sym_t *); static int icon(int); static int fcon(void); static int operator(int, op_t); static int ccon(void); static int wccon(void); static int getescc(int); static void directive(void); static void comment(void); static void slashslashcomment(void); static int string(void); static int wcstrg(void); %} %option nounput L [_A-Za-z] D [0-9] NZD [1-9] OD [0-7] HD [0-9A-Fa-f] EX ([eE][+-]?[0-9]+) %% {L}({L}|{D})* return (name()); 0{OD}*[lLuU]* return (icon(8)); {NZD}{D}*[lLuU]* return (icon(10)); 0[xX]{HD}+[lLuU]* return (icon(16)); {D}+\.{D}*{EX}?[fFlL]? | {D}+{EX}[fFlL]? | 0[xX]{HD}+p{HD}+[fFlL]? | \.{D}+{EX}?[fFlL]? return (fcon()); "=" return (operator(T_ASSIGN, ASSIGN)); "*=" return (operator(T_OPASS, MULASS)); "/=" return (operator(T_OPASS, DIVASS)); "%=" return (operator(T_OPASS, MODASS)); "+=" return (operator(T_OPASS, ADDASS)); "-=" return (operator(T_OPASS, SUBASS)); "<<=" return (operator(T_OPASS, SHLASS)); ">>=" return (operator(T_OPASS, SHRASS)); "&=" return (operator(T_OPASS, ANDASS)); "^=" return (operator(T_OPASS, XORASS)); "|=" return (operator(T_OPASS, ORASS)); "||" return (operator(T_LOGOR, LOGOR)); "&&" return (operator(T_LOGAND, LOGAND)); "|" return (operator(T_OR, OR)); "&" return (operator(T_AND, AND)); "^" return (operator(T_XOR, XOR)); "==" return (operator(T_EQOP, EQ)); "!=" return (operator(T_EQOP, NE)); "<" return (operator(T_RELOP, LT)); ">" return (operator(T_RELOP, GT)); "<=" return (operator(T_RELOP, LE)); ">=" return (operator(T_RELOP, GE)); "<<" return (operator(T_SHFTOP, SHL)); ">>" return (operator(T_SHFTOP, SHR)); "++" return (operator(T_INCDEC, INC)); "--" return (operator(T_INCDEC, DEC)); "->" return (operator(T_STROP, ARROW)); "." return (operator(T_STROP, POINT)); "+" return (operator(T_ADDOP, PLUS)); "-" return (operator(T_ADDOP, MINUS)); "*" return (operator(T_MULT, MULT)); "/" return (operator(T_DIVOP, DIV)); "%" return (operator(T_DIVOP, MOD)); "!" return (operator(T_UNOP, NOT)); "~" return (operator(T_UNOP, COMPL)); "\"" return (string()); "L\"" return (wcstrg()); ";" return (T_SEMI); "{" return (T_LBRACE); "}" return (T_RBRACE); "," return (T_COMMA); ":" return (T_COLON); "?" return (T_QUEST); "[" return (T_LBRACK); "]" return (T_RBRACK); "(" return (T_LPARN); ")" return (T_RPARN); "..." return (T_ELLIPSE); "'" return (ccon()); "L'" return (wccon()); ^#.*$ directive(); \n incline(); \t|" "|\f|\v ; "/*" comment(); "//" slashslashcomment(); . badchar(yytext[0]); %% static void incline(void) { curr_pos.p_line++; curr_pos.p_uniq = 0; if (curr_pos.p_file == csrc_pos.p_file) { csrc_pos.p_line++; csrc_pos.p_uniq = 0; } } static void badchar(int c) { /* unknown character \%o */ error(250, c); } /* * Keywords. * During initialisation they are written to the symbol table. */ static struct kwtab { const char *kw_name; /* keyword */ int kw_token; /* token returned by yylex() */ scl_t kw_scl; /* storage class if kw_token T_SCLASS */ tspec_t kw_tspec; /* type spec. if kw_token T_TYPE or T_SOU */ tqual_t kw_tqual; /* type qual. fi kw_token T_QUAL */ u_int kw_c89; /* c89 keyword */ u_int kw_c99; /* c99 keyword */ u_int kw_gcc; /* GCC keyword */ } kwtab[] = { { "asm", T_ASM, 0, 0, 0, 0, 0, 1 }, { "__asm", T_ASM, 0, 0, 0, 0, 0, 0 }, { "__asm__", T_ASM, 0, 0, 0, 0, 0, 0 }, { "auto", T_SCLASS, AUTO, 0, 0, 0, 0, 0 }, { "break", T_BREAK, 0, 0, 0, 0, 0, 0 }, { "case", T_CASE, 0, 0, 0, 0, 0, 0 }, { "char", T_TYPE, 0, CHAR, 0, 0, 0, 0 }, { "const", T_QUAL, 0, 0, CONST, 1, 0, 0 }, { "__const__", T_QUAL, 0, 0, CONST, 0, 0, 0 }, { "__const", T_QUAL, 0, 0, CONST, 0, 0, 0 }, { "continue", T_CONTINUE, 0, 0, 0, 0, 0, 0 }, { "default", T_DEFAULT, 0, 0, 0, 0, 0, 0 }, { "do", T_DO, 0, 0, 0, 0, 0, 0 }, { "double", T_TYPE, 0, DOUBLE, 0, 0, 0, 0 }, { "else", T_ELSE, 0, 0, 0, 0, 0, 0 }, { "enum", T_ENUM, 0, 0, 0, 0, 0, 0 }, { "extern", T_SCLASS, EXTERN, 0, 0, 0, 0, 0 }, { "float", T_TYPE, 0, FLOAT, 0, 0, 0, 0 }, { "for", T_FOR, 0, 0, 0, 0, 0, 0 }, { "goto", T_GOTO, 0, 0, 0, 0, 0, 0 }, { "if", T_IF, 0, 0, 0, 0, 0, 0 }, { "inline", T_SCLASS, INLINE, 0, 0, 0, 1, 0 }, { "__inline__", T_SCLASS, INLINE, 0, 0, 0, 0, 0 }, { "__inline", T_SCLASS, INLINE, 0, 0, 0, 0, 0 }, { "int", T_TYPE, 0, INT, 0, 0, 0, 0 }, { "__symbolrename", T_SYMBOLRENAME, 0, 0, 0, 0, 0, 0 }, { "long", T_TYPE, 0, LONG, 0, 0, 0, 0 }, { "register", T_SCLASS, REG, 0, 0, 0, 0, 0 }, { "return", T_RETURN, 0, 0, 0, 0, 0, 0 }, { "short", T_TYPE, 0, SHORT, 0, 0, 0, 0 }, { "signed", T_TYPE, 0, SIGNED, 0, 1, 0, 0 }, { "__signed__", T_TYPE, 0, SIGNED, 0, 0, 0, 0 }, { "__signed", T_TYPE, 0, SIGNED, 0, 0, 0, 0 }, { "sizeof", T_SIZEOF, 0, 0, 0, 0, 0, 0 }, { "static", T_SCLASS, STATIC, 0, 0, 0, 0, 0 }, { "struct", T_SOU, 0, STRUCT, 0, 0, 0, 0 }, { "switch", T_SWITCH, 0, 0, 0, 0, 0, 0 }, { "typedef", T_SCLASS, TYPEDEF, 0, 0, 0, 0, 0 }, { "union", T_SOU, 0, UNION, 0, 0, 0, 0 }, { "unsigned", T_TYPE, 0, UNSIGN, 0, 0, 0, 0 }, { "void", T_TYPE, 0, VOID, 0, 0, 0, 0 }, { "volatile", T_QUAL, 0, 0, VOLATILE, 1, 0, 0 }, { "__volatile__", T_QUAL, 0, 0, VOLATILE, 0, 0, 0 }, { "__volatile", T_QUAL, 0, 0, VOLATILE, 0, 0, 0 }, { "while", T_WHILE, 0, 0, 0, 0, 0, 0 }, { NULL, 0, 0, 0, 0, 0, 0, 0 } }; /* Symbol table */ static sym_t *symtab[HSHSIZ1]; /* bit i of the entry with index i is set */ uint64_t qbmasks[sizeof(uint64_t) * CHAR_BIT]; /* least significant i bits are set in the entry with index i */ uint64_t qlmasks[sizeof(uint64_t) * CHAR_BIT + 1]; /* least significant i bits are not set in the entry with index i */ uint64_t qumasks[sizeof(uint64_t) * CHAR_BIT + 1]; /* free list for sbuf structures */ static sbuf_t *sbfrlst; -/* Typ of next expected symbol */ +/* type of next expected symbol */ symt_t symtyp; /* * All keywords are written to the symbol table. This saves us looking * in an extra table for each name we found. */ void initscan(void) { struct kwtab *kw; sym_t *sym; int h, i; uint64_t uq; for (kw = kwtab; kw->kw_name != NULL; kw++) { if ((kw->kw_c89 || kw->kw_c99) && tflag) continue; if (kw->kw_c99 && !(Sflag || gflag)) continue; if (kw->kw_gcc && !gflag) continue; sym = getblk(sizeof (sym_t)); sym->s_name = kw->kw_name; sym->s_keyw = 1; sym->s_value.v_quad = kw->kw_token; if (kw->kw_token == T_TYPE || kw->kw_token == T_SOU) { sym->s_tspec = kw->kw_tspec; } else if (kw->kw_token == T_SCLASS) { sym->s_scl = kw->kw_scl; } else if (kw->kw_token == T_QUAL) { sym->s_tqual = kw->kw_tqual; } h = hash(sym->s_name); if ((sym->s_link = symtab[h]) != NULL) symtab[h]->s_rlink = &sym->s_link; (symtab[h] = sym)->s_rlink = &symtab[h]; } /* initialize bit-masks for quads */ for (i = 0; i < sizeof (uint64_t) * CHAR_BIT; i++) { qbmasks[i] = (uint64_t)1 << i; uq = ~(uint64_t)0 << i; qumasks[i] = uq; qlmasks[i] = ~uq; } qumasks[i] = 0; qlmasks[i] = ~(uint64_t)0; } /* * Get a free sbuf structure, if possible from the free list */ static sbuf_t * allocsb(void) { sbuf_t *sb; if ((sb = sbfrlst) != NULL) { sbfrlst = sb->sb_nxt; } else { if ((sb = malloc(sizeof (sbuf_t))) == NULL) nomem(); } (void)memset(sb, 0, sizeof (*sb)); return (sb); } /* * Put a sbuf structure to the free list */ static void freesb(sbuf_t *sb) { sb->sb_nxt = sbfrlst; sbfrlst = sb; } /* * Read a character and ensure that it is positive (except EOF). * Increment line count(s) if necessary. */ static int inpc(void) { int c; if ((c = input()) != EOF && (c &= CHAR_MASK) == '\n') incline(); return (c); } static int hash(const char *s) { u_int v; const u_char *us; v = 0; for (us = (const u_char *)s; *us != '\0'; us++) { v = (v << sizeof (v)) + *us; v ^= v >> (sizeof (v) * CHAR_BIT - sizeof (v)); } return (v % HSHSIZ1); } /* * Lex has found a letter followed by zero or more letters or digits. * It looks for a symbol in the symbol table with the same name. This * symbol must either be a keyword or a symbol of the type required by * symtyp (label, member, tag, ...). * * If it is a keyword, the token is returned. In some cases it is described * more deeply by data written to yylval. * * If it is a symbol, T_NAME is returned and the pointer to a sbuf struct * is stored in yylval. This struct contains the name of the symbol, it's * length and hash value. If there is already a symbol of the same name * and type in the symbol table, the sbuf struct also contains a pointer * to the symbol table entry. */ static int name(void) { char *s; sbuf_t *sb; sym_t *sym; int tok; sb = allocsb(); sb->sb_name = yytext; sb->sb_len = yyleng; sb->sb_hash = hash(yytext); if ((sym = search(sb)) != NULL && sym->s_keyw) { freesb(sb); return (keyw(sym)); } sb->sb_sym = sym; if (sym != NULL) { if (blklev < sym->s_blklev) LERROR("name()"); sb->sb_name = sym->s_name; sb->sb_len = strlen(sym->s_name); tok = sym->s_scl == TYPEDEF ? T_TYPENAME : T_NAME; } else { s = getblk(yyleng + 1); (void)memcpy(s, yytext, yyleng + 1); sb->sb_name = s; sb->sb_len = yyleng; tok = T_NAME; } yylval.y_sb = sb; return (tok); } static sym_t * search(sbuf_t *sb) { sym_t *sym; for (sym = symtab[sb->sb_hash]; sym != NULL; sym = sym->s_link) { if (strcmp(sym->s_name, sb->sb_name) == 0) { if (sym->s_keyw || sym->s_kind == symtyp) return (sym); } } return (NULL); } static int keyw(sym_t *sym) { int t; if ((t = (int)sym->s_value.v_quad) == T_SCLASS) { yylval.y_scl = sym->s_scl; } else if (t == T_TYPE || t == T_SOU) { yylval.y_tspec = sym->s_tspec; } else if (t == T_QUAL) { yylval.y_tqual = sym->s_tqual; } return (t); } /* * Convert a string representing an integer into internal representation. * The value is returned in yylval. icon() (and yylex()) returns T_CON. */ static int icon(int base) { int l_suffix, u_suffix; int len; const char *cp; char c, *eptr; tspec_t typ; u_long ul = 0; uint64_t uq = 0; int ansiu; static tspec_t contypes[2][3] = { { INT, LONG, QUAD }, { UINT, ULONG, UQUAD } }; cp = yytext; len = yyleng; /* skip 0x */ if (base == 16) { cp += 2; len -= 2; } /* read suffixes */ l_suffix = u_suffix = 0; for ( ; ; ) { if ((c = cp[len - 1]) == 'l' || c == 'L') { l_suffix++; } else if (c == 'u' || c == 'U') { u_suffix++; } else { break; } len--; } if (l_suffix > 2 || u_suffix > 1) { /* malformed integer constant */ warning(251); if (l_suffix > 2) l_suffix = 2; if (u_suffix > 1) u_suffix = 1; } if (tflag && u_suffix != 0) { /* suffix U is illegal in traditional C */ warning(97); } typ = contypes[u_suffix][l_suffix]; errno = 0; if (l_suffix < 2) { ul = strtoul(cp, &eptr, base); } else { uq = strtouq(cp, &eptr, base); } if (eptr != cp + len) LERROR("icon()"); if (errno != 0) /* integer constant out of range */ warning(252); /* - * If the value is to big for the current type, we must choose + * If the value is too big for the current type, we must choose * another type. */ ansiu = 0; switch (typ) { case INT: if (ul <= INT_MAX) { /* ok */ } else if (ul <= (unsigned)UINT_MAX && base != 10) { typ = UINT; #if INT_MAX != LONG_MAX } else if (ul <= LONG_MAX) { typ = LONG; #endif } else { typ = ULONG; } if (typ == UINT || typ == ULONG) { if (tflag) { typ = LONG; } else if (!sflag) { /* * Remember that the constant is unsigned * only in ANSI C */ ansiu = 1; } } break; case UINT: if (ul > (u_int)UINT_MAX) typ = ULONG; break; case LONG: if (ul > LONG_MAX && !tflag) { typ = ULONG; if (!sflag) ansiu = 1; } break; case QUAD: if (uq > QUAD_MAX && !tflag) { typ = UQUAD; if (!sflag) ansiu = 1; } break; /* LINTED (enumeration values not handled in switch) */ case STRUCT: case VOID: case LDOUBLE: case FUNC: case ARRAY: case PTR: case ENUM: case UNION: case SIGNED: case NOTSPEC: case DOUBLE: case FLOAT: case UQUAD: case ULONG: case USHORT: case SHORT: case UCHAR: case SCHAR: case CHAR: case UNSIGN: break; } if (typ != QUAD && typ != UQUAD) { if (isutyp(typ)) { uq = ul; } else { uq = (int64_t)(long)ul; } } uq = (uint64_t)xsign((int64_t)uq, typ, -1); if ((yylval.y_val = calloc(1, sizeof(val_t))) == NULL) nomem(); yylval.y_val->v_tspec = typ; yylval.y_val->v_ansiu = ansiu; yylval.y_val->v_quad = (int64_t)uq; return (T_CON); } /* * Returns 1 if t is a signed type and the value is negative. * * len is the number of significant bits. If len is -1, len is set * to the width of type t. */ int sign(int64_t q, tspec_t t, int len) { if (t == PTR || isutyp(t)) return (0); return (msb(q, t, len)); } int msb(int64_t q, tspec_t t, int len) { if (len <= 0) len = size(t); return ((q & qbmasks[len - 1]) != 0); } /* * Extends the sign of q. */ int64_t xsign(int64_t q, tspec_t t, int len) { if (len <= 0) len = size(t); if (t == PTR || isutyp(t) || !sign(q, t, len)) { q &= qlmasks[len]; } else { q |= qumasks[len]; } return (q); } /* * Convert a string representing a floating point value into its interal * representation. Type and value are returned in yylval. fcon() * (and yylex()) returns T_CON. * XXX Currently it is not possible to convert constants of type * long double which are greater than DBL_MAX. */ static int fcon(void) { const char *cp; int len; tspec_t typ; char c, *eptr; double d; float f = 0; cp = yytext; len = yyleng; if ((c = cp[len - 1]) == 'f' || c == 'F') { typ = FLOAT; len--; } else if (c == 'l' || c == 'L') { typ = LDOUBLE; len--; } else { typ = DOUBLE; } if (tflag && typ != DOUBLE) { /* suffixes F and L are illegal in traditional C */ warning(98); } errno = 0; d = strtod(cp, &eptr); if (eptr != cp + len) { switch (*eptr) { /* * XXX: non-native non-current strtod() may not handle hex * floats, ignore the rest if we find traces of hex float * syntax... */ case 'p': case 'P': case 'x': case 'X': d = 0; errno = 0; break; default: LERROR("fcon()"); } } if (errno != 0) /* floating-point constant out of range */ warning(248); if (typ == FLOAT) { f = (float)d; if (!finite(f)) { /* floating-point constant out of range */ warning(248); f = f > 0 ? FLT_MAX : -FLT_MAX; } } if ((yylval.y_val = calloc(1, sizeof (val_t))) == NULL) nomem(); yylval.y_val->v_tspec = typ; if (typ == FLOAT) { yylval.y_val->v_ldbl = f; } else { yylval.y_val->v_ldbl = d; } return (T_CON); } static int operator(int t, op_t o) { yylval.y_op = o; return (t); } /* * Called if lex found a leading \'. */ static int ccon(void) { int n, val, c; char cv; n = 0; val = 0; while ((c = getescc('\'')) >= 0) { val = (val << CHAR_BIT) + c; n++; } if (c == -2) { /* unterminated character constant */ error(253); } else { if (n > sizeof (int) || (n > 1 && (pflag || hflag))) { /* too many characters in character constant */ error(71); } else if (n > 1) { /* multi-character character constant */ warning(294); } else if (n == 0) { /* empty character constant */ error(73); } } if (n == 1) { cv = (char)val; val = cv; } yylval.y_val = xcalloc(1, sizeof (val_t)); yylval.y_val->v_tspec = INT; yylval.y_val->v_quad = val; return (T_CON); } /* * Called if lex found a leading L\' */ static int wccon(void) { static char buf[MB_LEN_MAX + 1]; int i, c; wchar_t wc; i = 0; while ((c = getescc('\'')) >= 0) { if (i < MB_CUR_MAX) buf[i] = (char)c; i++; } wc = 0; if (c == -2) { /* unterminated character constant */ error(253); } else if (c == 0) { /* empty character constant */ error(73); } else { if (i > MB_CUR_MAX) { i = MB_CUR_MAX; /* too many characters in character constant */ error(71); } else { buf[i] = '\0'; (void)mbtowc(NULL, NULL, 0); if (mbtowc(&wc, buf, MB_CUR_MAX) < 0) /* invalid multibyte character */ error(291); } } if ((yylval.y_val = calloc(1, sizeof (val_t))) == NULL) nomem(); yylval.y_val->v_tspec = WCHAR; yylval.y_val->v_quad = wc; return (T_CON); } /* * Read a character which is part of a character constant or of a string * and handle escapes. * * The Argument is the character which delimits the character constant or * string. * * Returns -1 if the end of the character constant or string is reached, * -2 if the EOF is reached, and the character otherwise. */ static int getescc(int d) { static int pbc = -1; int n, c, v; if (pbc == -1) { c = inpc(); } else { c = pbc; pbc = -1; } if (c == d) return (-1); switch (c) { case '\n': if (tflag) { /* newline in string or char constant */ error(254); return (-2); } return (c); case EOF: return (-2); case '\\': switch (c = inpc()) { case '"': if (tflag && d == '\'') /* \" inside character constant undef. ... */ warning(262); return ('"'); case '\'': return ('\''); case '?': if (tflag) /* \? undefined in traditional C */ warning(263); return ('?'); case '\\': return ('\\'); case 'a': if (tflag) /* \a undefined in traditional C */ warning(81); return ('\a'); case 'b': return ('\b'); case 'f': return ('\f'); case 'n': return ('\n'); case 'r': return ('\r'); case 't': return ('\t'); case 'v': if (tflag) /* \v undefined in traditional C */ warning(264); return ('\v'); case '8': case '9': /* bad octal digit %c */ warning(77, c); /* FALLTHROUGH */ case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': n = 3; v = 0; do { v = (v << 3) + (c - '0'); c = inpc(); } while (--n && isdigit(c) && (tflag || c <= '7')); if (tflag && n > 0 && isdigit(c)) /* bad octal digit %c */ warning(77, c); pbc = c; if (v > UCHAR_MAX) { /* character escape does not fit in char. */ warning(76); v &= CHAR_MASK; } return (v); case 'x': if (tflag) /* \x undefined in traditional C */ warning(82); v = 0; n = 0; while ((c = inpc()) >= 0 && isxdigit(c)) { c = isdigit(c) ? c - '0' : toupper(c) - 'A' + 10; v = (v << 4) + c; if (n >= 0) { if ((v & ~CHAR_MASK) != 0) { /* overflow in hex escape */ warning(75); n = -1; } else { n++; } } } pbc = c; if (n == 0) { /* no hex digits follow \x */ error(74); } if (n == -1) { v &= CHAR_MASK; } return (v); case '\n': return (getescc(d)); case EOF: return (-2); default: if (isprint(c)) { /* dubious escape \%c */ warning(79, c); } else { /* dubious escape \%o */ warning(80, c); } } } return (c); } /* * Called for preprocessor directives. Currently implemented are: * # lineno * # lineno "filename" */ static void directive(void) { const char *cp, *fn; char c, *eptr; size_t fnl; long ln; static int first = 1; /* Go to first non-whitespace after # */ for (cp = yytext + 1; (c = *cp) == ' ' || c == '\t'; cp++) continue; if (!isdigit((unsigned char)c)) { error: /* undefined or invalid # directive */ warning(255); return; } ln = strtol(--cp, &eptr, 10); if (cp == eptr) goto error; if ((c = *(cp = eptr)) != ' ' && c != '\t' && c != '\0') goto error; while ((c = *cp++) == ' ' || c == '\t') continue; if (c != '\0') { if (c != '"') goto error; fn = cp; while ((c = *cp) != '"' && c != '\0') cp++; if (c != '"') goto error; if ((fnl = cp++ - fn) > PATH_MAX) goto error; while ((c = *cp++) == ' ' || c == '\t') continue; #if 0 if (c != '\0') warning("extra character(s) after directive"); #endif /* empty string means stdin */ if (fnl == 0) { fn = "{standard input}"; fnl = 16; /* strlen (fn) */ } curr_pos.p_file = fnnalloc(fn, fnl); /* * If this is the first directive, the name is the name * of the C source file as specified at the command line. * It is written to the output file. */ if (first) { csrc_pos.p_file = curr_pos.p_file; outsrc(curr_pos.p_file); first = 0; } } curr_pos.p_line = (int)ln - 1; curr_pos.p_uniq = 0; if (curr_pos.p_file == csrc_pos.p_file) { csrc_pos.p_line = (int)ln - 1; csrc_pos.p_uniq = 0; } } /* * Handle lint comments. Following comments are currently understood: * ARGSUSEDn * BITFIELDTYPE * CONSTCOND CONSTANTCOND CONSTANTCONDITION * FALLTHRU FALLTHROUGH * LINTLIBRARY * LINTED NOSTRICT * LONGLONG * NOTREACHED * PRINTFLIKEn * PROTOLIB * SCANFLIKEn * VARARGSn * If one of this comments is recognized, the arguments, if any, are * parsed and a function which handles this comment is called. */ static void comment(void) { int c, lc; static struct { const char *keywd; int arg; void (*func)(int); } keywtab[] = { { "ARGSUSED", 1, argsused }, { "BITFIELDTYPE", 0, bitfieldtype }, { "CONSTCOND", 0, constcond }, { "CONSTANTCOND", 0, constcond }, { "CONSTANTCONDITION", 0, constcond }, { "FALLTHRU", 0, fallthru }, { "FALLTHROUGH", 0, fallthru }, { "LINTLIBRARY", 0, lintlib }, { "LINTED", 0, linted }, { "LONGLONG", 0, longlong }, { "NOSTRICT", 0, linted }, { "NOTREACHED", 0, notreach }, { "PRINTFLIKE", 1, printflike }, { "PROTOLIB", 1, protolib }, { "SCANFLIKE", 1, scanflike }, { "VARARGS", 1, varargs }, }; char keywd[32]; char arg[32]; int l, i, a; int eoc; eoc = 0; /* Skip white spaces after the start of the comment */ while ((c = inpc()) != EOF && isspace(c)) continue; /* Read the potential keyword to keywd */ l = 0; while (c != EOF && isupper(c) && l < sizeof (keywd) - 1) { keywd[l++] = (char)c; c = inpc(); } keywd[l] = '\0'; /* look for the keyword */ for (i = 0; i < sizeof (keywtab) / sizeof (keywtab[0]); i++) { if (strcmp(keywtab[i].keywd, keywd) == 0) break; } if (i == sizeof (keywtab) / sizeof (keywtab[0])) goto skip_rest; /* skip white spaces after the keyword */ while (c != EOF && isspace(c)) c = inpc(); /* read the argument, if the keyword accepts one and there is one */ l = 0; if (keywtab[i].arg) { while (c != EOF && isdigit(c) && l < sizeof (arg) - 1) { arg[l++] = (char)c; c = inpc(); } } arg[l] = '\0'; a = l != 0 ? atoi(arg) : -1; /* skip white spaces after the argument */ while (c != EOF && isspace(c)) c = inpc(); if (c != '*' || (c = inpc()) != '/') { if (keywtab[i].func != linted) /* extra characters in lint comment */ warning(257); } else { /* * remember that we have already found the end of the * comment */ eoc = 1; } if (keywtab[i].func != NULL) (*keywtab[i].func)(a); skip_rest: while (!eoc) { lc = c; if ((c = inpc()) == EOF) { /* unterminated comment */ error(256); break; } if (lc == '*' && c == '/') eoc = 1; } } /* * Handle // style comments */ static void slashslashcomment(void) { int c; if (!Sflag && !gflag) /* // comments only supported in C99 */ (void)gnuism(312, tflag ? "traditional" : "ANSI"); while ((c = inpc()) != EOF && c != '\n') continue; } /* * Clear flags for lint comments LINTED, LONGLONG and CONSTCOND. * clrwflgs() is called after function definitions and global and * local declarations and definitions. It is also called between * the controlling expression and the body of control statements * (if, switch, for, while). */ void clrwflgs(void) { nowarn = 0; quadflg = 0; ccflg = 0; } /* * Strings are stored in a dynamically alloceted buffer and passed * in yylval.y_xstrg to the parser. The parser or the routines called * by the parser are responsible for freeing this buffer. */ static int string(void) { u_char *s; int c; size_t len, max; strg_t *strg; if ((s = malloc(max = 64)) == NULL) nomem(); len = 0; while ((c = getescc('"')) >= 0) { /* +1 to reserve space for a trailing NUL character */ if (len + 1 == max) if ((s = realloc(s, max *= 2)) == NULL) nomem(); s[len++] = (char)c; } s[len] = '\0'; if (c == -2) /* unterminated string constant */ error(258); if ((strg = calloc(1, sizeof (strg_t))) == NULL) nomem(); strg->st_tspec = CHAR; strg->st_len = len; strg->st_cp = s; yylval.y_strg = strg; return (T_STRING); } static int wcstrg(void) { char *s; int c, i, n, wi; size_t len, max, wlen; wchar_t *ws; strg_t *strg; if ((s = malloc(max = 64)) == NULL) nomem(); len = 0; while ((c = getescc('"')) >= 0) { /* +1 to save space for a trailing NUL character */ if (len + 1 >= max) if ((s = realloc(s, max *= 2)) == NULL) nomem(); s[len++] = (char)c; } s[len] = '\0'; if (c == -2) /* unterminated string constant */ error(258); /* get length of wide character string */ (void)mblen(NULL, 0); for (i = 0, wlen = 0; i < len; i += n, wlen++) { if ((n = mblen(&s[i], MB_CUR_MAX)) == -1) { /* invalid multibyte character */ error(291); break; } if (n == 0) n = 1; } if ((ws = malloc((wlen + 1) * sizeof (wchar_t))) == NULL) nomem(); /* convert from multibyte to wide char */ (void)mbtowc(NULL, NULL, 0); for (i = 0, wi = 0; i < len; i += n, wi++) { if ((n = mbtowc(&ws[wi], &s[i], MB_CUR_MAX)) == -1) break; if (n == 0) n = 1; } ws[wi] = 0; free(s); if ((strg = calloc(1, sizeof (strg_t))) == NULL) nomem(); strg->st_tspec = WCHAR; strg->st_len = wlen; strg->st_wcp = ws; yylval.y_strg = strg; return (T_STRING); } /* * As noted above the scanner does not create new symbol table entries * for symbols it cannot find in the symbol table. This is to avoid * putting undeclared symbols into the symbol table if a syntax error * occurs. * * getsym() is called as soon as it is probably ok to put the symbol to * the symbol table. This does not mean that it is not possible that * symbols are put to the symbol table which are than not completely * declared due to syntax errors. To avoid too many problems in this * case symbols get type int in getsym(). * * XXX calls to getsym() should be delayed until decl1*() is called */ sym_t * getsym(sbuf_t *sb) { dinfo_t *di; char *s; sym_t *sym; sym = sb->sb_sym; /* * During member declaration it is possible that name() looked * for symbols of type FVFT, although it should have looked for * symbols of type FTAG. Same can happen for labels. Both cases * are compensated here. */ if (symtyp == FMOS || symtyp == FLAB) { if (sym == NULL || sym->s_kind == FVFT) sym = search(sb); } if (sym != NULL) { if (sym->s_kind != symtyp) LERROR("storesym()"); symtyp = FVFT; freesb(sb); return (sym); } /* create a new symbol table entry */ /* labels must always be allocated at level 1 (outhermost block) */ if (symtyp == FLAB) { sym = getlblk(1, sizeof (sym_t)); s = getlblk(1, sb->sb_len + 1); (void)memcpy(s, sb->sb_name, sb->sb_len + 1); sym->s_name = s; sym->s_blklev = 1; di = dcs; while (di->d_nxt != NULL && di->d_nxt->d_nxt != NULL) di = di->d_nxt; if (di->d_ctx != AUTO) LERROR("storesym()"); } else { sym = getblk(sizeof (sym_t)); sym->s_name = sb->sb_name; sym->s_blklev = blklev; di = dcs; } UNIQUE_CURR_POS(sym->s_dpos); if ((sym->s_kind = symtyp) != FLAB) sym->s_type = gettyp(INT); symtyp = FVFT; if ((sym->s_link = symtab[sb->sb_hash]) != NULL) symtab[sb->sb_hash]->s_rlink = &sym->s_link; (symtab[sb->sb_hash] = sym)->s_rlink = &symtab[sb->sb_hash]; *di->d_ldlsym = sym; di->d_ldlsym = &sym->s_dlnxt; freesb(sb); return (sym); } /* * Construct a temporary symbol. The symbol starts with a digit, so that * it is illegal. */ sym_t * mktempsym(type_t *t) { static int n = 0; int h; char *s = getlblk(blklev, 64); sym_t *sym = getblk(sizeof (sym_t)); (void)snprintf(s, 64, "%.8d_tmp", n++); h = hash(s); sym->s_name = s; sym->s_type = t; sym->s_blklev = blklev; sym->s_scl = AUTO; sym->s_kind = FVFT; sym->s_used = 1; sym->s_set = 1; if ((sym->s_link = symtab[h]) != NULL) symtab[h]->s_rlink = &sym->s_link; (symtab[h] = sym)->s_rlink = &symtab[h]; *dcs->d_ldlsym = sym; dcs->d_ldlsym = &sym->s_dlnxt; return sym; } /* * Remove a symbol forever from the symbol table. s_blklev * is set to -1 to avoid that the symbol will later be put * back to the symbol table. */ void rmsym(sym_t *sym) { if ((*sym->s_rlink = sym->s_link) != NULL) sym->s_link->s_rlink = sym->s_rlink; sym->s_blklev = -1; sym->s_link = NULL; } /* * Remove a list of symbols declared at one level from the symbol * table. */ void rmsyms(sym_t *syms) { sym_t *sym; for (sym = syms; sym != NULL; sym = sym->s_dlnxt) { if (sym->s_blklev != -1) { if ((*sym->s_rlink = sym->s_link) != NULL) sym->s_link->s_rlink = sym->s_rlink; sym->s_link = NULL; sym->s_rlink = NULL; } } } /* * Put a symbol into the symbol table */ void inssym(int bl, sym_t *sym) { int h; h = hash(sym->s_name); if ((sym->s_link = symtab[h]) != NULL) symtab[h]->s_rlink = &sym->s_link; (symtab[h] = sym)->s_rlink = &symtab[h]; sym->s_blklev = bl; if (sym->s_link != NULL && sym->s_blklev < sym->s_link->s_blklev) LERROR("inssym()"); } /* * Called at level 0 after syntax errors * Removes all symbols which are not declared at level 0 from the * symbol table. Also frees all memory which is not associated with * level 0. */ void cleanup(void) { sym_t *sym, *nsym; int i; for (i = 0; i < HSHSIZ1; i++) { for (sym = symtab[i]; sym != NULL; sym = nsym) { nsym = sym->s_link; if (sym->s_blklev >= 1) { if ((*sym->s_rlink = nsym) != NULL) nsym->s_rlink = sym->s_rlink; } } } for (i = mblklev; i > 0; i--) freelblk(i); } /* * Create a new symbol with the name of an existing symbol. */ sym_t * pushdown(sym_t *sym) { int h; sym_t *nsym; h = hash(sym->s_name); nsym = getblk(sizeof (sym_t)); if (sym->s_blklev > blklev) LERROR("pushdown()"); nsym->s_name = sym->s_name; UNIQUE_CURR_POS(nsym->s_dpos); nsym->s_kind = sym->s_kind; nsym->s_blklev = blklev; if ((nsym->s_link = symtab[h]) != NULL) symtab[h]->s_rlink = &nsym->s_link; (symtab[h] = nsym)->s_rlink = &symtab[h]; *dcs->d_ldlsym = nsym; dcs->d_ldlsym = &nsym->s_dlnxt; return (nsym); } /* * Free any dynamically allocated memory referenced by * the value stack or yylval. * The type of information in yylval is described by tok. */ void freeyyv(void *sp, int tok) { if (tok == T_NAME || tok == T_TYPENAME) { sbuf_t *sb = *(sbuf_t **)sp; freesb(sb); } else if (tok == T_CON) { val_t *val = *(val_t **)sp; free(val); } else if (tok == T_STRING) { strg_t *strg = *(strg_t **)sp; if (strg->st_tspec == CHAR) { free(strg->st_cp); } else if (strg->st_tspec == WCHAR) { free(strg->st_wcp); } else { LERROR("fryylv()"); } free(strg); } } Index: head/usr.bin/xlint/lint1/tree.c =================================================================== --- head/usr.bin/xlint/lint1/tree.c (revision 286613) +++ head/usr.bin/xlint/lint1/tree.c (revision 286614) @@ -1,4040 +1,4040 @@ /* $NetBSD: tree.c,v 1.45 2008/03/04 02:41:46 christos Exp $ */ /* * Copyright (c) 1994, 1995 Jochen Pohl * All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Jochen Pohl for * The NetBSD Project. * 4. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #if defined(__RCSID) && !defined(lint) __RCSID("$NetBSD: tree.c,v 1.45 2008/03/04 02:41:46 christos Exp $"); #endif __FBSDID("$FreeBSD$"); #include #include #include #include #include #include "lint1.h" #include "cgram.h" /* Various flags for each operator. */ static mod_t modtab[NOPS]; static tnode_t *getinode(tspec_t, int64_t); static void ptrcmpok(op_t, tnode_t *, tnode_t *); static int asgntypok(op_t, int, tnode_t *, tnode_t *); static void chkbeop(op_t, tnode_t *, tnode_t *); static void chkeop2(op_t, int, tnode_t *, tnode_t *); static void chkeop1(op_t, int, tnode_t *, tnode_t *); static tnode_t *mktnode(op_t, type_t *, tnode_t *, tnode_t *); static void balance(op_t, tnode_t **, tnode_t **); static void incompat(op_t, tspec_t, tspec_t); static void illptrc(mod_t *, type_t *, type_t *); static void mrgqual(type_t **, type_t *, type_t *); static int conmemb(type_t *); static void ptconv(int, tspec_t, tspec_t, type_t *, tnode_t *); static void iiconv(op_t, int, tspec_t, tspec_t, type_t *, tnode_t *); static void piconv(op_t, tspec_t, type_t *, tnode_t *); static void ppconv(op_t, tnode_t *, type_t *); static tnode_t *bldstr(op_t, tnode_t *, tnode_t *); static tnode_t *bldincdec(op_t, tnode_t *); static tnode_t *bldamper(tnode_t *, int); static tnode_t *bldplmi(op_t, tnode_t *, tnode_t *); static tnode_t *bldshft(op_t, tnode_t *, tnode_t *); static tnode_t *bldcol(tnode_t *, tnode_t *); static tnode_t *bldasgn(op_t, tnode_t *, tnode_t *); static tnode_t *plength(type_t *); static tnode_t *fold(tnode_t *); static tnode_t *foldtst(tnode_t *); static tnode_t *foldflt(tnode_t *); static tnode_t *chkfarg(type_t *, tnode_t *); static tnode_t *parg(int, type_t *, tnode_t *); static void nulleff(tnode_t *); static void displexpr(tnode_t *, int); static void chkaidx(tnode_t *, int); static void chkcomp(op_t, tnode_t *, tnode_t *); static void precconf(tnode_t *); /* * Initialize mods of operators. */ void initmtab(void) { static struct { op_t op; mod_t m; } imods[] = { { ARROW, { 1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0, "->" } }, { POINT, { 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, "." } }, { NOT, { 0,1,0,1,0,1,0,1,0,0,0,0,0,0,0,1,0, "!" } }, { COMPL, { 0,0,1,0,0,1,1,0,0,0,0,0,0,0,0,1,1, "~" } }, { INCBEF, { 0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0, "prefix++" } }, { DECBEF, { 0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0, "prefix--" } }, { INCAFT, { 0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0, "postfix++" } }, { DECAFT, { 0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0, "postfix--" } }, { UPLUS, { 0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,1,1, "unary +" } }, { UMINUS, { 0,0,0,0,1,1,1,0,0,0,1,0,0,0,0,1,1, "unary -" } }, { STAR, { 0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0, "unary *" } }, { AMPER, { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, "unary &" } }, { MULT, { 1,0,0,0,1,1,1,0,1,0,0,1,0,0,0,1,1, "*" } }, { DIV, { 1,0,0,0,1,1,1,0,1,0,1,1,0,0,0,1,1, "/" } }, { MOD, { 1,0,1,0,0,1,1,0,1,0,1,1,0,0,0,1,1, "%" } }, { PLUS, { 1,0,0,1,0,1,1,0,1,0,0,0,0,0,0,1,0, "+" } }, { MINUS, { 1,0,0,1,0,1,1,0,1,0,0,0,0,0,0,1,0, "-" } }, { SHL, { 1,0,1,0,0,1,1,0,0,0,0,0,1,0,0,1,1, "<<" } }, { SHR, { 1,0,1,0,0,1,1,0,0,0,1,0,1,0,0,1,1, ">>" } }, { LT, { 1,1,0,1,0,1,1,0,1,0,1,1,0,1,1,0,1, "<" } }, { LE, { 1,1,0,1,0,1,1,0,1,0,1,1,0,1,1,0,1, "<=" } }, { GT, { 1,1,0,1,0,1,1,0,1,0,1,1,0,1,1,0,1, ">" } }, { GE, { 1,1,0,1,0,1,1,0,1,0,1,1,0,1,1,0,1, ">=" } }, { EQ, { 1,1,0,1,0,1,1,0,1,0,0,0,0,1,1,0,1, "==" } }, { NE, { 1,1,0,1,0,1,1,0,1,0,0,0,0,1,1,0,1, "!=" } }, { AND, { 1,0,1,0,0,1,1,0,1,0,0,0,1,0,0,1,0, "&" } }, { XOR, { 1,0,1,0,0,1,1,0,1,0,0,0,1,0,0,1,0, "^" } }, { OR, { 1,0,1,0,0,1,1,0,1,0,0,0,1,0,0,1,0, "|" } }, { LOGAND, { 1,1,0,1,0,1,0,1,0,0,0,0,0,0,0,1,0, "&&" } }, { LOGOR, { 1,1,0,1,0,1,0,1,0,0,0,0,1,0,0,1,0, "||" } }, { QUEST, { 1,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0, "?" } }, { COLON, { 1,0,0,0,0,0,1,0,1,0,0,0,0,0,1,0,0, ":" } }, { ASSIGN, { 1,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0, "=" } }, { MULASS, { 1,0,0,0,1,0,0,0,0,1,0,0,0,0,0,1,0, "*=" } }, { DIVASS, { 1,0,0,0,1,0,0,0,0,1,0,1,0,0,0,1,0, "/=" } }, { MODASS, { 1,0,1,0,0,0,0,0,0,1,0,1,0,0,0,1,0, "%=" } }, { ADDASS, { 1,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0, "+=" } }, { SUBASS, { 1,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0, "-=" } }, { SHLASS, { 1,0,1,0,0,0,0,0,0,1,0,0,0,0,0,1,0, "<<=" } }, { SHRASS, { 1,0,1,0,0,0,0,0,0,1,0,0,0,0,0,1,0, ">>=" } }, { ANDASS, { 1,0,1,0,0,0,0,0,0,1,0,0,0,0,0,1,0, "&=" } }, { XORASS, { 1,0,1,0,0,0,0,0,0,1,0,0,0,0,0,1,0, "^=" } }, { ORASS, { 1,0,1,0,0,0,0,0,0,1,0,0,0,0,0,1,0, "|=" } }, { NAME, { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, "NAME" } }, { CON, { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, "CON" } }, { STRING, { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, "STRING" } }, { FSEL, { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, "FSEL" } }, { CALL, { 1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, "CALL" } }, { COMMA, { 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, "," } }, { CVT, { 0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0, "CVT" } }, { ICALL, { 1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, "ICALL" } }, { LOAD, { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, "LOAD" } }, { PUSH, { 0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0, "PUSH" } }, { RETURN, { 1,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0, "RETURN" } }, { INIT, { 1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0, "INIT" } }, { FARG, { 1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0, "FARG" } }, { NOOP } }; int i; for (i = 0; imods[i].op != NOOP; i++) STRUCT_ASSIGN(modtab[imods[i].op], imods[i].m); } /* * Increase degree of reference. * This is most often used to change type "T" in type "pointer to T". */ type_t * incref(type_t *tp, tspec_t t) { type_t *tp2; tp2 = getblk(sizeof (type_t)); tp2->t_tspec = t; tp2->t_subt = tp; return (tp2); } /* * same for use in expressions */ type_t * tincref(type_t *tp, tspec_t t) { type_t *tp2; tp2 = tgetblk(sizeof (type_t)); tp2->t_tspec = t; tp2->t_subt = tp; return (tp2); } /* * Create a node for a constant. */ tnode_t * getcnode(type_t *tp, val_t *v) { tnode_t *n; n = getnode(); n->tn_op = CON; n->tn_type = tp; n->tn_val = tgetblk(sizeof (val_t)); n->tn_val->v_tspec = tp->t_tspec; n->tn_val->v_ansiu = v->v_ansiu; n->tn_val->v_u = v->v_u; free(v); return (n); } /* * Create a node for an integer constant. */ static tnode_t * getinode(tspec_t t, int64_t q) { tnode_t *n; n = getnode(); n->tn_op = CON; n->tn_type = gettyp(t); n->tn_val = tgetblk(sizeof (val_t)); n->tn_val->v_tspec = t; n->tn_val->v_quad = q; return (n); } /* * Create a node for a name (symbol table entry). * ntok is the token which follows the name. */ tnode_t * getnnode(sym_t *sym, int ntok) { tnode_t *n; if (sym->s_scl == NOSCL) { sym->s_scl = EXTERN; sym->s_def = DECL; if (ntok == T_LPARN) { if (sflag) { /* function implicitly declared to ... */ warning(215); } /* * XXX if tflag is set the symbol should be * exported to level 0 */ sym->s_type = incref(sym->s_type, FUNC); } else { if (!blklev) { /* %s undefined */ error(99, sym->s_name); } else { int fixtype; if (strcmp(sym->s_name, "__FUNCTION__") == 0) { gnuism(316); fixtype = 1; } else if (strcmp(sym->s_name, "__func__") == 0) { if (!Sflag) warning(317); fixtype = 1; } else { error(99, sym->s_name); fixtype = 0; } if (fixtype) { sym->s_type = incref(gettyp(CHAR), PTR); sym->s_type->t_const = 1; } } } } if (sym->s_kind != FVFT && sym->s_kind != FMOS) LERROR("getnnode()"); n = getnode(); n->tn_type = sym->s_type; if (sym->s_scl != ENUMCON) { n->tn_op = NAME; n->tn_sym = sym; if (sym->s_kind == FVFT && sym->s_type->t_tspec != FUNC) n->tn_lvalue = 1; } else { n->tn_op = CON; n->tn_val = tgetblk(sizeof (val_t)); *n->tn_val = sym->s_value; } return (n); } /* * Create a node for a string. */ tnode_t * getsnode(strg_t *strg) { size_t len; tnode_t *n; len = strg->st_len; n = getnode(); n->tn_op = STRING; n->tn_type = tincref(gettyp(strg->st_tspec), ARRAY); n->tn_type->t_dim = len + 1; n->tn_lvalue = 1; n->tn_strg = tgetblk(sizeof (strg_t)); n->tn_strg->st_tspec = strg->st_tspec; n->tn_strg->st_len = len; if (strg->st_tspec == CHAR) { n->tn_strg->st_cp = tgetblk(len + 1); (void)memcpy(n->tn_strg->st_cp, strg->st_cp, len + 1); free(strg->st_cp); } else { n->tn_strg->st_wcp = tgetblk((len + 1) * sizeof (wchar_t)); (void)memcpy(n->tn_strg->st_wcp, strg->st_wcp, (len + 1) * sizeof (wchar_t)); free(strg->st_wcp); } free(strg); return (n); } /* * Returns a symbol which has the same name as the msym argument and is a * member of the struct or union specified by the tn argument. */ sym_t * strmemb(tnode_t *tn, op_t op, sym_t *msym) { str_t *str; type_t *tp; sym_t *sym, *csym; int eq; tspec_t t; /* * Remove the member if it was unknown until now (Which means * that no defined struct or union has a member with the same name). */ if (msym->s_scl == NOSCL) { /* undefined struct/union member: %s */ fprintf(stderr, "3. %s\n", msym->s_name); error(101, msym->s_name); rmsym(msym); msym->s_kind = FMOS; msym->s_scl = MOS; msym->s_styp = tgetblk(sizeof (str_t)); msym->s_styp->stag = tgetblk(sizeof (sym_t)); msym->s_styp->stag->s_name = unnamed; msym->s_value.v_tspec = INT; return (msym); } /* Set str to the tag of which msym is expected to be a member. */ str = NULL; t = (tp = tn->tn_type)->t_tspec; if (op == POINT) { if (t == STRUCT || t == UNION) str = tp->t_str; } else if (op == ARROW && t == PTR) { t = (tp = tp->t_subt)->t_tspec; if (t == STRUCT || t == UNION) str = tp->t_str; } /* * If this struct/union has a member with the name of msym, return * return this it. */ if (str != NULL) { for (sym = msym; sym != NULL; sym = sym->s_link) { if (sym->s_scl != MOS && sym->s_scl != MOU) continue; if (sym->s_styp != str) continue; if (strcmp(sym->s_name, msym->s_name) != 0) continue; return (sym); } } /* * Set eq to 0 if there are struct/union members with the same name * and different types and/or offsets. */ eq = 1; for (csym = msym; csym != NULL; csym = csym->s_link) { if (csym->s_scl != MOS && csym->s_scl != MOU) continue; if (strcmp(msym->s_name, csym->s_name) != 0) continue; for (sym = csym->s_link ; sym != NULL; sym = sym->s_link) { int w; if (sym->s_scl != MOS && sym->s_scl != MOU) continue; if (strcmp(csym->s_name, sym->s_name) != 0) continue; if (csym->s_value.v_quad != sym->s_value.v_quad) { eq = 0; break; } w = 0; eq = eqtype(csym->s_type, sym->s_type, 0, 0, &w) && !w; if (!eq) break; if (csym->s_field != sym->s_field) { eq = 0; break; } if (csym->s_field) { type_t *tp1, *tp2; tp1 = csym->s_type; tp2 = sym->s_type; if (tp1->t_flen != tp2->t_flen) { eq = 0; break; } if (tp1->t_foffs != tp2->t_foffs) { eq = 0; break; } } } if (!eq) break; } /* * Now handle the case in which the left operand refers really * to a struct/union, but the right operand is not member of it. */ if (str != NULL) { /* illegal member use: %s */ if (eq && tflag) { warning(102, msym->s_name); } else { error(102, msym->s_name); } return (msym); } /* * Now the left operand of ARROW does not point to a struct/union * or the left operand of POINT is no struct/union. */ if (eq) { if (op == POINT) { /* left operand of "." must be struct/union object */ if (tflag) { warning(103); } else { error(103); } } else { /* left operand of "->" must be pointer to ... */ if (tflag && tn->tn_type->t_tspec == PTR) { warning(104); } else { error(104); } } } else { if (tflag) { /* non-unique member requires struct/union %s */ error(105, op == POINT ? "object" : "pointer"); } else { /* unacceptable operand of %s */ error(111, modtab[op].m_name); } } return (msym); } /* * Create a tree node. Called for most operands except function calls, * sizeof and casts. * * op operator * ln left operand * rn if not NULL, right operand */ tnode_t * build(op_t op, tnode_t *ln, tnode_t *rn) { mod_t *mp; tnode_t *ntn; type_t *rtp; mp = &modtab[op]; /* If there was an error in one of the operands, return. */ if (ln == NULL || (mp->m_binary && rn == NULL)) return (NULL); /* * Apply class conversions to the left operand, but only if its - * value is needed or it is compaired with null. + * value is needed or it is compared with null. */ if (mp->m_vctx || mp->m_tctx) ln = cconv(ln); /* * The right operand is almost always in a test or value context, * except if it is a struct or union member. */ if (mp->m_binary && op != ARROW && op != POINT) rn = cconv(rn); /* * Print some warnings for comparisons of unsigned values with * constants lower than or equal to null. This must be done * before promote() because otherwise unsigned char and unsigned * short would be promoted to int. Also types are tested to be * CHAR, which would also become int. */ if (mp->m_comp) chkcomp(op, ln, rn); /* * Promote the left operand if it is in a test or value context */ if (mp->m_vctx || mp->m_tctx) ln = promote(op, 0, ln); /* * Promote the right operand, but only if it is no struct or * union member, or if it is not to be assigned to the left operand */ if (mp->m_binary && op != ARROW && op != POINT && op != ASSIGN && op != RETURN) { rn = promote(op, 0, rn); } /* * If the result of the operation is different for signed or * unsigned operands and one of the operands is signed only in * ANSI C, print a warning. */ if (mp->m_tlansiu && ln->tn_op == CON && ln->tn_val->v_ansiu) { /* ANSI C treats constant as unsigned, op %s */ warning(218, mp->m_name); ln->tn_val->v_ansiu = 0; } if (mp->m_transiu && rn->tn_op == CON && rn->tn_val->v_ansiu) { /* ANSI C treats constant as unsigned, op %s */ warning(218, mp->m_name); rn->tn_val->v_ansiu = 0; } /* Make sure both operands are of the same type */ if (mp->m_balance || (tflag && (op == SHL || op == SHR))) balance(op, &ln, &rn); /* * Check types for compatibility with the operation and mutual - * compatibility. Return if there are serios problems. + * compatibility. Return if there are serious problems. */ if (!typeok(op, 0, ln, rn)) return (NULL); /* And now create the node. */ switch (op) { case POINT: case ARROW: ntn = bldstr(op, ln, rn); break; case INCAFT: case DECAFT: case INCBEF: case DECBEF: ntn = bldincdec(op, ln); break; case AMPER: ntn = bldamper(ln, 0); break; case STAR: ntn = mktnode(STAR, ln->tn_type->t_subt, ln, NULL); break; case PLUS: case MINUS: ntn = bldplmi(op, ln, rn); break; case SHL: case SHR: ntn = bldshft(op, ln, rn); break; case COLON: ntn = bldcol(ln, rn); break; case ASSIGN: case MULASS: case DIVASS: case MODASS: case ADDASS: case SUBASS: case SHLASS: case SHRASS: case ANDASS: case XORASS: case ORASS: case RETURN: ntn = bldasgn(op, ln, rn); break; case COMMA: case QUEST: ntn = mktnode(op, rn->tn_type, ln, rn); break; default: rtp = mp->m_logop ? gettyp(INT) : ln->tn_type; if (!mp->m_binary && rn != NULL) LERROR("build()"); ntn = mktnode(op, rtp, ln, rn); break; } /* Return if an error occurred. */ if (ntn == NULL) return (NULL); /* Print a warning if precedence confusion is possible */ if (mp->m_tpconf) precconf(ntn); /* * Print a warning if one of the operands is in a context where * it is compared with null and if this operand is a constant. */ if (mp->m_tctx) { if (ln->tn_op == CON || ((mp->m_binary && op != QUEST) && rn->tn_op == CON)) { if (hflag && !ccflg) /* constant in conditional context */ warning(161); } } /* Fold if the operator requires it */ if (mp->m_fold) { if (ln->tn_op == CON && (!mp->m_binary || rn->tn_op == CON)) { if (mp->m_tctx) { ntn = foldtst(ntn); } else if (isftyp(ntn->tn_type->t_tspec)) { ntn = foldflt(ntn); } else { ntn = fold(ntn); } } else if (op == QUEST && ln->tn_op == CON) { ntn = ln->tn_val->v_quad ? rn->tn_left : rn->tn_right; } } return (ntn); } /* * Perform class conversions. * * Arrays of type T are converted into pointers to type T. * Functions are converted to pointers to functions. * Lvalues are converted to rvalues. */ tnode_t * cconv(tnode_t *tn) { type_t *tp; /* * Array-lvalue (array of type T) is converted into rvalue * (pointer to type T) */ if (tn->tn_type->t_tspec == ARRAY) { if (!tn->tn_lvalue) { - /* %soperand of '%s' must be lvalue */ + /* operand of '%s' must be lvalue */ /* XXX print correct operator */ (void)gnuism(114, "", modtab[AMPER].m_name); } tn = mktnode(AMPER, tincref(tn->tn_type->t_subt, PTR), tn, NULL); } /* * Expression of type function (function with return value of type T) * in rvalue-expression (pointer to function with return value * of type T) */ if (tn->tn_type->t_tspec == FUNC) tn = bldamper(tn, 1); /* lvalue to rvalue */ if (tn->tn_lvalue) { tp = tduptyp(tn->tn_type); tp->t_const = tp->t_volatile = 0; tn = mktnode(LOAD, tp, tn, NULL); } return (tn); } /* * Perform most type checks. First the types are checked using - * informations from modtab[]. After that it is done by hand for + * information from modtab[]. After that it is done by hand for * more complicated operators and type combinations. * * If the types are ok, typeok() returns 1, otherwise 0. */ int typeok(op_t op, int arg, tnode_t *ln, tnode_t *rn) { mod_t *mp; tspec_t lt, rt = NOTSPEC, lst = NOTSPEC, rst = NOTSPEC, olt = NOTSPEC, ort = NOTSPEC; type_t *ltp, *rtp = NULL, *lstp = NULL, *rstp = NULL; tnode_t *tn; mp = &modtab[op]; if ((ltp = ln->tn_type) == NULL) LERROR("typeok()"); if ((lt = ltp->t_tspec) == PTR) lst = (lstp = ltp->t_subt)->t_tspec; if (mp->m_binary) { if ((rtp = rn->tn_type) == NULL) LERROR("typeok()"); if ((rt = rtp->t_tspec) == PTR) rst = (rstp = rtp->t_subt)->t_tspec; } if (mp->m_rqint) { - /* integertypes required */ + /* integer types required */ if (!isityp(lt) || (mp->m_binary && !isityp(rt))) { incompat(op, lt, rt); return (0); } } else if (mp->m_rqsclt) { /* scalar types required */ if (!issclt(lt) || (mp->m_binary && !issclt(rt))) { incompat(op, lt, rt); return (0); } } else if (mp->m_rqatyp) { /* arithmetic types required */ if (!isatyp(lt) || (mp->m_binary && !isatyp(rt))) { incompat(op, lt, rt); return (0); } } if (op == SHL || op == SHR || op == SHLASS || op == SHRASS) { /* * For these operations we need the types before promotion * and balancing. */ for (tn=ln; tn->tn_op==CVT && !tn->tn_cast; tn=tn->tn_left) continue; olt = tn->tn_type->t_tspec; for (tn=rn; tn->tn_op==CVT && !tn->tn_cast; tn=tn->tn_left) continue; ort = tn->tn_type->t_tspec; } switch (op) { case POINT: /* * Most errors required by ANSI C are reported in strmemb(). - * Here we only must check for totaly wrong things. + * Here we only must check for totally wrong things. */ if (lt == FUNC || lt == VOID || ltp->t_isfield || ((lt != STRUCT && lt != UNION) && !ln->tn_lvalue)) { - /* Without tflag we got already an error */ + /* Without tflag we already got an error */ if (tflag) /* unacceptable operand of %s */ error(111, mp->m_name); return (0); } /* Now we have an object we can create a pointer to */ break; case ARROW: if (lt != PTR && !(tflag && isityp(lt))) { /* Without tflag we got already an error */ if (tflag) - /* unacceptabel operand of %s */ + /* unacceptable operand of %s */ error(111, mp->m_name); return (0); } break; case INCAFT: case DECAFT: case INCBEF: case DECBEF: /* operands have scalar types (checked above) */ if (!ln->tn_lvalue) { if (ln->tn_op == CVT && ln->tn_cast && ln->tn_left->tn_op == LOAD) { /* a cast does not yield an lvalue */ error(163); } - /* %soperand of %s must be lvalue */ + /* operand of %s must be lvalue */ error(114, "", mp->m_name); return (0); } else if (ltp->t_const) { - /* %soperand of %s must be modifiable lvalue */ + /* operand of %s must be modifiable lvalue */ if (!tflag) warning(115, "", mp->m_name); } break; case AMPER: if (lt == ARRAY || lt == FUNC) { /* ok, a warning comes later (in bldamper()) */ } else if (!ln->tn_lvalue) { if (ln->tn_op == CVT && ln->tn_cast && ln->tn_left->tn_op == LOAD) { /* a cast does not yield an lvalue */ error(163); } /* %soperand of %s must be lvalue */ error(114, "", mp->m_name); return (0); } else if (issclt(lt)) { if (ltp->t_isfield) { /* cannot take address of bit-field */ error(112); return (0); } } else if (lt != STRUCT && lt != UNION) { /* unacceptable operand of %s */ error(111, mp->m_name); return (0); } if (ln->tn_op == NAME && ln->tn_sym->s_reg) { /* cannot take address of register %s */ error(113, ln->tn_sym->s_name); return (0); } break; case STAR: /* until now there were no type checks for this operator */ if (lt != PTR) { /* cannot dereference non-pointer type */ error(96); return (0); } break; case PLUS: /* operands have scalar types (checked above) */ if ((lt == PTR && !isityp(rt)) || (rt == PTR && !isityp(lt))) { incompat(op, lt, rt); return (0); } break; case MINUS: /* operands have scalar types (checked above) */ if (lt == PTR && (!isityp(rt) && rt != PTR)) { incompat(op, lt, rt); return (0); } else if (rt == PTR && lt != PTR) { incompat(op, lt, rt); return (0); } if (lt == PTR && rt == PTR) { if (!eqtype(lstp, rstp, 1, 0, NULL)) { /* illegal pointer subtraction */ error(116); } } break; case SHR: /* operands have integer types (checked above) */ if (pflag && !isutyp(lt)) { /* * The left operand is signed. This means that * the operation is (possibly) nonportable. */ /* bitwise operation on signed value nonportable */ if (ln->tn_op != CON) { /* possibly nonportable */ warning(117); } else if (ln->tn_val->v_quad < 0) { warning(120); } } else if (!tflag && !sflag && !isutyp(olt) && isutyp(ort)) { /* * The left operand would become unsigned in * traditional C. */ if (hflag && (ln->tn_op != CON || ln->tn_val->v_quad < 0)) { /* semantics of %s change in ANSI C; use ... */ warning(118, mp->m_name); } } else if (!tflag && !sflag && !isutyp(olt) && !isutyp(ort) && psize(lt) < psize(rt)) { /* * In traditional C the left operand would be extended, * possibly with 1, and then shifted. */ if (hflag && (ln->tn_op != CON || ln->tn_val->v_quad < 0)) { /* semantics of %s change in ANSI C; use ... */ warning(118, mp->m_name); } } goto shift; case SHL: /* * ANSI C does not perform balancing for shift operations, * but traditional C does. If the width of the right operand * is greather than the width of the left operand, than in * traditional C the left operand would be extendet to the * width of the right operand. For SHL this may result in * different results. */ if (psize(lt) < psize(rt)) { /* * XXX If both operands are constant make sure * that there is really a differencs between * ANSI C and traditional C. */ if (hflag) /* semantics of %s change in ANSI C; use ... */ warning(118, mp->m_name); } shift: if (rn->tn_op == CON) { if (!isutyp(rt) && rn->tn_val->v_quad < 0) { /* negative shift */ warning(121); } else if ((uint64_t)rn->tn_val->v_quad == size(lt)) { /* shift equal to size fo object */ warning(267); } else if ((uint64_t)rn->tn_val->v_quad > size(lt)) { /* shift greater than size of object */ warning(122); } } break; case EQ: case NE: /* * Accept some things which are allowed with EQ and NE, * but not with ordered comparisons. */ if (lt == PTR && ((rt == PTR && rst == VOID) || isityp(rt))) { if (rn->tn_op == CON && rn->tn_val->v_quad == 0) break; } if (rt == PTR && ((lt == PTR && lst == VOID) || isityp(lt))) { if (ln->tn_op == CON && ln->tn_val->v_quad == 0) break; } /* FALLTHROUGH */ case LT: case GT: case LE: case GE: if ((lt == PTR || rt == PTR) && lt != rt) { if (isityp(lt) || isityp(rt)) { /* illegal comb. of pointer and int., op %s */ warning(123, mp->m_name); } else { incompat(op, lt, rt); return (0); } } else if (lt == PTR && rt == PTR) { ptrcmpok(op, ln, rn); } break; case QUEST: if (!issclt(lt)) { /* first operand must have scalar type, op ? : */ error(170); return (0); } while (rn->tn_op == CVT) rn = rn->tn_left; if (rn->tn_op != COLON) LERROR("typeok()"); break; case COLON: if (isatyp(lt) && isatyp(rt)) break; if (lt == STRUCT && rt == STRUCT && ltp->t_str == rtp->t_str) break; if (lt == UNION && rt == UNION && ltp->t_str == rtp->t_str) break; /* combination of any pointer and 0, 0L or (void *)0 is ok */ if (lt == PTR && ((rt == PTR && rst == VOID) || isityp(rt))) { if (rn->tn_op == CON && rn->tn_val->v_quad == 0) break; } if (rt == PTR && ((lt == PTR && lst == VOID) || isityp(lt))) { if (ln->tn_op == CON && ln->tn_val->v_quad == 0) break; } if ((lt == PTR && isityp(rt)) || (isityp(lt) && rt == PTR)) { /* illegal comb. of ptr. and int., op %s */ warning(123, mp->m_name); break; } if (lt == VOID || rt == VOID) { if (lt != VOID || rt != VOID) /* incompatible types in conditional */ warning(126); break; } if (lt == PTR && rt == PTR && ((lst == VOID && rst == FUNC) || (lst == FUNC && rst == VOID))) { /* (void *)0 handled above */ if (sflag) /* ANSI C forbids conv. of %s to %s, op %s */ warning(305, "function pointer", "'void *'", mp->m_name); break; } if (rt == PTR && lt == PTR) { if (!eqtype(lstp, rstp, 1, 0, NULL)) illptrc(mp, ltp, rtp); break; } /* incompatible types in conditional */ error(126); return (0); case ASSIGN: case INIT: case FARG: case RETURN: if (!asgntypok(op, arg, ln, rn)) return (0); goto assign; case MULASS: case DIVASS: case MODASS: goto assign; case ADDASS: case SUBASS: /* operands have scalar types (checked above) */ if ((lt == PTR && !isityp(rt)) || rt == PTR) { incompat(op, lt, rt); return (0); } goto assign; case SHLASS: goto assign; case SHRASS: if (pflag && !isutyp(lt) && !(tflag && isutyp(rt))) { /* bitwise operation on s.v. possibly nonportabel */ warning(117); } goto assign; case ANDASS: case XORASS: case ORASS: goto assign; assign: if (!ln->tn_lvalue) { if (ln->tn_op == CVT && ln->tn_cast && ln->tn_left->tn_op == LOAD) { /* a cast does not yield an lvalue */ error(163); } /* %soperand of %s must be lvalue */ error(114, "left ", mp->m_name); return (0); } else if (ltp->t_const || ((lt == STRUCT || lt == UNION) && conmemb(ltp))) { /* %soperand of %s must be modifiable lvalue */ if (!tflag) warning(115, "left ", mp->m_name); } break; case COMMA: if (!modtab[ln->tn_op].m_sideeff) nulleff(ln); break; /* LINTED (enumeration values not handled in switch) */ case CON: case CASE: case PUSH: case LOAD: case ICALL: case CVT: case CALL: case FSEL: case STRING: case NAME: case LOGOR: case LOGAND: case OR: case XOR: case AND: case MOD: case DIV: case MULT: case UMINUS: case UPLUS: case DEC: case INC: case COMPL: case NOT: case NOOP: break; } if (mp->m_badeop && (ltp->t_isenum || (mp->m_binary && rtp->t_isenum))) { chkbeop(op, ln, rn); } else if (mp->m_enumop && (ltp->t_isenum && rtp && rtp->t_isenum)) { chkeop2(op, arg, ln, rn); } else if (mp->m_enumop && (ltp->t_isenum || (rtp &&rtp->t_isenum))) { chkeop1(op, arg, ln, rn); } return (1); } static void ptrcmpok(op_t op, tnode_t *ln, tnode_t *rn) { type_t *ltp, *rtp; tspec_t lt, rt; const char *lts, *rts; lt = (ltp = ln->tn_type)->t_subt->t_tspec; rt = (rtp = rn->tn_type)->t_subt->t_tspec; if (lt == VOID || rt == VOID) { if (sflag && (lt == FUNC || rt == FUNC)) { /* (void *)0 already handled in typeok() */ *(lt == FUNC ? <s : &rts) = "function pointer"; *(lt == VOID ? <s : &rts) = "'void *'"; /* ANSI C forbids comparison of %s with %s */ warning(274, lts, rts); } return; } if (!eqtype(ltp->t_subt, rtp->t_subt, 1, 0, NULL)) { illptrc(&modtab[op], ltp, rtp); return; } if (lt == FUNC && rt == FUNC) { if (sflag && op != EQ && op != NE) /* ANSI C forbids ordered comp. of func ptr */ warning(125); } } /* * Checks type compatibility for ASSIGN, INIT, FARG and RETURN * and prints warnings/errors if necessary. * If the types are (almost) compatible, 1 is returned, otherwise 0. */ static int asgntypok(op_t op, int arg, tnode_t *ln, tnode_t *rn) { tspec_t lt, rt, lst = NOTSPEC, rst = NOTSPEC; type_t *ltp, *rtp, *lstp = NULL, *rstp = NULL; mod_t *mp; const char *lts, *rts; if ((lt = (ltp = ln->tn_type)->t_tspec) == PTR) lst = (lstp = ltp->t_subt)->t_tspec; if ((rt = (rtp = rn->tn_type)->t_tspec) == PTR) rst = (rstp = rtp->t_subt)->t_tspec; mp = &modtab[op]; if (isatyp(lt) && isatyp(rt)) return (1); if ((lt == STRUCT || lt == UNION) && (rt == STRUCT || rt == UNION)) /* both are struct or union */ return (ltp->t_str == rtp->t_str); /* 0, 0L and (void *)0 may be assigned to any pointer */ if (lt == PTR && ((rt == PTR && rst == VOID) || isityp(rt))) { if (rn->tn_op == CON && rn->tn_val->v_quad == 0) return (1); } if (lt == PTR && rt == PTR && (lst == VOID || rst == VOID)) { /* two pointers, at least one pointer to void */ if (sflag && (lst == FUNC || rst == FUNC)) { /* comb. of ptr to func and ptr to void */ *(lst == FUNC ? <s : &rts) = "function pointer"; *(lst == VOID ? <s : &rts) = "'void *'"; switch (op) { case INIT: case RETURN: /* ANSI C forbids conversion of %s to %s */ warning(303, rts, lts); break; case FARG: /* ANSI C forbids conv. of %s to %s, arg #%d */ warning(304, rts, lts, arg); break; default: /* ANSI C forbids conv. of %s to %s, op %s */ warning(305, rts, lts, mp->m_name); break; } } } if (lt == PTR && rt == PTR && (lst == VOID || rst == VOID || eqtype(lstp, rstp, 1, 0, NULL))) { /* compatible pointer types (qualifiers ignored) */ if (!tflag && ((!lstp->t_const && rstp->t_const) || (!lstp->t_volatile && rstp->t_volatile))) { /* left side has not all qualifiers of right */ switch (op) { case INIT: case RETURN: /* incompatible pointer types */ warning(182); break; case FARG: /* argument has incompat. ptr. type, arg #%d */ warning(153, arg); break; default: /* operands have incompat. ptr. types, op %s */ warning(128, mp->m_name); break; } } return (1); } if ((lt == PTR && isityp(rt)) || (isityp(lt) && rt == PTR)) { switch (op) { case INIT: case RETURN: /* illegal combination of pointer and integer */ warning(183); break; case FARG: /* illegal comb. of ptr. and int., arg #%d */ warning(154, arg); break; default: /* illegal comb. of ptr. and int., op %s */ warning(123, mp->m_name); break; } return (1); } if (lt == PTR && rt == PTR) { switch (op) { case INIT: case RETURN: illptrc(NULL, ltp, rtp); break; case FARG: /* argument has incompatible pointer type, arg #%d */ warning(153, arg); break; default: illptrc(mp, ltp, rtp); break; } return (1); } switch (op) { case INIT: /* initialisation type mismatch */ error(185); break; case RETURN: /* return value type mismatch */ error(211); break; case FARG: /* argument is incompatible with prototype, arg #%d */ warning(155, arg); break; default: incompat(op, lt, rt); break; } return (0); } /* * Prints a warning if an operator, which should be senseless for an * enum type, is applied to an enum type. */ static void chkbeop(op_t op, tnode_t *ln, tnode_t *rn) { mod_t *mp; if (!eflag) return; mp = &modtab[op]; if (!(ln->tn_type->t_isenum || (mp->m_binary && rn->tn_type->t_isenum))) { return; } /* * Enum as offset to a pointer is an exception (otherwise enums * could not be used as array indizes). */ if (op == PLUS && ((ln->tn_type->t_isenum && rn->tn_type->t_tspec == PTR) || (rn->tn_type->t_isenum && ln->tn_type->t_tspec == PTR))) { return; } /* dubious operation on enum, op %s */ warning(241, mp->m_name); } /* * Prints a warning if an operator is applied to two different enum types. */ static void chkeop2(op_t op, int arg, tnode_t *ln, tnode_t *rn) { mod_t *mp; mp = &modtab[op]; if (ln->tn_type->t_enum != rn->tn_type->t_enum) { switch (op) { case INIT: /* enum type mismatch in initialisation */ warning(210); break; case FARG: /* enum type mismatch, arg #%d */ warning(156, arg); break; case RETURN: /* return value type mismatch */ warning(211); break; default: /* enum type mismatch, op %s */ warning(130, mp->m_name); break; } #if 0 } else if (mp->m_comp && op != EQ && op != NE) { if (eflag) /* dubious comparisons of enums */ warning(243, mp->m_name); #endif } } /* * Prints a warning if an operator has both enum end other integer * types. */ static void chkeop1(op_t op, int arg, tnode_t *ln, tnode_t *rn) { char lbuf[64], rbuf[64]; if (!eflag) return; switch (op) { case INIT: /* * Initializations with 0 should be allowed. Otherwise, * we should complain about all uninitialized enums, * consequently. */ if (!rn->tn_type->t_isenum && rn->tn_op == CON && isityp(rn->tn_type->t_tspec) && rn->tn_val->v_quad == 0) { return; } /* initialisation of '%s' with '%s' */ warning(277, tyname(lbuf, sizeof(lbuf), ln->tn_type), tyname(rbuf, sizeof(rbuf), rn->tn_type)); break; case FARG: /* combination of '%s' and '%s', arg #%d */ warning(278, tyname(lbuf, sizeof(lbuf), ln->tn_type), tyname(rbuf, sizeof(rbuf), rn->tn_type), arg); break; case RETURN: /* combination of '%s' and '%s' in return */ warning(279, tyname(lbuf, sizeof(lbuf), ln->tn_type), tyname(rbuf, sizeof(rbuf), rn->tn_type)); break; default: /* combination of '%s' and %s, op %s */ warning(242, tyname(lbuf, sizeof(lbuf), ln->tn_type), tyname(rbuf, sizeof(rbuf), rn->tn_type), modtab[op].m_name); break; } } /* * Build and initialize a new node. */ static tnode_t * mktnode(op_t op, type_t *type, tnode_t *ln, tnode_t *rn) { tnode_t *ntn; tspec_t t; ntn = getnode(); ntn->tn_op = op; ntn->tn_type = type; ntn->tn_left = ln; ntn->tn_right = rn; if (op == STAR || op == FSEL) { if (ln->tn_type->t_tspec == PTR) { t = ln->tn_type->t_subt->t_tspec; if (t != FUNC && t != VOID) ntn->tn_lvalue = 1; } else { LERROR("mktnode()"); } } return (ntn); } /* * Performs usual conversion of operands to (unsigned) int. * * If tflag is set or the operand is a function argument with no * type information (no prototype or variable # of args), convert * float to double. */ tnode_t * promote(op_t op, int farg, tnode_t *tn) { tspec_t t; type_t *ntp; int len; t = tn->tn_type->t_tspec; if (!isatyp(t)) return (tn); if (!tflag) { /* * ANSI C requires that the result is always of type INT * if INT can represent all possible values of the previous * type. */ if (tn->tn_type->t_isfield) { len = tn->tn_type->t_flen; if (size(INT) > len) { t = INT; } else { if (size(INT) != len) LERROR("promote()"); if (isutyp(t)) { t = UINT; } else { t = INT; } } } else if (t == CHAR || t == UCHAR || t == SCHAR) { t = (size(CHAR) < size(INT) || t != UCHAR) ? INT : UINT; } else if (t == SHORT || t == USHORT) { t = (size(SHORT) < size(INT) || t == SHORT) ? INT : UINT; } else if (t == ENUM) { t = INT; } else if (farg && t == FLOAT) { t = DOUBLE; } } else { /* * In traditional C, keep unsigned and promote FLOAT * to DOUBLE. */ if (t == UCHAR || t == USHORT) { t = UINT; } else if (t == CHAR || t == SCHAR || t == SHORT) { t = INT; } else if (t == FLOAT) { t = DOUBLE; } else if (t == ENUM) { t = INT; } } if (t != tn->tn_type->t_tspec) { ntp = tduptyp(tn->tn_type); ntp->t_tspec = t; /* * Keep t_isenum so we are later able to check compatibility * of enum types. */ tn = convert(op, 0, ntp, tn); } return (tn); } /* * Insert conversions which are necessary to give both operands the same - * type. This is done in different ways for traditional C and ANIS C. + * type. This is done in different ways for traditional C and ANSI C. */ static void balance(op_t op, tnode_t **lnp, tnode_t **rnp) { tspec_t lt, rt, t; int i, u; type_t *ntp; static tspec_t tl[] = { LDOUBLE, DOUBLE, FLOAT, UQUAD, QUAD, ULONG, LONG, UINT, INT, }; lt = (*lnp)->tn_type->t_tspec; rt = (*rnp)->tn_type->t_tspec; if (!isatyp(lt) || !isatyp(rt)) return; if (!tflag) { if (lt == rt) { t = lt; } else if (lt == LDOUBLE || rt == LDOUBLE) { t = LDOUBLE; } else if (lt == DOUBLE || rt == DOUBLE) { t = DOUBLE; } else if (lt == FLOAT || rt == FLOAT) { t = FLOAT; } else { /* * If type A has more bits than type B it should * be able to hold all possible values of type B. */ if (size(lt) > size(rt)) { t = lt; } else if (size(lt) < size(rt)) { t = rt; } else { for (i = 3; tl[i] != INT; i++) { if (tl[i] == lt || tl[i] == rt) break; } if ((isutyp(lt) || isutyp(rt)) && !isutyp(tl[i])) { i--; } t = tl[i]; } } } else { /* Keep unsigned in traditional C */ u = isutyp(lt) || isutyp(rt); for (i = 0; tl[i] != INT; i++) { if (lt == tl[i] || rt == tl[i]) break; } t = tl[i]; if (u && isityp(t) && !isutyp(t)) t = utyp(t); } if (t != lt) { ntp = tduptyp((*lnp)->tn_type); ntp->t_tspec = t; *lnp = convert(op, 0, ntp, *lnp); } if (t != rt) { ntp = tduptyp((*rnp)->tn_type); ntp->t_tspec = t; *rnp = convert(op, 0, ntp, *rnp); } } /* * Insert a conversion operator, which converts the type of the node * to another given type. * If op is FARG, arg is the number of the argument (used for warnings). */ tnode_t * convert(op_t op, int arg, type_t *tp, tnode_t *tn) { tnode_t *ntn; tspec_t nt, ot, ost = NOTSPEC; if (tn->tn_lvalue) LERROR("convert()"); nt = tp->t_tspec; if ((ot = tn->tn_type->t_tspec) == PTR) ost = tn->tn_type->t_subt->t_tspec; if (!tflag && !sflag && op == FARG) ptconv(arg, nt, ot, tp, tn); if (isityp(nt) && isityp(ot)) { iiconv(op, arg, nt, ot, tp, tn); } else if (nt == PTR && ((ot == PTR && ost == VOID) || isityp(ot)) && tn->tn_op == CON && tn->tn_val->v_quad == 0) { /* 0, 0L and (void *)0 may be assigned to any pointer. */ } else if (isityp(nt) && ot == PTR) { piconv(op, nt, tp, tn); } else if (nt == PTR && ot == PTR) { ppconv(op, tn, tp); } ntn = getnode(); ntn->tn_op = CVT; ntn->tn_type = tp; ntn->tn_cast = op == CVT; if (tn->tn_op != CON || nt == VOID) { ntn->tn_left = tn; } else { ntn->tn_op = CON; ntn->tn_val = tgetblk(sizeof (val_t)); cvtcon(op, arg, ntn->tn_type, ntn->tn_val, tn->tn_val); } return (ntn); } /* * Print a warning if a prototype causes a type conversion that is * different from what would happen to the same argument in the * absence of a prototype. * * Errors/Warnings about illegal type combinations are already printed * in asgntypok(). */ static void ptconv(int arg, tspec_t nt, tspec_t ot, type_t *tp, tnode_t *tn) { tnode_t *ptn; char buf[64]; if (!isatyp(nt) || !isatyp(ot)) return; /* * If the type of the formal parameter is char/short, a warning * would be useless, because functions declared the old style * can't expect char/short arguments. */ if (nt == CHAR || nt == UCHAR || nt == SHORT || nt == USHORT) return; /* get default promotion */ ptn = promote(NOOP, 1, tn); ot = ptn->tn_type->t_tspec; /* return if types are the same with and without prototype */ if (nt == ot || (nt == ENUM && ot == INT)) return; if (isftyp(nt) != isftyp(ot) || psize(nt) != psize(ot)) { /* representation and/or width change */ if (!isityp(ot) || psize(ot) > psize(INT)) { /* conversion to '%s' due to prototype, arg #%d */ warning(259, tyname(buf, sizeof(buf), tp), arg); } } else if (hflag) { /* * they differ in sign or base type (char, short, int, * long, long long, float, double, long double) * * if they differ only in sign and the argument is a constant * and the msb of the argument is not set, print no warning */ if (ptn->tn_op == CON && isityp(nt) && styp(nt) == styp(ot) && msb(ptn->tn_val->v_quad, ot, -1) == 0) { /* ok */ } else { /* conversion to '%s' due to prototype, arg #%d */ warning(259, tyname(buf, sizeof(buf), tp), arg); } } } /* - * Print warnings for conversions of integer types which my cause + * Print warnings for conversions of integer types which may cause * problems. */ /* ARGSUSED */ static void iiconv(op_t op, int arg, tspec_t nt, tspec_t ot, type_t *tp, tnode_t *tn) { char lbuf[64], rbuf[64]; if (tn->tn_op == CON) return; if (op == CVT) return; #if 0 if (psize(nt) > psize(ot) && isutyp(nt) != isutyp(ot)) { /* conversion to %s may sign-extend incorrectly (, arg #%d) */ if (aflag && pflag) { if (op == FARG) { warning(297, tyname(lbuf, sizeof(lbuf), tp), arg); } else { warning(131, tyname(lbuf, sizeof(lbuf), tp)); } } } #endif if (psize(nt) < psize(ot) && (ot == LONG || ot == ULONG || ot == QUAD || ot == UQUAD || aflag > 1)) { /* conversion from '%s' may lose accuracy */ if (aflag) { if (op == FARG) { warning(298, tyname(rbuf, sizeof(rbuf), tn->tn_type), tyname(lbuf, sizeof(lbuf), tp), arg); } else { warning(132, tyname(rbuf, sizeof(rbuf), tn->tn_type), tyname(lbuf, sizeof(lbuf), tp)); } } } } /* * Print warnings for dubious conversions of pointer to integer. */ static void piconv(op_t op, tspec_t nt, type_t *tp, tnode_t *tn) { char buf[64]; if (tn->tn_op == CON) return; if (op != CVT) { - /* We got already an error. */ + /* We already got an error. */ return; } if (psize(nt) < psize(PTR)) { if (pflag && size(nt) >= size(PTR)) { /* conv. of pointer to %s may lose bits */ warning(134, tyname(buf, sizeof(buf), tp)); } else { /* conv. of pointer to %s loses bits */ warning(133, tyname(buf, sizeof(buf), tp)); } } } /* * Print warnings for questionable pointer conversions. */ static void ppconv(op_t op, tnode_t *tn, type_t *tp) { tspec_t nt, ot; const char *nts, *ots; /* - * We got already an error (pointers of different types + * We already got an error (pointers of different types * without a cast) or we will not get a warning. */ if (op != CVT) return; nt = tp->t_subt->t_tspec; ot = tn->tn_type->t_subt->t_tspec; if (nt == VOID || ot == VOID) { if (sflag && (nt == FUNC || ot == FUNC)) { /* (void *)0 already handled in convert() */ *(nt == FUNC ? &nts : &ots) = "function pointer"; *(nt == VOID ? &nts : &ots) = "'void *'"; /* ANSI C forbids conversion of %s to %s */ warning(303, ots, nts); } return; } else if (nt == FUNC && ot == FUNC) { return; } else if (nt == FUNC || ot == FUNC) { /* questionable conversion of function pointer */ warning(229); return; } if (getbound(tp->t_subt) > getbound(tn->tn_type->t_subt)) { if (hflag) /* possible pointer alignment problem */ warning(135); } if (((nt == STRUCT || nt == UNION) && tp->t_subt->t_str != tn->tn_type->t_subt->t_str) || psize(nt) != psize(ot)) { if (cflag) { /* pointer casts may be troublesome */ warning(247); } } } /* * Converts a typed constant in a constant of another type. * * op operator which requires conversion * arg if op is FARG, # of argument - * tp type in which to convert the constant + * tp type to which convert the constant * nv new constant * v old constant */ void cvtcon(op_t op, int arg, type_t *tp, val_t *nv, val_t *v) { char lbuf[64], rbuf[64]; tspec_t ot, nt; ldbl_t max = 0.0, min = 0.0; int sz, rchk; int64_t xmask, xmsk1; int osz, nsz; ot = v->v_tspec; nt = nv->v_tspec = tp->t_tspec; rchk = 0; if (ot == FLOAT || ot == DOUBLE || ot == LDOUBLE) { switch (nt) { case CHAR: max = CHAR_MAX; min = CHAR_MIN; break; case UCHAR: max = UCHAR_MAX; min = 0; break; case SCHAR: max = SCHAR_MAX; min = SCHAR_MIN; break; case SHORT: max = SHRT_MAX; min = SHRT_MIN; break; case USHORT: max = USHRT_MAX; min = 0; break; case ENUM: case INT: max = INT_MAX; min = INT_MIN; break; case UINT: max = (u_int)UINT_MAX; min = 0; break; case LONG: max = LONG_MAX; min = LONG_MIN; break; case ULONG: max = (u_long)ULONG_MAX; min = 0; break; case QUAD: max = QUAD_MAX; min = QUAD_MIN; break; case UQUAD: max = (uint64_t)UQUAD_MAX; min = 0; break; case FLOAT: max = FLT_MAX; min = -FLT_MAX; break; case DOUBLE: max = DBL_MAX; min = -DBL_MAX; break; case PTR: - /* Got already an error because of float --> ptr */ + /* Already got an error because of float --> ptr */ case LDOUBLE: max = LDBL_MAX; min = -LDBL_MAX; break; default: LERROR("cvtcon()"); } if (v->v_ldbl > max || v->v_ldbl < min) { if (nt == LDOUBLE) LERROR("cvtcon()"); if (op == FARG) { /* conv. of %s to %s is out of rng., arg #%d */ warning(295, tyname(lbuf, sizeof(lbuf), gettyp(ot)), tyname(rbuf, sizeof(rbuf), tp), arg); } else { /* conversion of %s to %s is out of range */ warning(119, tyname(lbuf, sizeof(lbuf), gettyp(ot)), tyname(rbuf, sizeof(rbuf), tp)); } v->v_ldbl = v->v_ldbl > 0 ? max : min; } if (nt == FLOAT) { nv->v_ldbl = (float)v->v_ldbl; } else if (nt == DOUBLE) { nv->v_ldbl = (double)v->v_ldbl; } else if (nt == LDOUBLE) { nv->v_ldbl = v->v_ldbl; } else { nv->v_quad = (nt == PTR || isutyp(nt)) ? (uint64_t)v->v_ldbl : (int64_t)v->v_ldbl; } } else { if (nt == FLOAT) { nv->v_ldbl = (ot == PTR || isutyp(ot)) ? (float)(uint64_t)v->v_quad : (float)v->v_quad; } else if (nt == DOUBLE) { nv->v_ldbl = (ot == PTR || isutyp(ot)) ? (double)(uint64_t)v->v_quad : (double)v->v_quad; } else if (nt == LDOUBLE) { nv->v_ldbl = (ot == PTR || isutyp(ot)) ? (ldbl_t)(uint64_t)v->v_quad : (ldbl_t)v->v_quad; } else { rchk = 1; /* Check for lost precision. */ nv->v_quad = v->v_quad; } } if (v->v_ansiu && isftyp(nt)) { /* ANSI C treats constant as unsigned */ warning(157); v->v_ansiu = 0; } else if (v->v_ansiu && (isityp(nt) && !isutyp(nt) && psize(nt) > psize(ot))) { /* ANSI C treats constant as unsigned */ warning(157); v->v_ansiu = 0; } if (nt != FLOAT && nt != DOUBLE && nt != LDOUBLE) { sz = tp->t_isfield ? tp->t_flen : size(nt); nv->v_quad = xsign(nv->v_quad, nt, sz); } if (rchk && op != CVT) { osz = size(ot); nsz = tp->t_isfield ? tp->t_flen : size(nt); xmask = qlmasks[nsz] ^ qlmasks[osz]; xmsk1 = qlmasks[nsz] ^ qlmasks[osz - 1]; /* * For bitwise operations we are not interested in the * value, but in the bits itself. */ if (op == ORASS || op == OR || op == XOR) { /* * Print a warning if bits which were set are * lost due to the conversion. * This can happen with operator ORASS only. */ if (nsz < osz && (v->v_quad & xmask) != 0) { /* constant truncated by conv., op %s */ warning(306, modtab[op].m_name); } } else if (op == ANDASS || op == AND) { /* * Print a warning if additional bits are not all 1 * and the most significant bit of the old value is 1, * or if at least one (but not all) removed bit was 0. */ if (nsz > osz && (nv->v_quad & qbmasks[osz - 1]) != 0 && (nv->v_quad & xmask) != xmask) { /* * extra bits set to 0 in conversion * of '%s' to '%s', op %s */ warning(309, tyname(lbuf, sizeof(lbuf), gettyp(ot)), tyname(rbuf, sizeof(rbuf), tp), modtab[op].m_name); } else if (nsz < osz && (v->v_quad & xmask) != xmask && (v->v_quad & xmask) != 0) { /* const. truncated by conv., op %s */ warning(306, modtab[op].m_name); } } else if ((nt != PTR && isutyp(nt)) && (ot != PTR && !isutyp(ot)) && v->v_quad < 0) { if (op == ASSIGN) { /* assignment of negative constant to ... */ warning(164); } else if (op == INIT) { /* initialisation of unsigned with neg. ... */ warning(221); } else if (op == FARG) { /* conversion of neg. const. to ..., arg #%d */ warning(296, arg); } else if (modtab[op].m_comp) { /* we get this warning already in chkcomp() */ } else { /* conversion of negative constant to ... */ warning(222); } } else if (nv->v_quad != v->v_quad && nsz <= osz && (v->v_quad & xmask) != 0 && (isutyp(ot) || (v->v_quad & xmsk1) != xmsk1)) { /* * Loss of significant bit(s). All truncated bits * of unsigned types or all truncated bits plus the * msb of the target for signed types are considered * to be significant bits. Loss of significant bits * means that at least on of the bits was set in an * unsigned type or that at least one, but not all of * the bits was set in a signed type. * Loss of significant bits means that it is not * possible, also not with necessary casts, to convert * back to the original type. An example for a * necessary cast is: * char c; int i; c = 128; * i = c; ** yields -128 ** * i = (unsigned char)c; ** yields 128 ** */ if (op == ASSIGN && tp->t_isfield) { /* precision lost in bit-field assignment */ warning(166); } else if (op == ASSIGN) { /* constant truncated by assignment */ warning(165); } else if (op == INIT && tp->t_isfield) { /* bit-field initializer does not fit */ warning(180); } else if (op == INIT) { /* initializer does not fit */ warning(178); } else if (op == CASE) { /* case label affected by conversion */ warning(196); } else if (op == FARG) { /* conv. of %s to %s is out of rng., arg #%d */ warning(295, tyname(lbuf, sizeof(lbuf), gettyp(ot)), tyname(rbuf, sizeof(rbuf), tp), arg); } else { /* conversion of %s to %s is out of range */ warning(119, tyname(lbuf, sizeof(lbuf), gettyp(ot)), tyname(rbuf, sizeof(rbuf), tp)); } } else if (nv->v_quad != v->v_quad) { if (op == ASSIGN && tp->t_isfield) { /* precision lost in bit-field assignment */ warning(166); } else if (op == INIT && tp->t_isfield) { /* bit-field initializer out of range */ warning(11); } else if (op == CASE) { /* case label affected by conversion */ warning(196); } else if (op == FARG) { /* conv. of %s to %s is out of rng., arg #%d */ warning(295, tyname(lbuf, sizeof(lbuf), gettyp(ot)), tyname(rbuf, sizeof(rbuf), tp), arg); } else { /* conversion of %s to %s is out of range */ warning(119, tyname(lbuf, sizeof(lbuf), gettyp(ot)), tyname(rbuf, sizeof(rbuf), tp)); } } } } /* * Called if incompatible types were detected. * Prints an appropriate warning. */ static void incompat(op_t op, tspec_t lt, tspec_t rt) { mod_t *mp; mp = &modtab[op]; if (lt == VOID || (mp->m_binary && rt == VOID)) { /* void type illegal in expression */ error(109); } else if (op == ASSIGN) { if ((lt == STRUCT || lt == UNION) && (rt == STRUCT || rt == UNION)) { /* assignment of different structures */ error(240); } else { /* assignment type mismatch */ error(171); } } else if (mp->m_binary) { /* operands of %s have incompatible types */ error(107, mp->m_name); } else { /* operand of %s has incompatible type */ error(108, mp->m_name); } } /* * Called if incompatible pointer types are detected. * Print an appropriate warning. */ static void illptrc(mod_t *mp, type_t *ltp, type_t *rtp) { tspec_t lt, rt; if (ltp->t_tspec != PTR || rtp->t_tspec != PTR) LERROR("illptrc()"); lt = ltp->t_subt->t_tspec; rt = rtp->t_subt->t_tspec; if ((lt == STRUCT || lt == UNION) && (rt == STRUCT || rt == UNION)) { if (mp == NULL) { /* illegal structure pointer combination */ warning(244); } else { /* illegal structure pointer combination, op %s */ warning(245, mp->m_name); } } else { if (mp == NULL) { /* illegal pointer combination */ warning(184); } else { /* illegal pointer combination, op %s */ warning(124, mp->m_name); } } } /* * Make sure type (*tpp)->t_subt has at least the qualifiers * of tp1->t_subt and tp2->t_subt. */ static void mrgqual(type_t **tpp, type_t *tp1, type_t *tp2) { if ((*tpp)->t_tspec != PTR || tp1->t_tspec != PTR || tp2->t_tspec != PTR) { LERROR("mrgqual()"); } if ((*tpp)->t_subt->t_const == (tp1->t_subt->t_const | tp2->t_subt->t_const) && (*tpp)->t_subt->t_volatile == (tp1->t_subt->t_volatile | tp2->t_subt->t_volatile)) { return; } *tpp = tduptyp(*tpp); (*tpp)->t_subt = tduptyp((*tpp)->t_subt); (*tpp)->t_subt->t_const = tp1->t_subt->t_const | tp2->t_subt->t_const; (*tpp)->t_subt->t_volatile = tp1->t_subt->t_volatile | tp2->t_subt->t_volatile; } /* * Returns 1 if the given structure or union has a constant member * (maybe recursively). */ static int conmemb(type_t *tp) { sym_t *m; tspec_t t; if ((t = tp->t_tspec) != STRUCT && t != UNION) LERROR("conmemb()"); for (m = tp->t_str->memb; m != NULL; m = m->s_nxt) { tp = m->s_type; if (tp->t_const) return (1); if ((t = tp->t_tspec) == STRUCT || t == UNION) { if (conmemb(m->s_type)) return (1); } } return (0); } const char * basictyname(tspec_t t) { switch (t) { case CHAR: return "char"; case UCHAR: return "unsigned char"; case SCHAR: return "signed char"; case SHORT: return "short"; case USHORT: return "unsigned short"; case INT: return "int"; case UINT: return "unsigned int"; case LONG: return "long"; case ULONG: return "unsigned long"; case QUAD: return "long long"; case UQUAD: return "unsigned long long"; case FLOAT: return "float"; case DOUBLE: return "double"; case LDOUBLE: return "long double"; case PTR: return "pointer"; case ENUM: return "enum"; case STRUCT: return "struct"; case UNION: return "union"; case FUNC: return "function"; case ARRAY: return "array"; default: LERROR("basictyname()"); return NULL; } } const char * tyname(char *buf, size_t bufsiz, type_t *tp) { tspec_t t; const char *s; char lbuf[64]; if ((t = tp->t_tspec) == INT && tp->t_isenum) t = ENUM; s = basictyname(t); switch (t) { case CHAR: case UCHAR: case SCHAR: case SHORT: case USHORT: case INT: case UINT: case LONG: case ULONG: case QUAD: case UQUAD: case FLOAT: case DOUBLE: case LDOUBLE: case FUNC: (void)snprintf(buf, bufsiz, "%s", s); break; case PTR: (void)snprintf(buf, bufsiz, "%s to %s", s, tyname(lbuf, sizeof(lbuf), tp->t_subt)); break; case ENUM: (void)snprintf(buf, bufsiz, "%s %s", s, tp->t_enum->etag->s_name); break; case STRUCT: case UNION: (void)snprintf(buf, bufsiz, "%s %s", s, tp->t_str->stag->s_name); break; case ARRAY: (void)snprintf(buf, bufsiz, "%s of %s[%d]", s, tyname(lbuf, sizeof(lbuf), tp->t_subt), tp->t_dim); break; default: LERROR("tyname()"); } return (buf); } /* * Create a new node for one of the operators POINT and ARROW. */ static tnode_t * bldstr(op_t op, tnode_t *ln, tnode_t *rn) { tnode_t *ntn, *ctn; int nolval; if (rn->tn_op != NAME) LERROR("bldstr()"); if (rn->tn_sym->s_value.v_tspec != INT) LERROR("bldstr()"); if (rn->tn_sym->s_scl != MOS && rn->tn_sym->s_scl != MOU) LERROR("bldstr()"); /* * Remember if the left operand is an lvalue (structure members * are lvalues if and only if the structure itself is an lvalue). */ nolval = op == POINT && !ln->tn_lvalue; if (op == POINT) { ln = bldamper(ln, 1); } else if (ln->tn_type->t_tspec != PTR) { if (!tflag || !isityp(ln->tn_type->t_tspec)) LERROR("bldstr()"); ln = convert(NOOP, 0, tincref(gettyp(VOID), PTR), ln); } #if PTRDIFF_IS_LONG ctn = getinode(LONG, rn->tn_sym->s_value.v_quad / CHAR_BIT); #else ctn = getinode(INT, rn->tn_sym->s_value.v_quad / CHAR_BIT); #endif ntn = mktnode(PLUS, tincref(rn->tn_type, PTR), ln, ctn); if (ln->tn_op == CON) ntn = fold(ntn); if (rn->tn_type->t_isfield) { ntn = mktnode(FSEL, ntn->tn_type->t_subt, ntn, NULL); } else { ntn = mktnode(STAR, ntn->tn_type->t_subt, ntn, NULL); } if (nolval) ntn->tn_lvalue = 0; return (ntn); } /* * Create a node for INCAFT, INCBEF, DECAFT and DECBEF. */ static tnode_t * bldincdec(op_t op, tnode_t *ln) { tnode_t *cn, *ntn; if (ln == NULL) LERROR("bldincdec()"); if (ln->tn_type->t_tspec == PTR) { cn = plength(ln->tn_type); } else { cn = getinode(INT, (int64_t)1); } ntn = mktnode(op, ln->tn_type, ln, cn); return (ntn); } /* * Create a tree node for the & operator */ static tnode_t * bldamper(tnode_t *tn, int noign) { tnode_t *ntn; tspec_t t; if (!noign && ((t = tn->tn_type->t_tspec) == ARRAY || t == FUNC)) { /* & before array or function: ignored */ if (tflag) warning(127); return (tn); } /* eliminate &* */ if (tn->tn_op == STAR && tn->tn_left->tn_type->t_tspec == PTR && tn->tn_left->tn_type->t_subt == tn->tn_type) { return (tn->tn_left); } ntn = mktnode(AMPER, tincref(tn->tn_type, PTR), tn, NULL); return (ntn); } /* * Create a node for operators PLUS and MINUS. */ static tnode_t * bldplmi(op_t op, tnode_t *ln, tnode_t *rn) { tnode_t *ntn, *ctn; type_t *tp; /* If pointer and integer, then pointer to the lhs. */ if (rn->tn_type->t_tspec == PTR && isityp(ln->tn_type->t_tspec)) { ntn = ln; ln = rn; rn = ntn; } if (ln->tn_type->t_tspec == PTR && rn->tn_type->t_tspec != PTR) { if (!isityp(rn->tn_type->t_tspec)) LERROR("bldplmi()"); ctn = plength(ln->tn_type); if (rn->tn_type->t_tspec != ctn->tn_type->t_tspec) rn = convert(NOOP, 0, ctn->tn_type, rn); rn = mktnode(MULT, rn->tn_type, rn, ctn); if (rn->tn_left->tn_op == CON) rn = fold(rn); ntn = mktnode(op, ln->tn_type, ln, rn); } else if (rn->tn_type->t_tspec == PTR) { if (ln->tn_type->t_tspec != PTR || op != MINUS) LERROR("bldplmi()"); #if PTRDIFF_IS_LONG tp = gettyp(LONG); #else tp = gettyp(INT); #endif ntn = mktnode(op, tp, ln, rn); if (ln->tn_op == CON && rn->tn_op == CON) ntn = fold(ntn); ctn = plength(ln->tn_type); balance(NOOP, &ntn, &ctn); ntn = mktnode(DIV, tp, ntn, ctn); } else { ntn = mktnode(op, ln->tn_type, ln, rn); } return (ntn); } /* * Create a node for operators SHL and SHR. */ static tnode_t * bldshft(op_t op, tnode_t *ln, tnode_t *rn) { tspec_t t; tnode_t *ntn; if ((t = rn->tn_type->t_tspec) != INT && t != UINT) rn = convert(CVT, 0, gettyp(INT), rn); ntn = mktnode(op, ln->tn_type, ln, rn); return (ntn); } /* * Create a node for COLON. */ static tnode_t * bldcol(tnode_t *ln, tnode_t *rn) { tspec_t lt, rt, pdt; type_t *rtp; tnode_t *ntn; lt = ln->tn_type->t_tspec; rt = rn->tn_type->t_tspec; #if PTRDIFF_IS_LONG pdt = LONG; #else pdt = INT; #endif /* * Arithmetic types are balanced, all other type combinations * still need to be handled. */ if (isatyp(lt) && isatyp(rt)) { rtp = ln->tn_type; } else if (lt == VOID || rt == VOID) { rtp = gettyp(VOID); } else if (lt == STRUCT || lt == UNION) { /* Both types must be identical. */ if (rt != STRUCT && rt != UNION) LERROR("bldcol()"); if (ln->tn_type->t_str != rn->tn_type->t_str) LERROR("bldcol()"); if (incompl(ln->tn_type)) { /* unknown operand size, op %s */ error(138, modtab[COLON].m_name); return (NULL); } rtp = ln->tn_type; } else if (lt == PTR && isityp(rt)) { if (rt != pdt) { rn = convert(NOOP, 0, gettyp(pdt), rn); rt = pdt; } rtp = ln->tn_type; } else if (rt == PTR && isityp(lt)) { if (lt != pdt) { ln = convert(NOOP, 0, gettyp(pdt), ln); lt = pdt; } rtp = rn->tn_type; } else if (lt == PTR && ln->tn_type->t_subt->t_tspec == VOID) { if (rt != PTR) LERROR("bldcol()"); rtp = ln->tn_type; mrgqual(&rtp, ln->tn_type, rn->tn_type); } else if (rt == PTR && rn->tn_type->t_subt->t_tspec == VOID) { if (lt != PTR) LERROR("bldcol()"); rtp = rn->tn_type; mrgqual(&rtp, ln->tn_type, rn->tn_type); } else { if (lt != PTR || rt != PTR) LERROR("bldcol()"); /* * XXX For now we simply take the left type. This is - * probably wrong, if one type contains a functionprototype + * probably wrong, if one type contains a function prototype * and the other one, at the same place, only an old style * declaration. */ rtp = ln->tn_type; mrgqual(&rtp, ln->tn_type, rn->tn_type); } ntn = mktnode(COLON, rtp, ln, rn); return (ntn); } /* * Create a node for an assignment operator (both = and op= ). */ static tnode_t * bldasgn(op_t op, tnode_t *ln, tnode_t *rn) { tspec_t lt, rt; tnode_t *ntn, *ctn; if (ln == NULL || rn == NULL) LERROR("bldasgn()"); lt = ln->tn_type->t_tspec; rt = rn->tn_type->t_tspec; if ((op == ADDASS || op == SUBASS) && lt == PTR) { if (!isityp(rt)) LERROR("bldasgn()"); ctn = plength(ln->tn_type); if (rn->tn_type->t_tspec != ctn->tn_type->t_tspec) rn = convert(NOOP, 0, ctn->tn_type, rn); rn = mktnode(MULT, rn->tn_type, rn, ctn); if (rn->tn_left->tn_op == CON) rn = fold(rn); } if ((op == ASSIGN || op == RETURN) && (lt == STRUCT || rt == STRUCT)) { if (rt != lt || ln->tn_type->t_str != rn->tn_type->t_str) LERROR("bldasgn()"); if (incompl(ln->tn_type)) { if (op == RETURN) { /* cannot return incomplete type */ error(212); } else { /* unknown operand size, op %s */ error(138, modtab[op].m_name); } return (NULL); } } if (op == SHLASS) { if (psize(lt) < psize(rt)) { if (hflag) /* semantics of %s change in ANSI C; use ... */ warning(118, "<<="); } } else if (op != SHRASS) { if (op == ASSIGN || lt != PTR) { if (lt != rt || (ln->tn_type->t_isfield && rn->tn_op == CON)) { rn = convert(op, 0, ln->tn_type, rn); rt = lt; } } } ntn = mktnode(op, ln->tn_type, ln, rn); return (ntn); } /* * Get length of type tp->t_subt. */ static tnode_t * plength(type_t *tp) { int elem, elsz; tspec_t st; if (tp->t_tspec != PTR) LERROR("plength()"); tp = tp->t_subt; elem = 1; elsz = 0; while (tp->t_tspec == ARRAY) { elem *= tp->t_dim; tp = tp->t_subt; } switch (tp->t_tspec) { case FUNC: /* pointer to function is not allowed here */ error(110); break; case VOID: /* cannot do pointer arithmetic on operand of ... */ (void)gnuism(136); break; case STRUCT: case UNION: if ((elsz = tp->t_str->size) == 0) /* cannot do pointer arithmetic on operand of ... */ error(136); break; case ENUM: if (incompl(tp)) { /* cannot do pointer arithmetic on operand of ... */ warning(136); } /* FALLTHROUGH */ default: if ((elsz = size(tp->t_tspec)) == 0) { /* cannot do pointer arithmetic on operand of ... */ error(136); } else if (elsz == -1) { LERROR("plength()"); } break; } if (elem == 0 && elsz != 0) { /* cannot do pointer arithmetic on operand of ... */ error(136); } if (elsz == 0) elsz = CHAR_BIT; #if PTRDIFF_IS_LONG st = LONG; #else st = INT; #endif return (getinode(st, (int64_t)(elem * elsz / CHAR_BIT))); } /* * XXX * Note: There appear to be a number of bugs in detecting overflow in * this function. An audit and a set of proper regression tests are needed. * --Perry Metzger, Nov. 16, 2001 */ /* * Do only as much as necessary to compute constant expressions. * Called only if the operator allows folding and (both) operands * are constants. */ static tnode_t * fold(tnode_t *tn) { val_t *v; tspec_t t; int utyp, ovfl; int64_t sl, sr = 0, q = 0, mask; uint64_t ul, ur = 0; tnode_t *cn; if ((v = calloc(1, sizeof (val_t))) == NULL) nomem(); v->v_tspec = t = tn->tn_type->t_tspec; utyp = t == PTR || isutyp(t); ul = sl = tn->tn_left->tn_val->v_quad; if (modtab[tn->tn_op].m_binary) ur = sr = tn->tn_right->tn_val->v_quad; mask = qlmasks[size(t)]; ovfl = 0; switch (tn->tn_op) { case UPLUS: q = sl; break; case UMINUS: q = -sl; if (msb(q, t, -1) == msb(sl, t, -1)) ovfl = 1; break; case COMPL: q = ~sl; break; case MULT: if (utyp) { q = ul * ur; if (q != (q & mask)) ovfl = 1; else if ((ul != 0) && ((q / ul) != ur)) ovfl = 1; } else { q = sl * sr; if (msb(q, t, -1) != (msb(sl, t, -1) ^ msb(sr, t, -1))) ovfl = 1; } break; case DIV: if (sr == 0) { /* division by 0 */ error(139); q = utyp ? UQUAD_MAX : QUAD_MAX; } else { q = utyp ? ul / ur : sl / sr; } break; case MOD: if (sr == 0) { /* modulus by 0 */ error(140); q = 0; } else { q = utyp ? ul % ur : sl % sr; } break; case PLUS: q = utyp ? ul + ur : sl + sr; if (msb(sl, t, -1) != 0 && msb(sr, t, -1) != 0) { if (msb(q, t, -1) == 0) ovfl = 1; } else if (msb(sl, t, -1) == 0 && msb(sr, t, -1) == 0) { if (msb(q, t, -1) != 0) ovfl = 1; } break; case MINUS: q = utyp ? ul - ur : sl - sr; if (msb(sl, t, -1) != 0 && msb(sr, t, -1) == 0) { if (msb(q, t, -1) == 0) ovfl = 1; } else if (msb(sl, t, -1) == 0 && msb(sr, t, -1) != 0) { if (msb(q, t, -1) != 0) ovfl = 1; } break; case SHL: q = utyp ? ul << sr : sl << sr; break; case SHR: /* * The sign must be explicitly extended because * shifts of signed values are implementation dependent. */ q = ul >> sr; q = xsign(q, t, size(t) - (int)sr); break; case LT: q = utyp ? ul < ur : sl < sr; break; case LE: q = utyp ? ul <= ur : sl <= sr; break; case GE: q = utyp ? ul >= ur : sl >= sr; break; case GT: q = utyp ? ul > ur : sl > sr; break; case EQ: q = utyp ? ul == ur : sl == sr; break; case NE: q = utyp ? ul != ur : sl != sr; break; case AND: q = utyp ? ul & ur : sl & sr; break; case XOR: q = utyp ? ul ^ ur : sl ^ sr; break; case OR: q = utyp ? ul | ur : sl | sr; break; default: LERROR("fold()"); } /* XXX does not work for quads. */ if (ovfl || ((q | mask) != ~(uint64_t)0 && (q & ~mask) != 0)) { if (hflag) /* integer overflow detected, op %s */ warning(141, modtab[tn->tn_op].m_name); } v->v_quad = xsign(q, t, -1); cn = getcnode(tn->tn_type, v); return (cn); } /* * Same for operators whose operands are compared with 0 (test context). */ static tnode_t * foldtst(tnode_t *tn) { int l, r = 0; val_t *v; if ((v = calloc(1, sizeof (val_t))) == NULL) nomem(); v->v_tspec = tn->tn_type->t_tspec; if (tn->tn_type->t_tspec != INT) LERROR("foldtst()"); if (isftyp(tn->tn_left->tn_type->t_tspec)) { l = tn->tn_left->tn_val->v_ldbl != 0.0; } else { l = tn->tn_left->tn_val->v_quad != 0; } if (modtab[tn->tn_op].m_binary) { if (isftyp(tn->tn_right->tn_type->t_tspec)) { r = tn->tn_right->tn_val->v_ldbl != 0.0; } else { r = tn->tn_right->tn_val->v_quad != 0; } } switch (tn->tn_op) { case NOT: if (hflag) /* constant argument to NOT */ warning(239); v->v_quad = !l; break; case LOGAND: v->v_quad = l && r; break; case LOGOR: v->v_quad = l || r; break; default: LERROR("foldtst()"); } return (getcnode(tn->tn_type, v)); } /* * Same for operands with floating point type. */ static tnode_t * foldflt(tnode_t *tn) { val_t *v; tspec_t t; ldbl_t l, r = 0; if ((v = calloc(1, sizeof (val_t))) == NULL) nomem(); v->v_tspec = t = tn->tn_type->t_tspec; if (!isftyp(t)) LERROR("foldflt()"); if (t != tn->tn_left->tn_type->t_tspec) LERROR("foldflt()"); if (modtab[tn->tn_op].m_binary && t != tn->tn_right->tn_type->t_tspec) LERROR("foldflt()"); l = tn->tn_left->tn_val->v_ldbl; if (modtab[tn->tn_op].m_binary) r = tn->tn_right->tn_val->v_ldbl; switch (tn->tn_op) { case UPLUS: v->v_ldbl = l; break; case UMINUS: v->v_ldbl = -l; break; case MULT: v->v_ldbl = l * r; break; case DIV: if (r == 0.0) { /* division by 0 */ error(139); if (t == FLOAT) { v->v_ldbl = l < 0 ? -FLT_MAX : FLT_MAX; } else if (t == DOUBLE) { v->v_ldbl = l < 0 ? -DBL_MAX : DBL_MAX; } else { v->v_ldbl = l < 0 ? -LDBL_MAX : LDBL_MAX; } } else { v->v_ldbl = l / r; } break; case PLUS: v->v_ldbl = l + r; break; case MINUS: v->v_ldbl = l - r; break; case LT: v->v_quad = l < r; break; case LE: v->v_quad = l <= r; break; case GE: v->v_quad = l >= r; break; case GT: v->v_quad = l > r; break; case EQ: v->v_quad = l == r; break; case NE: v->v_quad = l != r; break; default: LERROR("foldflt()"); } if (isnan((double)v->v_ldbl)) LERROR("foldflt()"); if (!finite((double)v->v_ldbl) || (t == FLOAT && (v->v_ldbl > FLT_MAX || v->v_ldbl < -FLT_MAX)) || (t == DOUBLE && (v->v_ldbl > DBL_MAX || v->v_ldbl < -DBL_MAX))) { /* floating point overflow detected, op %s */ warning(142, modtab[tn->tn_op].m_name); if (t == FLOAT) { v->v_ldbl = v->v_ldbl < 0 ? -FLT_MAX : FLT_MAX; } else if (t == DOUBLE) { v->v_ldbl = v->v_ldbl < 0 ? -DBL_MAX : DBL_MAX; } else { v->v_ldbl = v->v_ldbl < 0 ? -LDBL_MAX: LDBL_MAX; } } return (getcnode(tn->tn_type, v)); } /* * Create a constant node for sizeof. */ tnode_t * bldszof(type_t *tp) { int elem, elsz; tspec_t st; elem = 1; while (tp->t_tspec == ARRAY) { elem *= tp->t_dim; tp = tp->t_subt; } if (elem == 0) { /* cannot take size of incomplete type */ error(143); elem = 1; } switch (tp->t_tspec) { case FUNC: /* cannot take size of function */ error(144); elsz = 1; break; case STRUCT: case UNION: if (incompl(tp)) { /* cannot take size of incomplete type */ error(143); elsz = 1; } else { elsz = tp->t_str->size; } break; case ENUM: if (incompl(tp)) { /* cannot take size of incomplete type */ warning(143); } /* FALLTHROUGH */ default: if (tp->t_isfield) { /* cannot take size of bit-field */ error(145); } if (tp->t_tspec == VOID) { /* cannot take size of void */ error(146); elsz = 1; } else { elsz = size(tp->t_tspec); if (elsz <= 0) LERROR("bldszof()"); } break; } #if SIZEOF_IS_ULONG st = ULONG; #else st = UINT; #endif return (getinode(st, (int64_t)(elem * elsz / CHAR_BIT))); } /* * Type casts. */ tnode_t * cast(tnode_t *tn, type_t *tp) { tspec_t nt, ot; if (tn == NULL) return (NULL); tn = cconv(tn); nt = tp->t_tspec; ot = tn->tn_type->t_tspec; if (nt == VOID) { /* * XXX ANSI C requires scalar types or void (Plauger&Brodie). * But this seams really questionable. */ } else if (nt == STRUCT || nt == UNION || nt == ARRAY || nt == FUNC) { /* invalid cast expression */ error(147); return (NULL); } else if (ot == STRUCT || ot == UNION) { /* invalid cast expression */ error(147); return (NULL); } else if (ot == VOID) { /* improper cast of void expression */ error(148); return (NULL); } else if (isityp(nt) && issclt(ot)) { /* ok */ } else if (isftyp(nt) && isatyp(ot)) { /* ok */ } else if (nt == PTR && isityp(ot)) { /* ok */ } else if (nt == PTR && ot == PTR) { if (!tp->t_subt->t_const && tn->tn_type->t_subt->t_const) { if (hflag) /* cast discards 'const' from ... */ warning(275); } } else { /* invalid cast expression */ error(147); return (NULL); } tn = convert(CVT, 0, tp, tn); tn->tn_cast = 1; return (tn); } /* * Create the node for a function argument. * All necessary conversions and type checks are done in funccall(), because * in funcarg() we have no information about expected argument types. */ tnode_t * funcarg(tnode_t *args, tnode_t *arg) { tnode_t *ntn; /* * If there was a serious error in the expression for the argument, * create a dummy argument so the positions of the remaining arguments * will not change. */ if (arg == NULL) arg = getinode(INT, (int64_t)0); ntn = mktnode(PUSH, arg->tn_type, arg, args); return (ntn); } /* * Create the node for a function call. Also check types of * function arguments and insert conversions, if necessary. */ tnode_t * funccall(tnode_t *func, tnode_t *args) { tnode_t *ntn; op_t fcop; if (func == NULL) return (NULL); if (func->tn_op == NAME && func->tn_type->t_tspec == FUNC) { fcop = CALL; } else { fcop = ICALL; } /* * after cconv() func will always be a pointer to a function * if it is a valid function designator. */ func = cconv(func); if (func->tn_type->t_tspec != PTR || func->tn_type->t_subt->t_tspec != FUNC) { /* illegal function */ error(149); return (NULL); } args = chkfarg(func->tn_type->t_subt, args); ntn = mktnode(fcop, func->tn_type->t_subt->t_subt, func, args); return (ntn); } /* * Check types of all function arguments and insert conversions, * if necessary. */ static tnode_t * chkfarg(type_t *ftp, tnode_t *args) { tnode_t *arg; sym_t *asym; tspec_t at; int narg, npar, n, i; /* get # of args in the prototype */ npar = 0; for (asym = ftp->t_args; asym != NULL; asym = asym->s_nxt) npar++; /* get # of args in function call */ narg = 0; for (arg = args; arg != NULL; arg = arg->tn_right) narg++; asym = ftp->t_args; if (ftp->t_proto && npar != narg && !(ftp->t_vararg && npar < narg)) { /* argument mismatch: %d arg%s passed, %d expected */ error(150, narg, narg > 1 ? "s" : "", npar); asym = NULL; } for (n = 1; n <= narg; n++) { /* * The rightmost argument is at the top of the argument * subtree. */ for (i = narg, arg = args; i > n; i--, arg = arg->tn_right) continue; /* some things which are always not allowd */ if ((at = arg->tn_left->tn_type->t_tspec) == VOID) { /* void expressions may not be arguments, arg #%d */ error(151, n); return (NULL); } else if ((at == STRUCT || at == UNION) && incompl(arg->tn_left->tn_type)) { /* argument cannot have unknown size, arg #%d */ error(152, n); return (NULL); } else if (isityp(at) && arg->tn_left->tn_type->t_isenum && incompl(arg->tn_left->tn_type)) { /* argument cannot have unknown size, arg #%d */ warning(152, n); } /* class conversions (arg in value context) */ arg->tn_left = cconv(arg->tn_left); if (asym != NULL) { arg->tn_left = parg(n, asym->s_type, arg->tn_left); } else { arg->tn_left = promote(NOOP, 1, arg->tn_left); } arg->tn_type = arg->tn_left->tn_type; if (asym != NULL) asym = asym->s_nxt; } return (args); } /* * Compare the type of an argument with the corresponding type of a * prototype parameter. If it is a valid combination, but both types * are not the same, insert a conversion to convert the argument into * the type of the parameter. */ static tnode_t * parg( int n, /* pos of arg */ type_t *tp, /* expected type (from prototype) */ tnode_t *tn) /* argument */ { tnode_t *ln; int warn; if ((ln = calloc(1, sizeof (tnode_t))) == NULL) nomem(); ln->tn_type = tduptyp(tp); ln->tn_type->t_const = 0; ln->tn_lvalue = 1; if (typeok(FARG, n, ln, tn)) { if (!eqtype(tp, tn->tn_type, 1, 0, (warn = 0, &warn)) || warn) tn = convert(FARG, n, tp, tn); } free(ln); return (tn); } /* * Return the value of an integral constant expression. * If the expression is not constant or its type is not an integer * type, an error message is printed. */ val_t * constant(tnode_t *tn, int required) { val_t *v; if (tn != NULL) tn = cconv(tn); if (tn != NULL) tn = promote(NOOP, 0, tn); if ((v = calloc(1, sizeof (val_t))) == NULL) nomem(); if (tn == NULL) { if (nerr == 0) LERROR("constant()"); v->v_tspec = INT; v->v_quad = 1; return (v); } v->v_tspec = tn->tn_type->t_tspec; if (tn->tn_op == CON) { if (tn->tn_type->t_tspec != tn->tn_val->v_tspec) LERROR("constant()"); if (isityp(tn->tn_val->v_tspec)) { v->v_ansiu = tn->tn_val->v_ansiu; v->v_quad = tn->tn_val->v_quad; return (v); } v->v_quad = tn->tn_val->v_ldbl; } else { v->v_quad = 1; } /* integral constant expression expected */ if (required) error(55); else c99ism(318); if (!isityp(v->v_tspec)) v->v_tspec = INT; return (v); } /* * Perform some tests on expressions which can't be done in build() and * functions called by build(). These tests must be done here because * we need some information about the context in which the operations * are performed. * After all tests are performed, expr() frees the memory which is used * for the expression. */ void expr(tnode_t *tn, int vctx, int tctx, int freeblk) { if (tn == NULL && nerr == 0) LERROR("expr()"); if (tn == NULL) { tfreeblk(); return; } /* expr() is also called in global initialisations */ if (dcs->d_ctx != EXTERN) chkreach(); chkmisc(tn, vctx, tctx, !tctx, 0, 0, 0); if (tn->tn_op == ASSIGN) { if (hflag && tctx) /* assignment in conditional context */ warning(159); } else if (tn->tn_op == CON) { if (hflag && tctx && !ccflg) /* constant in conditional context */ warning(161); } if (!modtab[tn->tn_op].m_sideeff) { /* * for left operands of COMMA this warning is already * printed */ if (tn->tn_op != COMMA && !vctx && !tctx) nulleff(tn); } if (dflag) displexpr(tn, 0); /* free the tree memory */ if (freeblk) tfreeblk(); } static void nulleff(tnode_t *tn) { if (!hflag) return; while (!modtab[tn->tn_op].m_sideeff) { if (tn->tn_op == CVT && tn->tn_type->t_tspec == VOID) { tn = tn->tn_left; } else if (tn->tn_op == LOGAND || tn->tn_op == LOGOR) { /* * && and || have a side effect if the right operand * has a side effect. */ tn = tn->tn_right; } else if (tn->tn_op == QUEST) { /* * ? has a side effect if at least one of its right * operands has a side effect */ tn = tn->tn_right; } else if (tn->tn_op == COLON || tn->tn_op == COMMA) { /* * : has a side effect if at least one of its operands * has a side effect */ if (modtab[tn->tn_left->tn_op].m_sideeff) { tn = tn->tn_left; } else if (modtab[tn->tn_right->tn_op].m_sideeff) { tn = tn->tn_right; } else { break; } } else { break; } } if (!modtab[tn->tn_op].m_sideeff) /* expression has null effect */ warning(129); } /* * Dump an expression to stdout * only used for debugging */ static void displexpr(tnode_t *tn, int offs) { uint64_t uq; if (tn == NULL) { (void)printf("%*s%s\n", offs, "", "NULL"); return; } (void)printf("%*sop %s ", offs, "", modtab[tn->tn_op].m_name); if (tn->tn_op == NAME) { (void)printf("%s: %s ", tn->tn_sym->s_name, scltoa(tn->tn_sym->s_scl)); } else if (tn->tn_op == CON && isftyp(tn->tn_type->t_tspec)) { (void)printf("%#g ", (double)tn->tn_val->v_ldbl); } else if (tn->tn_op == CON && isityp(tn->tn_type->t_tspec)) { uq = tn->tn_val->v_quad; (void)printf("0x %08lx %08lx ", (long)(uq >> 32) & 0xffffffffl, (long)uq & 0xffffffffl); } else if (tn->tn_op == CON) { if (tn->tn_type->t_tspec != PTR) LERROR("displexpr()"); (void)printf("0x%0*lx ", (int)(sizeof (void *) * CHAR_BIT / 4), (u_long)tn->tn_val->v_quad); } else if (tn->tn_op == STRING) { if (tn->tn_strg->st_tspec == CHAR) { (void)printf("\"%s\"", tn->tn_strg->st_cp); } else { char *s; size_t n; n = MB_CUR_MAX * (tn->tn_strg->st_len + 1); if ((s = malloc(n)) == NULL) nomem(); (void)wcstombs(s, tn->tn_strg->st_wcp, n); (void)printf("L\"%s\"", s); free(s); } (void)printf(" "); } else if (tn->tn_op == FSEL) { (void)printf("o=%d, l=%d ", tn->tn_type->t_foffs, tn->tn_type->t_flen); } (void)printf("%s\n", ttos(tn->tn_type)); if (tn->tn_op == NAME || tn->tn_op == CON || tn->tn_op == STRING) return; displexpr(tn->tn_left, offs + 2); if (modtab[tn->tn_op].m_binary || (tn->tn_op == PUSH && tn->tn_right != NULL)) { displexpr(tn->tn_right, offs + 2); } } /* * Called by expr() to recursively perform some tests. */ /* ARGSUSED */ void chkmisc(tnode_t *tn, int vctx, int tctx, int eqwarn, int fcall, int rvdisc, int szof) { tnode_t *ln, *rn; mod_t *mp; int nrvdisc, cvctx, ctctx; op_t op; scl_t sc; dinfo_t *di; if (tn == NULL) return; ln = tn->tn_left; rn = tn->tn_right; mp = &modtab[op = tn->tn_op]; switch (op) { case AMPER: if (ln->tn_op == NAME && (reached || rchflg)) { if (!szof) setsflg(ln->tn_sym); setuflg(ln->tn_sym, fcall, szof); } if (ln->tn_op == STAR && ln->tn_left->tn_op == PLUS) /* check the range of array indices */ chkaidx(ln->tn_left, 1); break; case LOAD: if (ln->tn_op == STAR && ln->tn_left->tn_op == PLUS) /* check the range of array indices */ chkaidx(ln->tn_left, 0); /* FALLTHROUGH */ case PUSH: case INCBEF: case DECBEF: case INCAFT: case DECAFT: case ADDASS: case SUBASS: case MULASS: case DIVASS: case MODASS: case ANDASS: case ORASS: case XORASS: case SHLASS: case SHRASS: if (ln->tn_op == NAME && (reached || rchflg)) { sc = ln->tn_sym->s_scl; /* * Look if there was an asm statement in one of the * compound statements we are in. If not, we don't * print a warning. */ for (di = dcs; di != NULL; di = di->d_nxt) { if (di->d_asm) break; } if (sc != EXTERN && sc != STATIC && !ln->tn_sym->s_set && !szof && di == NULL) { /* %s may be used before set */ warning(158, ln->tn_sym->s_name); setsflg(ln->tn_sym); } setuflg(ln->tn_sym, 0, 0); } break; case ASSIGN: if (ln->tn_op == NAME && !szof && (reached || rchflg)) { setsflg(ln->tn_sym); if (ln->tn_sym->s_scl == EXTERN) outusg(ln->tn_sym); } if (ln->tn_op == STAR && ln->tn_left->tn_op == PLUS) /* check the range of array indices */ chkaidx(ln->tn_left, 0); break; case CALL: if (ln->tn_op != AMPER || ln->tn_left->tn_op != NAME) LERROR("chkmisc()"); if (!szof) outcall(tn, vctx || tctx, rvdisc); break; case EQ: /* equality operator "==" found where "=" was exp. */ if (hflag && eqwarn) warning(160); break; case CON: case NAME: case STRING: return; /* LINTED (enumeration values not handled in switch) */ case OR: case XOR: case NE: case GE: case GT: case LE: case LT: case SHR: case SHL: case MINUS: case PLUS: case MOD: case DIV: case MULT: case STAR: case UMINUS: case UPLUS: case DEC: case INC: case COMPL: case NOT: case POINT: case ARROW: case NOOP: case AND: case FARG: case CASE: case INIT: case RETURN: case ICALL: case CVT: case COMMA: case FSEL: case COLON: case QUEST: case LOGOR: case LOGAND: break; } cvctx = mp->m_vctx; ctctx = mp->m_tctx; /* * values of operands of ':' are not used if the type of at least * one of the operands (for gcc compatibility) is void * XXX test/value context of QUEST should probably be used as * context for both operands of COLON */ if (op == COLON && tn->tn_type->t_tspec == VOID) cvctx = ctctx = 0; nrvdisc = op == CVT && tn->tn_type->t_tspec == VOID; chkmisc(ln, cvctx, ctctx, mp->m_eqwarn, op == CALL, nrvdisc, szof); switch (op) { case PUSH: if (rn != NULL) chkmisc(rn, 0, 0, mp->m_eqwarn, 0, 0, szof); break; case LOGAND: case LOGOR: chkmisc(rn, 0, 1, mp->m_eqwarn, 0, 0, szof); break; case COLON: chkmisc(rn, cvctx, ctctx, mp->m_eqwarn, 0, 0, szof); break; case COMMA: chkmisc(rn, vctx, tctx, mp->m_eqwarn, 0, 0, szof); break; default: if (mp->m_binary) chkmisc(rn, 1, 0, mp->m_eqwarn, 0, 0, szof); break; } } /* * Checks the range of array indices, if possible. * amper is set if only the address of the element is used. This - * means that the index is allowd to refere to the first element + * means that the index is allowed to refer to the first element * after the array. */ static void chkaidx(tnode_t *tn, int amper) { int dim; tnode_t *ln, *rn; int elsz; int64_t con; ln = tn->tn_left; rn = tn->tn_right; /* We can only check constant indices. */ if (rn->tn_op != CON) return; /* Return if the left node does not stem from an array. */ if (ln->tn_op != AMPER) return; if (ln->tn_left->tn_op != STRING && ln->tn_left->tn_op != NAME) return; if (ln->tn_left->tn_type->t_tspec != ARRAY) return; /* * For incomplete array types, we can print a warning only if * the index is negative. */ if (incompl(ln->tn_left->tn_type) && rn->tn_val->v_quad >= 0) return; /* Get the size of one array element */ if ((elsz = length(ln->tn_type->t_subt, NULL)) == 0) return; elsz /= CHAR_BIT; /* Change the unit of the index from bytes to element size. */ if (isutyp(rn->tn_type->t_tspec)) { con = (uint64_t)rn->tn_val->v_quad / elsz; } else { con = rn->tn_val->v_quad / elsz; } dim = ln->tn_left->tn_type->t_dim + (amper ? 1 : 0); if (!isutyp(rn->tn_type->t_tspec) && con < 0) { /* array subscript cannot be negative: %ld */ warning(167, (long)con); } else if (dim > 0 && (uint64_t)con >= dim) { /* array subscript cannot be > %d: %ld */ warning(168, dim - 1, (long)con); } } /* * Check for ordered comparisons of unsigned values with 0. */ static void chkcomp(op_t op, tnode_t *ln, tnode_t *rn) { char buf[64]; tspec_t lt, rt; mod_t *mp; lt = ln->tn_type->t_tspec; rt = rn->tn_type->t_tspec; mp = &modtab[op]; if (ln->tn_op != CON && rn->tn_op != CON) return; if (!isityp(lt) || !isityp(rt)) return; if ((hflag || pflag) && lt == CHAR && rn->tn_op == CON && (rn->tn_val->v_quad < 0 || rn->tn_val->v_quad > ~(~0 << (CHAR_BIT - 1)))) { /* nonportable character comparison, op %s */ warning(230, mp->m_name); return; } if ((hflag || pflag) && rt == CHAR && ln->tn_op == CON && (ln->tn_val->v_quad < 0 || ln->tn_val->v_quad > ~(~0 << (CHAR_BIT - 1)))) { /* nonportable character comparison, op %s */ warning(230, mp->m_name); return; } if (isutyp(lt) && !isutyp(rt) && rn->tn_op == CON && rn->tn_val->v_quad <= 0) { if (rn->tn_val->v_quad < 0) { /* comparison of %s with %s, op %s */ warning(162, tyname(buf, sizeof(buf), ln->tn_type), "negative constant", mp->m_name); } else if (op == LT || op == GE || (hflag && op == LE)) { /* comparison of %s with %s, op %s */ warning(162, tyname(buf, sizeof(buf), ln->tn_type), "0", mp->m_name); } return; } if (isutyp(rt) && !isutyp(lt) && ln->tn_op == CON && ln->tn_val->v_quad <= 0) { if (ln->tn_val->v_quad < 0) { /* comparison of %s with %s, op %s */ warning(162, "negative constant", tyname(buf, sizeof(buf), rn->tn_type), mp->m_name); } else if (op == GT || op == LE || (hflag && op == GE)) { /* comparison of %s with %s, op %s */ warning(162, "0", tyname(buf, sizeof(buf), rn->tn_type), mp->m_name); } return; } } /* - * Takes an expression an returns 0 if this expression can be used + * Takes an expression and returns 0 if this expression can be used * for static initialisation, otherwise -1. * * Constant initialisation expressions must be constant or an address * of a static object with an optional offset. In the first case, * the result is returned in *offsp. In the second case, the static * object is returned in *symp and the offset in *offsp. * * The expression can consist of PLUS, MINUS, AMPER, NAME, STRING and * CON. Type conversions are allowed if they do not change binary * representation (including width). */ int conaddr(tnode_t *tn, sym_t **symp, ptrdiff_t *offsp) { sym_t *sym; ptrdiff_t offs1, offs2; tspec_t t, ot; switch (tn->tn_op) { case MINUS: if (tn->tn_right->tn_op == CVT) return conaddr(tn->tn_right, symp, offsp); else if (tn->tn_right->tn_op != CON) return (-1); /* FALLTHROUGH */ case PLUS: offs1 = offs2 = 0; if (tn->tn_left->tn_op == CON) { offs1 = (ptrdiff_t)tn->tn_left->tn_val->v_quad; if (conaddr(tn->tn_right, &sym, &offs2) == -1) return (-1); } else if (tn->tn_right->tn_op == CON) { offs2 = (ptrdiff_t)tn->tn_right->tn_val->v_quad; if (tn->tn_op == MINUS) offs2 = -offs2; if (conaddr(tn->tn_left, &sym, &offs1) == -1) return (-1); } else { return (-1); } *symp = sym; *offsp = offs1 + offs2; break; case AMPER: if (tn->tn_left->tn_op == NAME) { *symp = tn->tn_left->tn_sym; *offsp = 0; } else if (tn->tn_left->tn_op == STRING) { /* * If this would be the front end of a compiler we * would return a label instead of 0. */ *offsp = 0; } break; case CVT: t = tn->tn_type->t_tspec; ot = tn->tn_left->tn_type->t_tspec; if ((!isityp(t) && t != PTR) || (!isityp(ot) && ot != PTR)) return (-1); else if (psize(t) != psize(ot)) { return (-1); } if (conaddr(tn->tn_left, symp, offsp) == -1) return (-1); break; default: return (-1); } return (0); } /* * Concatenate two string constants. */ strg_t * catstrg(strg_t *strg1, strg_t *strg2) { size_t len1, len2, len; if (strg1->st_tspec != strg2->st_tspec) { /* cannot concatenate wide and regular string literals */ error(292); return (strg1); } len = (len1 = strg1->st_len) + (len2 = strg2->st_len); if (strg1->st_tspec == CHAR) { if ((strg1->st_cp = realloc(strg1->st_cp, len + 1)) == NULL) nomem(); (void)memcpy(strg1->st_cp + len1, strg2->st_cp, len2 + 1); free(strg2->st_cp); } else { if ((strg1->st_wcp = realloc(strg1->st_wcp, (len + 1) * sizeof (wchar_t))) == NULL) nomem(); (void)memcpy(strg1->st_wcp + len1, strg2->st_wcp, (len2 + 1) * sizeof (wchar_t)); free(strg2->st_wcp); } strg1->st_len = len; free(strg2); return (strg1); } /* * Print a warning if the given node has operands which should be * parenthesized. * * XXX Does not work if an operand is a constant expression. Constant * expressions are already folded. */ static void precconf(tnode_t *tn) { tnode_t *ln, *rn; op_t lop, rop = NOOP; int lparn, rparn = 0; mod_t *mp; int warn; if (!hflag) return; mp = &modtab[tn->tn_op]; lparn = 0; for (ln = tn->tn_left; ln->tn_op == CVT; ln = ln->tn_left) lparn |= ln->tn_parn; lparn |= ln->tn_parn; lop = ln->tn_op; if (mp->m_binary) { rparn = 0; for (rn = tn->tn_right; tn->tn_op == CVT; rn = rn->tn_left) rparn |= rn->tn_parn; rparn |= rn->tn_parn; rop = rn->tn_op; } warn = 0; switch (tn->tn_op) { case SHL: case SHR: if (!lparn && (lop == PLUS || lop == MINUS)) { warn = 1; } else if (!rparn && (rop == PLUS || rop == MINUS)) { warn = 1; } break; case LOGOR: if (!lparn && lop == LOGAND) { warn = 1; } else if (!rparn && rop == LOGAND) { warn = 1; } break; case AND: case XOR: case OR: if (!lparn && lop != tn->tn_op) { if (lop == PLUS || lop == MINUS) { warn = 1; } else if (lop == AND || lop == XOR) { warn = 1; } } if (!warn && !rparn && rop != tn->tn_op) { if (rop == PLUS || rop == MINUS) { warn = 1; } else if (rop == AND || rop == XOR) { warn = 1; } } break; /* LINTED (enumeration values not handled in switch) */ case DECAFT: case XORASS: case SHLASS: case NOOP: case ARROW: case ORASS: case POINT: case NAME: case NOT: case COMPL: case CON: case INC: case STRING: case DEC: case INCBEF: case DECBEF: case INCAFT: case FSEL: case CALL: case COMMA: case CVT: case ICALL: case LOAD: case PUSH: case RETURN: case INIT: case CASE: case FARG: case SUBASS: case ADDASS: case MODASS: case DIVASS: case MULASS: case ASSIGN: case COLON: case QUEST: case LOGAND: case NE: case EQ: case GE: case GT: case LE: case LT: case MINUS: case PLUS: case MOD: case DIV: case MULT: case AMPER: case STAR: case UMINUS: case SHRASS: case UPLUS: case ANDASS: break; } if (warn) { /* precedence confusion possible: parenthesize! */ warning(169); } } Index: head/usr.bin/xlint/lint2/read.c =================================================================== --- head/usr.bin/xlint/lint2/read.c (revision 286613) +++ head/usr.bin/xlint/lint2/read.c (revision 286614) @@ -1,1245 +1,1245 @@ /* $NetBSD: read.c,v 1.19 2007/09/28 21:53:50 uwe Exp $ */ /* * Copyright (c) 1996 Christopher G. Demetriou. All Rights Reserved. * Copyright (c) 1994, 1995 Jochen Pohl * All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Jochen Pohl for * The NetBSD Project. * 4. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #if defined(__RCSID) && !defined(lint) __RCSID("$NetBSD: read.c,v 1.19 2007/09/28 21:53:50 uwe Exp $"); #endif __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include "lint2.h" /* index of current (included) source file */ static int srcfile; /* * The array pointed to by inpfns maps the file name indices of input files * to the file name indices used in lint2 */ static short *inpfns; static size_t ninpfns; /* * The array pointed to by *fnames maps file name indizes to file names. * Indices of type short are used instead of pointers to save memory. */ const char **fnames; static size_t nfnames; /* * Types are shared (to save memory for the types itself) and accessed * via indices (to save memory for references to types (indices are short)). * To share types, an equal type must be located fast. This is done by a * hash table. Access by indices is done via an array of pointers to the * types. */ typedef struct thtab { const char *th_name; u_short th_idx; struct thtab *th_nxt; } thtab_t; static thtab_t **thtab; /* hash table */ type_t **tlst; /* array for indexed access */ static size_t tlstlen; /* length of tlst */ static hte_t **renametab; /* index of current C source file (as spezified at the command line) */ static int csrcfile; #define inperr() inperror(__FILE__, __LINE__) static void inperror(const char *, size_t); static void setsrc(const char *); static void setfnid(int, const char *); static void funccall(pos_t *, const char *); static void decldef(pos_t *, const char *); static void usedsym(pos_t *, const char *); static u_short inptype(const char *, const char **); static int gettlen(const char *, const char **); static u_short findtype(const char *, size_t, int); static u_short storetyp(type_t *, const char *, size_t, int); static int thash(const char *, size_t); static char *inpqstrg(const char *, const char **); static const char *inpname(const char *, const char **); static int getfnidx(const char *); void readfile(const char *name) { FILE *inp; size_t len; const char *cp; char *line, *eptr, rt = '\0'; int cline, isrc, iline; pos_t pos; if (inpfns == NULL) if ((inpfns = calloc(ninpfns = 128, sizeof (short))) == NULL) nomem(); if (fnames == NULL) if ((fnames = calloc(nfnames = 256, sizeof (char *))) == NULL) nomem(); if (tlstlen == 0) if ((tlst = calloc(tlstlen = 256, sizeof (type_t *))) == NULL) nomem(); if (thtab == NULL) if ((thtab = calloc(THSHSIZ2, sizeof (thtab_t))) == NULL) nomem(); _inithash(&renametab); srcfile = getfnidx(name); if ((inp = fopen(name, "r")) == NULL) err(1, "cannot open %s", name); while ((line = fgetln(inp, &len)) != NULL) { if (len == 0 || line[len - 1] != '\n') inperr(); line[len - 1] = '\0'; cp = line; /* line number in csrcfile */ cline = (int)strtol(cp, &eptr, 10); if (cp == eptr) { - cline = -1; + cline = -1; } else { cp = eptr; } /* record type */ if (*cp != '\0') { rt = *cp++; } else { inperr(); } if (rt == 'S') { setsrc(cp); continue; } else if (rt == 's') { setfnid(cline, cp); continue; } /* * Index of (included) source file. If this index is * different from csrcfile, it refers to an included * file. */ isrc = (int)strtol(cp, &eptr, 10); if (cp == eptr) inperr(); cp = eptr; isrc = inpfns[isrc]; /* line number in isrc */ if (*cp++ != '.') inperr(); iline = (int)strtol(cp, &eptr, 10); if (cp == eptr) inperr(); cp = eptr; pos.p_src = (u_short)csrcfile; pos.p_line = (u_short)cline; pos.p_isrc = (u_short)isrc; pos.p_iline = (u_short)iline; /* process rest of this record */ switch (rt) { case 'c': funccall(&pos, cp); break; case 'd': decldef(&pos, cp); break; case 'u': usedsym(&pos, cp); break; default: inperr(); } } _destroyhash(renametab); if (ferror(inp)) err(1, "read error on %s", name); (void)fclose(inp); } static void inperror(const char *file, size_t line) { errx(1, "%s,%zd: input file error: %s", file, line, fnames[srcfile]); } /* * Set the name of the C source file of the .ln file which is * currently read. */ static void setsrc(const char *cp) { csrcfile = getfnidx(cp); } /* * setfnid() gets as input an index as used in an input file and the * associated file name. If necessary, it creates a new lint2 file * name index for this file name and creates the mapping of the index * as used in the input file to the index used in lint2. */ static void setfnid(int fid, const char *cp) { if (fid == -1) inperr(); if (fid >= ninpfns) { if ((inpfns = realloc(inpfns, (ninpfns * 2) * sizeof (short))) == NULL) nomem(); (void)memset(inpfns + ninpfns, 0, ninpfns * sizeof (short)); ninpfns *= 2; } /* * Should always be true because indices written in the output * file by lint1 are always the previous index + 1. */ if (fid >= ninpfns) errx(1, "internal error: setfnid()"); inpfns[fid] = (u_short)getfnidx(cp); } /* * Process a function call record (c-record). */ static void funccall(pos_t *posp, const char *cp) { arginf_t *ai, **lai; char c, *eptr; int rused, rdisc; hte_t *hte; fcall_t *fcall; const char *name; fcall = xalloc(sizeof (fcall_t)); STRUCT_ASSIGN(fcall->f_pos, *posp); /* read flags */ rused = rdisc = 0; lai = &fcall->f_args; while ((c = *cp) == 'u' || c == 'i' || c == 'd' || c == 'z' || c == 'p' || c == 'n' || c == 's') { cp++; switch (c) { case 'u': if (rused || rdisc) inperr(); rused = 1; break; case 'i': if (rused || rdisc) inperr(); break; case 'd': if (rused || rdisc) inperr(); rdisc = 1; break; case 'z': case 'p': case 'n': case 's': ai = xalloc(sizeof (arginf_t)); ai->a_num = (int)strtol(cp, &eptr, 10); if (cp == eptr) inperr(); cp = eptr; if (c == 'z') { ai->a_pcon = ai->a_zero = 1; } else if (c == 'p') { ai->a_pcon = 1; } else if (c == 'n') { ai->a_ncon = 1; } else { ai->a_fmt = 1; ai->a_fstrg = inpqstrg(cp, &cp); } *lai = ai; lai = &ai->a_nxt; break; } } fcall->f_rused = rused; fcall->f_rdisc = rdisc; /* read name of function */ name = inpname(cp, &cp); /* first look it up in the renaming table, then in the normal table */ hte = _hsearch(renametab, name, 0); if (hte != NULL) hte = hte->h_hte; else hte = hsearch(name, 1); hte->h_used = 1; fcall->f_type = inptype(cp, &cp); *hte->h_lcall = fcall; hte->h_lcall = &fcall->f_nxt; if (*cp != '\0') inperr(); } /* * Process a declaration or definition (d-record). */ static void decldef(pos_t *posp, const char *cp) { sym_t *symp, sym; char c, *ep, *pos1; int used, renamed; hte_t *hte, *renamehte = NULL; const char *name, *rename; (void)memset(&sym, 0, sizeof (sym)); STRUCT_ASSIGN(sym.s_pos, *posp); sym.s_def = NODECL; used = 0; while (strchr("tdeurosvPS", (c = *cp)) != NULL) { cp++; switch (c) { case 't': if (sym.s_def != NODECL) inperr(); sym.s_def = TDEF; break; case 'd': if (sym.s_def != NODECL) inperr(); sym.s_def = DEF; break; case 'e': if (sym.s_def != NODECL) inperr(); sym.s_def = DECL; break; case 'u': if (used) inperr(); used = 1; break; case 'r': if (sym.s_rval) inperr(); sym.s_rval = 1; break; case 'o': if (sym.s_osdef) inperr(); sym.s_osdef = 1; break; case 's': if (sym.s_static) inperr(); sym.s_static = 1; break; case 'v': if (sym.s_va) inperr(); sym.s_va = 1; sym.s_nva = (short)strtol(cp, &ep, 10); if (cp == ep) inperr(); cp = ep; break; case 'P': if (sym.s_prfl) inperr(); sym.s_prfl = 1; sym.s_nprfl = (short)strtol(cp, &ep, 10); if (cp == ep) inperr(); cp = ep; break; case 'S': if (sym.s_scfl) inperr(); sym.s_scfl = 1; sym.s_nscfl = (short)strtol(cp, &ep, 10); if (cp == ep) inperr(); cp = ep; break; } } /* read symbol name, doing renaming if necessary */ name = inpname(cp, &cp); renamed = 0; if (*cp == 'r') { cp++; name = xstrdup(name); rename = inpname(cp, &cp); /* enter it and see if it's already been renamed */ renamehte = _hsearch(renametab, name, 1); if (renamehte->h_hte == NULL) { hte = hsearch(rename, 1); renamehte->h_hte = hte; renamed = 1; } else if (strcmp((hte = renamehte->h_hte)->h_name, rename)) { pos1 = xstrdup(mkpos(&renamehte->h_syms->s_pos)); /* %s renamed multiple times\t%s :: %s */ msg(18, name, pos1, mkpos(&sym.s_pos)); free(pos1); } free((char *)name); } else { /* it might be a previously-done rename */ hte = _hsearch(renametab, name, 0); if (hte != NULL) hte = hte->h_hte; else hte = hsearch(name, 1); } hte->h_used |= used; if (sym.s_def == DEF || sym.s_def == TDEF) hte->h_def = 1; sym.s_type = inptype(cp, &cp); /* * Allocate memory for this symbol only if it was not already * declared or tentatively defined at the same location with * the same type. Works only for symbols with external linkage, * because static symbols, tentatively defined at the same location * but in different translation units are really different symbols. */ for (symp = hte->h_syms; symp != NULL; symp = symp->s_nxt) { if (symp->s_pos.p_isrc == sym.s_pos.p_isrc && symp->s_pos.p_iline == sym.s_pos.p_iline && symp->s_type == sym.s_type && ((symp->s_def == DECL && sym.s_def == DECL) || (!sflag && symp->s_def == TDEF && sym.s_def == TDEF)) && !symp->s_static && !sym.s_static) { break; } } if (symp == NULL) { /* allocsym reserviert keinen Platz fuer s_nva */ if (sym.s_va || sym.s_prfl || sym.s_scfl) { symp = xalloc(sizeof (sym_t)); STRUCT_ASSIGN(*symp, sym); } else { symp = xalloc(sizeof (symp->s_s)); STRUCT_ASSIGN(symp->s_s, sym.s_s); } *hte->h_lsym = symp; hte->h_lsym = &symp->s_nxt; /* XXX hack so we can remember where a symbol was renamed */ if (renamed) renamehte->h_syms = symp; } if (*cp != '\0') inperr(); } /* * Read an u-record (emitted by lint1 if a symbol was used). */ static void usedsym(pos_t *posp, const char *cp) { usym_t *usym; hte_t *hte; const char *name; usym = xalloc(sizeof (usym_t)); STRUCT_ASSIGN(usym->u_pos, *posp); /* needed as delimiter between two numbers */ if (*cp++ != 'x') inperr(); name = inpname(cp, &cp); hte = _hsearch(renametab, name, 0); if (hte != NULL) hte = hte->h_hte; else hte = hsearch(name, 1); hte->h_used = 1; *hte->h_lusym = usym; hte->h_lusym = &usym->u_nxt; } /* * Read a type and return the index of this type. */ static u_short inptype(const char *cp, const char **epp) { char c, s, *eptr; const char *ep; type_t *tp; int narg, i, osdef = 0; size_t tlen; u_short tidx, sidx; int h; /* If we have this type already, return it's index. */ tlen = gettlen(cp, &ep); h = thash(cp, tlen); if ((tidx = findtype(cp, tlen, h)) != 0) { *epp = ep; return (tidx); } /* No, we must create a new type. */ tp = xalloc(sizeof (type_t)); tidx = storetyp(tp, cp, tlen, h); c = *cp++; while (c == 'c' || c == 'v') { if (c == 'c') { tp->t_const = 1; } else { tp->t_volatile = 1; } c = *cp++; } if (c == 's' || c == 'u' || c == 'l' || c == 'e') { s = c; c = *cp++; } else { s = '\0'; } switch (c) { case 'C': tp->t_tspec = s == 's' ? SCHAR : (s == 'u' ? UCHAR : CHAR); break; case 'S': tp->t_tspec = s == 'u' ? USHORT : SHORT; break; case 'I': tp->t_tspec = s == 'u' ? UINT : INT; break; case 'L': tp->t_tspec = s == 'u' ? ULONG : LONG; break; case 'Q': tp->t_tspec = s == 'u' ? UQUAD : QUAD; break; case 'D': tp->t_tspec = s == 's' ? FLOAT : (s == 'l' ? LDOUBLE : DOUBLE); break; case 'V': tp->t_tspec = VOID; break; case 'P': tp->t_tspec = PTR; break; case 'A': tp->t_tspec = ARRAY; break; case 'F': case 'f': osdef = c == 'f'; tp->t_tspec = FUNC; break; case 'T': tp->t_tspec = s == 'e' ? ENUM : (s == 's' ? STRUCT : UNION); break; } switch (tp->t_tspec) { case ARRAY: tp->t_dim = (int)strtol(cp, &eptr, 10); cp = eptr; sidx = inptype(cp, &cp); /* force seq. point! (ditto below) */ tp->t_subt = TP(sidx); break; case PTR: sidx = inptype(cp, &cp); tp->t_subt = TP(sidx); break; case FUNC: c = *cp; if (isdigit((u_char)c)) { if (!osdef) tp->t_proto = 1; narg = (int)strtol(cp, &eptr, 10); cp = eptr; if ((tp->t_args = calloc((size_t)(narg + 1), sizeof (type_t *))) == NULL) nomem(); for (i = 0; i < narg; i++) { if (i == narg - 1 && *cp == 'E') { tp->t_vararg = 1; cp++; } else { sidx = inptype(cp, &cp); tp->t_args[i] = TP(sidx); } } } sidx = inptype(cp, &cp); tp->t_subt = TP(sidx); break; case ENUM: tp->t_tspec = INT; tp->t_isenum = 1; /* FALLTHROUGH */ case STRUCT: case UNION: switch (*cp++) { case '1': tp->t_istag = 1; tp->t_tag = hsearch(inpname(cp, &cp), 1); break; case '2': tp->t_istynam = 1; tp->t_tynam = hsearch(inpname(cp, &cp), 1); break; case '3': tp->t_isuniqpos = 1; tp->t_uniqpos.p_line = strtol(cp, &eptr, 10); cp = eptr; cp++; /* xlate to 'global' file name. */ tp->t_uniqpos.p_file = addoutfile(inpfns[strtol(cp, &eptr, 10)]); cp = eptr; cp++; tp->t_uniqpos.p_uniq = strtol(cp, &eptr, 10); cp = eptr; break; } break; case LONG: case VOID: case LDOUBLE: case DOUBLE: case FLOAT: case UQUAD: case QUAD: case ULONG: case UINT: case INT: case USHORT: case SHORT: case UCHAR: case SCHAR: case CHAR: case UNSIGN: case SIGNED: case NOTSPEC: break; } *epp = cp; return (tidx); } /* * Get the length of a type string. */ static int gettlen(const char *cp, const char **epp) { const char *cp1; char c, s, *eptr; tspec_t t; int narg, i, cm, vm; cp1 = cp; c = *cp++; cm = vm = 0; while (c == 'c' || c == 'v') { if (c == 'c') { if (cm) inperr(); cm = 1; } else { if (vm) inperr(); vm = 1; } c = *cp++; } if (c == 's' || c == 'u' || c == 'l' || c == 'e') { s = c; c = *cp++; } else { s = '\0'; } t = NOTSPEC; switch (c) { case 'C': if (s == 's') { t = SCHAR; } else if (s == 'u') { t = UCHAR; } else if (s == '\0') { t = CHAR; } break; case 'S': if (s == 'u') { t = USHORT; } else if (s == '\0') { t = SHORT; } break; case 'I': if (s == 'u') { t = UINT; } else if (s == '\0') { t = INT; } break; case 'L': if (s == 'u') { t = ULONG; } else if (s == '\0') { t = LONG; } break; case 'Q': if (s == 'u') { t = UQUAD; } else if (s == '\0') { t = QUAD; } break; case 'D': if (s == 's') { t = FLOAT; } else if (s == 'l') { t = LDOUBLE; } else if (s == '\0') { t = DOUBLE; } break; case 'V': if (s == '\0') t = VOID; break; case 'P': if (s == '\0') t = PTR; break; case 'A': if (s == '\0') t = ARRAY; break; case 'F': case 'f': if (s == '\0') t = FUNC; break; case 'T': if (s == 'e') { t = ENUM; } else if (s == 's') { t = STRUCT; } else if (s == 'u') { t = UNION; } break; default: inperr(); } if (t == NOTSPEC) inperr(); switch (t) { case ARRAY: (void)strtol(cp, &eptr, 10); if (cp == eptr) inperr(); cp = eptr; (void)gettlen(cp, &cp); break; case PTR: (void)gettlen(cp, &cp); break; case FUNC: c = *cp; if (isdigit((u_char)c)) { narg = (int)strtol(cp, &eptr, 10); cp = eptr; for (i = 0; i < narg; i++) { if (i == narg - 1 && *cp == 'E') { cp++; } else { (void)gettlen(cp, &cp); } } } (void)gettlen(cp, &cp); break; case ENUM: case STRUCT: case UNION: switch (*cp++) { case '1': (void)inpname(cp, &cp); break; case '2': (void)inpname(cp, &cp); break; case '3': /* unique position: line.file.uniquifier */ (void)strtol(cp, &eptr, 10); if (cp == eptr) inperr(); cp = eptr; if (*cp++ != '.') inperr(); (void)strtol(cp, &eptr, 10); if (cp == eptr) inperr(); cp = eptr; if (*cp++ != '.') inperr(); (void)strtol(cp, &eptr, 10); if (cp == eptr) inperr(); cp = eptr; break; default: inperr(); } break; case FLOAT: case USHORT: case SHORT: case UCHAR: case SCHAR: case CHAR: case UNSIGN: case SIGNED: case NOTSPEC: case INT: case UINT: case DOUBLE: case LDOUBLE: case VOID: case ULONG: case QUAD: case UQUAD: case LONG: break; } *epp = cp; return (cp - cp1); } /* * Search a type by it's type string. */ static u_short findtype(const char *cp, size_t len, int h) { thtab_t *thte; for (thte = thtab[h]; thte != NULL; thte = thte->th_nxt) { if (strncmp(thte->th_name, cp, len) != 0) continue; if (thte->th_name[len] == '\0') return (thte->th_idx); } return (0); } /* * Store a type and it's type string so we can later share this type * if we read the same type string from the input file. */ static u_short storetyp(type_t *tp, const char *cp, size_t len, int h) { static u_int tidx = 1; /* 0 is reserved */ thtab_t *thte; char *name; if (tidx >= USHRT_MAX) errx(1, "sorry, too many types"); if (tidx == tlstlen - 1) { if ((tlst = realloc(tlst, (tlstlen * 2) * sizeof (type_t *))) == NULL) nomem(); (void)memset(tlst + tlstlen, 0, tlstlen * sizeof (type_t *)); tlstlen *= 2; } tlst[tidx] = tp; /* create a hash table entry */ name = xalloc(len + 1); (void)memcpy(name, cp, len); name[len] = '\0'; thte = xalloc(sizeof (thtab_t)); thte->th_name = name; thte->th_idx = tidx; thte->th_nxt = thtab[h]; thtab[h] = thte; return ((u_short)tidx++); } /* * Hash function for types */ static int thash(const char *s, size_t len) { u_int v; v = 0; while (len-- != 0) { v = (v << sizeof (v)) + (u_char)*s++; v ^= v >> (sizeof (v) * CHAR_BIT - sizeof (v)); } return (v % THSHSIZ2); } /* * Read a string enclosed by "". This string may contain quoted chars. */ static char * inpqstrg(const char *src, const char **epp) { char *strg, *dst; size_t slen; int c; int v; if ((dst = strg = malloc(slen = 32)) == NULL) nomem(); if ((c = *src++) != '"') inperr(); if ((c = *src++) == '\0') inperr(); while (c != '"') { if (c == '\\') { if ((c = *src++) == '\0') inperr(); switch (c) { case 'n': c = '\n'; break; case 't': c = '\t'; break; case 'v': c = '\v'; break; case 'b': c = '\b'; break; case 'r': c = '\r'; break; case 'f': c = '\f'; break; case 'a': c = '\a'; break; case '\\': c = '\\'; break; case '"': c = '"'; break; case '\'': c = '\''; break; case '0': case '1': case '2': case '3': v = (c - '0') << 6; if ((c = *src++) < '0' || c > '7') inperr(); v |= (c - '0') << 3; if ((c = *src++) < '0' || c > '7') inperr(); v |= c - '0'; c = (u_char)v; break; default: inperr(); } } /* keep space for trailing '\0' */ if (dst - strg == slen - 1) { if ((strg = realloc(strg, slen * 2)) == NULL) nomem(); dst = strg + (slen - 1); slen *= 2; } *dst++ = (char)c; if ((c = *src++) == '\0') inperr(); } *dst = '\0'; *epp = src; return (strg); } /* * Read the name of a symbol in static memory. */ static const char * inpname(const char *cp, const char **epp) { static char *buf; static size_t blen = 0; size_t len, i; char *eptr, c; len = (int)strtol(cp, &eptr, 10); if (cp == eptr) inperr(); cp = eptr; if (len + 1 > blen) if ((buf = realloc(buf, blen = len + 1)) == NULL) nomem(); for (i = 0; i < len; i++) { c = *cp++; if (!isalnum((unsigned char)c) && c != '_') inperr(); buf[i] = c; } buf[i] = '\0'; *epp = cp; return (buf); } /* * Return the index of a file name. If the name cannot be found, create * a new entry and return the index of the newly created entry. */ static int getfnidx(const char *fn) { int i; /* 0 ist reserved */ for (i = 1; fnames[i] != NULL; i++) { if (strcmp(fnames[i], fn) == 0) break; } if (fnames[i] != NULL) return (i); if (i == nfnames - 1) { if ((fnames = realloc(fnames, (nfnames * 2) * sizeof (char *))) == NULL) nomem(); (void)memset(fnames + nfnames, 0, nfnames * sizeof (char *)); nfnames *= 2; } if ((fnames[i] = strdup(fn)) == NULL) nomem(); return (i); } /* * Separate symbols with static and external linkage. */ void mkstatic(hte_t *hte) { sym_t *sym1, **symp, *sym; fcall_t **callp, *call; usym_t **usymp, *usym; hte_t *nhte; int ofnd; /* Look for first static definition */ for (sym1 = hte->h_syms; sym1 != NULL; sym1 = sym1->s_nxt) { if (sym1->s_static) break; } if (sym1 == NULL) return; /* Do nothing if this name is used only in one translation unit. */ ofnd = 0; for (sym = hte->h_syms; sym != NULL && !ofnd; sym = sym->s_nxt) { if (sym->s_pos.p_src != sym1->s_pos.p_src) ofnd = 1; } for (call = hte->h_calls; call != NULL && !ofnd; call = call->f_nxt) { if (call->f_pos.p_src != sym1->s_pos.p_src) ofnd = 1; } for (usym = hte->h_usyms; usym != NULL && !ofnd; usym = usym->u_nxt) { if (usym->u_pos.p_src != sym1->s_pos.p_src) ofnd = 1; } if (!ofnd) { hte->h_used = 1; /* errors about undef. static symbols are printed in lint1 */ hte->h_def = 1; hte->h_static = 1; return; } /* * Create a new hash table entry * * XXX this entry should be put at the beginning of the list to * avoid to process the same symbol twice. */ for (nhte = hte; nhte->h_link != NULL; nhte = nhte->h_link) continue; nhte->h_link = xmalloc(sizeof (hte_t)); nhte = nhte->h_link; nhte->h_name = hte->h_name; nhte->h_used = 1; nhte->h_def = 1; /* error in lint1 */ nhte->h_static = 1; nhte->h_syms = NULL; nhte->h_lsym = &nhte->h_syms; nhte->h_calls = NULL; nhte->h_lcall = &nhte->h_calls; nhte->h_usyms = NULL; nhte->h_lusym = &nhte->h_usyms; nhte->h_link = NULL; nhte->h_hte = NULL; /* * move all symbols used in this translation unit into the new * hash table entry. */ for (symp = &hte->h_syms; (sym = *symp) != NULL; ) { if (sym->s_pos.p_src == sym1->s_pos.p_src) { sym->s_static = 1; (*symp) = sym->s_nxt; if (hte->h_lsym == &sym->s_nxt) hte->h_lsym = symp; sym->s_nxt = NULL; *nhte->h_lsym = sym; nhte->h_lsym = &sym->s_nxt; } else { symp = &sym->s_nxt; } } for (callp = &hte->h_calls; (call = *callp) != NULL; ) { if (call->f_pos.p_src == sym1->s_pos.p_src) { (*callp) = call->f_nxt; if (hte->h_lcall == &call->f_nxt) hte->h_lcall = callp; call->f_nxt = NULL; *nhte->h_lcall = call; nhte->h_lcall = &call->f_nxt; } else { callp = &call->f_nxt; } } for (usymp = &hte->h_usyms; (usym = *usymp) != NULL; ) { if (usym->u_pos.p_src == sym1->s_pos.p_src) { (*usymp) = usym->u_nxt; if (hte->h_lusym == &usym->u_nxt) hte->h_lusym = usymp; usym->u_nxt = NULL; *nhte->h_lusym = usym; nhte->h_lusym = &usym->u_nxt; } else { usymp = &usym->u_nxt; } } /* h_def must be recalculated for old hte */ hte->h_def = nhte->h_def = 0; for (sym = hte->h_syms; sym != NULL; sym = sym->s_nxt) { if (sym->s_def == DEF || sym->s_def == TDEF) { hte->h_def = 1; break; } } mkstatic(hte); }