diff --git a/usr.bin/xlint/arch/sparc64/targparam.h b/usr.bin/xlint/arch/sparc64/targparam.h index 8d57fbd3b65d..df151f2344ec 100644 --- a/usr.bin/xlint/arch/sparc64/targparam.h +++ b/usr.bin/xlint/arch/sparc64/targparam.h @@ -1,53 +1,55 @@ -/* $NetBSD: targparam.h,v 1.2 2002/01/30 06:55:00 thorpej Exp $ */ +/* $NetBSD: targparam.h,v 1.3 2002/01/31 23:31:34 he 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$ */ /* * Machine-dependent target parameters for lint1. */ #include "lp64.h" /* * Should be set to 1 if the difference of two pointers is of type long * or the value of sizeof is of type unsigned long. Note this MUST be * kept in sync with the compiler! */ #define PTRDIFF_IS_LONG 1 #define SIZEOF_IS_ULONG 1 #define FLOAT_SIZE (4 * CHAR_BIT) #define DOUBLE_SIZE (8 * CHAR_BIT) #define LDOUBLE_SIZE (16 * CHAR_BIT) #define ENUM_SIZE (4 * CHAR_BIT) diff --git a/usr.bin/xlint/common/lint.h b/usr.bin/xlint/common/lint.h index c50d28dd2f84..231d2d6be490 100644 --- a/usr.bin/xlint/common/lint.h +++ b/usr.bin/xlint/common/lint.h @@ -1,126 +1,128 @@ -/* $NetBSD: lint.h,v 1.5 2002/03/07 18:29:56 tv Exp $ */ +/* $NetBSD: lint.h,v 1.7 2003/10/27 00:12:44 lukem 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$ */ #if HAVE_CONFIG_H #include "config.h" #else #define HAVE_DECL_SYS_SIGNAME 1 #endif #include #include #include #include #include #include "param.h" /* * Type specifiers, used in type structures (type_t) and otherwere. */ typedef enum { NOTSPEC = 0, 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 */ NTSPEC } 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 */ + const char *tt_name; /* Bezeichnung des Typs */ } 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" diff --git a/usr.bin/xlint/common/mem.c b/usr.bin/xlint/common/mem.c index 2621081f1aee..8802da1879b0 100644 --- a/usr.bin/xlint/common/mem.c +++ b/usr.bin/xlint/common/mem.c @@ -1,88 +1,93 @@ -/* $NetBSD: mem.c,v 1.2 2002/01/21 19:49:51 tv Exp $ */ +/* $NetBSD: mem.c,v 1.4 2003/10/16 06:35:26 itojun 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: mem.c,v 1.2 2002/01/21 19:49:51 tv Exp $"); +__RCSID("$NetBSD: mem.c,v 1.4 2003/10/16 06:35:26 itojun Exp $"); #endif +__FBSDID("$FreeBSD$"); #include #include #include "lint.h" void * xmalloc(size_t s) { void *p; if ((p = malloc(s)) == NULL) nomem(); return (p); } void * xcalloc(size_t n, size_t s) { void *p; if ((p = calloc(n, s)) == NULL) nomem(); return (p); } void * xrealloc(void *p, size_t s) { + void *n; - if ((p = realloc(p, s)) == NULL) + if ((n = realloc(p, s)) == NULL) { + free(p); nomem(); + } + p = n; return (p); } char * xstrdup(const char *s) { char *s2; if ((s2 = strdup(s)) == NULL) nomem(); return (s2); } void nomem(void) { errx(1, "virtual memory exhausted"); } diff --git a/usr.bin/xlint/lint1/cgram.y b/usr.bin/xlint/lint1/cgram.y index 8791a2d3b8ed..71d38f4f00bb 100644 --- a/usr.bin/xlint/lint1/cgram.y +++ b/usr.bin/xlint/lint1/cgram.y @@ -1,1783 +1,1893 @@ %{ -/* $NetBSD: cgram.y,v 1.23 2002/01/31 19:36:53 tv Exp $ */ +/* $NetBSD: cgram.y,v 1.40 2008/04/25 17:18:24 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 #if defined(__RCSID) && !defined(lint) -__RCSID("$NetBSD: cgram.y,v 1.23 2002/01/31 19:36:53 tv Exp $"); +__RCSID("$NetBSD: cgram.y,v 1.40 2008/04/25 17:18:24 christos Exp $"); #endif __FBSDID("$FreeBSD$"); #include #include #include #include "lint1.h" /* * Contains the level of current declaration. 0 is extern. * Used for symbol table entries. */ int blklev; /* * level for memory allocation. Normaly the same as blklev. * An exeption is the declaration of arguments in prototypes. Memory * for these can't be freed after the declaration, but symbols must * be removed from the symbol table after the declaration. */ int mblklev; /* * Save the no-warns state and restore it to avoid the problem where * if (expr) { stmt } / * NOLINT * / stmt; */ static int onowarn = -1; -static int toicon(tnode_t *); +static int toicon(tnode_t *, int); static void idecl(sym_t *, int, sbuf_t *); static void ignuptorp(void); #ifdef DEBUG -static __inline void CLRWFLGS(void); -static __inline void CLRWFLGS(void) +static inline void CLRWFLGS(void); +static inline void CLRWFLGS(void) { printf("%s, %d: clear flags %s %d\n", curr_pos.p_file, curr_pos.p_line, __FILE__, __LINE__); clrwflgs(); onowarn = -1; } -static __inline void SAVE(void); -static __inline void SAVE(void) +static inline void SAVE(void); +static inline void SAVE(void) { if (onowarn != -1) abort(); printf("%s, %d: save flags %s %d = %d\n", curr_pos.p_file, curr_pos.p_line, __FILE__, __LINE__, nowarn); onowarn = nowarn; } -static __inline void RESTORE(void); -static __inline void RESTORE(void) +static inline void RESTORE(void); +static inline void RESTORE(void) { if (onowarn != -1) { nowarn = onowarn; printf("%s, %d: restore flags %s %d = %d\n", curr_pos.p_file, curr_pos.p_line, __FILE__, __LINE__, nowarn); onowarn = -1; } else CLRWFLGS(); } #else #define CLRWFLGS() clrwflgs(), onowarn = -1 #define SAVE() onowarn = nowarn #define RESTORE() (void)(onowarn == -1 ? (clrwflgs(), 0) : (nowarn = onowarn)) #endif %} +%expect 1 + %union { int y_int; val_t *y_val; sbuf_t *y_sb; sym_t *y_sym; op_t y_op; scl_t y_scl; tspec_t y_tspec; tqual_t y_tqual; type_t *y_type; tnode_t *y_tnode; + range_t y_range; strg_t *y_strg; pqinf_t *y_pqinf; }; %token T_LBRACE T_RBRACE T_LBRACK T_RBRACK T_LPARN T_RPARN %token T_STROP %token T_UNOP %token T_INCDEC %token T_SIZEOF %token T_MULT %token T_DIVOP %token T_ADDOP %token T_SHFTOP %token T_RELOP %token T_EQOP %token T_AND %token T_XOR %token T_OR %token T_LOGAND %token T_LOGOR %token T_QUEST %token T_COLON %token T_ASSIGN %token T_OPASS %token T_COMMA %token T_SEMI %token T_ELLIPSE /* storage classes (extern, static, auto, register and typedef) */ %token T_SCLASS /* types (char, int, short, long, unsigned, signed, float, double, void) */ %token T_TYPE /* qualifiers (const, volatile) */ %token T_QUAL /* struct or union */ %token T_SOU /* enum */ %token T_ENUM /* remaining keywords */ %token T_CASE %token T_DEFAULT %token T_IF %token T_ELSE %token T_SWITCH %token T_DO %token T_WHILE %token T_FOR %token T_GOTO %token T_CONTINUE %token T_BREAK %token T_RETURN %token T_ASM %token T_SYMBOLRENAME %left T_COMMA %right T_ASSIGN T_OPASS %right T_QUEST T_COLON %left T_LOGOR %left T_LOGAND %left T_OR %left T_XOR %left T_AND %left T_EQOP %left T_RELOP %left T_SHFTOP %left T_ADDOP %left T_MULT T_DIVOP %right T_UNOP T_INCDEC T_SIZEOF %left T_LPARN T_LBRACK T_STROP %token T_NAME %token T_TYPENAME %token T_CON %token T_STRING %type func_decl %type notype_decl %type type_decl %type typespec %type clrtyp_typespec %type notype_typespec %type struct_spec %type enum_spec %type struct_tag %type enum_tag %type struct %type struct_declaration %type identifier %type member_declaration_list_with_rbrace %type member_declaration_list %type member_declaration %type notype_member_decls %type type_member_decls %type notype_member_decl %type type_member_decl %type constant %type enum_declaration %type enums_with_opt_comma %type enums %type enumerator %type ename %type notype_direct_decl %type type_direct_decl %type pointer %type asterisk %type param_decl %type param_list %type abs_decl_param_list %type direct_param_decl %type notype_param_decl %type direct_notype_param_decl %type type_qualifier_list %type type_qualifier %type identifier_list %type abs_decl %type direct_abs_decl %type vararg_parameter_type_list %type parameter_type_list %type parameter_declaration %type expr +%type expr_stmnt_val +%type expr_stmnt_list %type term %type func_arg_list %type point_or_arrow %type type_name %type abstract_declaration %type do_while_expr %type opt_expr %type string %type string2 %type opt_asm_or_symbolrename +%type range +%type lorange %% program: /* empty */ { if (sflag) { /* empty translation unit */ error(272); } else if (!tflag) { /* empty translation unit */ warning(272); } } | translation_unit ; translation_unit: ext_decl | translation_unit ext_decl ; ext_decl: asm_stmnt | func_def { glclup(0); CLRWFLGS(); } | data_def { glclup(0); CLRWFLGS(); } ; data_def: T_SEMI { if (sflag) { /* syntax error: empty declaration */ error(0); } else if (!tflag) { /* syntax error: empty declaration */ warning(0); } } | clrtyp deftyp notype_init_decls T_SEMI { if (sflag) { /* old style declaration; add "int" */ error(1); } else if (!tflag) { /* old style declaration; add "int" */ warning(1); } } | declmods deftyp T_SEMI { if (dcs->d_scl == TYPEDEF) { /* typedef declares no type name */ warning(72); } else { /* empty declaration */ warning(2); } } | declmods deftyp notype_init_decls T_SEMI | declspecs deftyp T_SEMI { if (dcs->d_scl == TYPEDEF) { /* typedef declares no type name */ warning(72); } else if (!dcs->d_nedecl) { /* empty declaration */ warning(2); } } | declspecs deftyp type_init_decls T_SEMI | error T_SEMI { globclup(); } | error T_RBRACE { globclup(); } ; func_def: func_decl { if ($1->s_type->t_tspec != FUNC) { /* syntax error */ error(249); YYERROR; } if ($1->s_type->t_typedef) { /* ()-less function definition */ error(64); YYERROR; } funcdef($1); blklev++; pushdecl(ARG); } opt_arg_declaration_list { popdecl(); blklev--; cluparg(); pushctrl(0); } comp_stmnt { funcend(); popctrl(0); } ; func_decl: clrtyp deftyp notype_decl { $$ = $3; } | declmods deftyp notype_decl { $$ = $3; } | declspecs deftyp type_decl { $$ = $3; } ; opt_arg_declaration_list: /* empty */ | arg_declaration_list ; arg_declaration_list: arg_declaration | arg_declaration_list arg_declaration /* XXX or better "arg_declaration error" ? */ | error ; /* * "arg_declaration" is separated from "declaration" because it * needs other error handling. */ arg_declaration: declmods deftyp T_SEMI { /* empty declaration */ warning(2); } | declmods deftyp notype_init_decls T_SEMI | declspecs deftyp T_SEMI { if (!dcs->d_nedecl) { /* empty declaration */ warning(2); } else { tspec_t ts = dcs->d_type->t_tspec; /* %s declared in argument declaration list */ warning(3, ts == STRUCT ? "struct" : (ts == UNION ? "union" : "enum")); } } | declspecs deftyp type_init_decls T_SEMI { if (dcs->d_nedecl) { tspec_t ts = dcs->d_type->t_tspec; /* %s declared in argument declaration list */ warning(3, ts == STRUCT ? "struct" : (ts == UNION ? "union" : "enum")); } } | declmods error | declspecs error ; declaration: declmods deftyp T_SEMI { if (dcs->d_scl == TYPEDEF) { /* typedef declares no type name */ warning(72); } else { /* empty declaration */ warning(2); } } | declmods deftyp notype_init_decls T_SEMI | declspecs deftyp T_SEMI { if (dcs->d_scl == TYPEDEF) { /* typedef declares no type name */ warning(72); } else if (!dcs->d_nedecl) { /* empty declaration */ warning(2); } } | declspecs deftyp type_init_decls T_SEMI | error T_SEMI ; clrtyp: { clrtyp(); } ; deftyp: /* empty */ { deftyp(); } ; declspecs: clrtyp_typespec { addtype($1); } | declmods typespec { addtype($2); } | declspecs declmod | declspecs notype_typespec { addtype($2); } ; declmods: clrtyp T_QUAL { addqual($2); } | clrtyp T_SCLASS { addscl($2); } | declmods declmod ; declmod: T_QUAL { addqual($1); } | T_SCLASS { addscl($1); } ; clrtyp_typespec: clrtyp notype_typespec { $$ = $2; } | T_TYPENAME clrtyp { $$ = getsym($1)->s_type; } ; typespec: notype_typespec { $$ = $1; } | T_TYPENAME { $$ = getsym($1)->s_type; } ; notype_typespec: T_TYPE { $$ = gettyp($1); } | struct_spec { popdecl(); $$ = $1; } | enum_spec { popdecl(); $$ = $1; } ; struct_spec: struct struct_tag { /* * STDC requires that "struct a;" always introduces * a new tag if "a" is not declared at current level * * yychar is valid because otherwise the parser would * not been able to deceide if he must shift or reduce */ $$ = mktag($2, $1, 0, yychar == T_SEMI); } | struct struct_tag { dcs->d_tagtyp = mktag($2, $1, 1, 0); } struct_declaration { $$ = compltag(dcs->d_tagtyp, $4); } | struct { dcs->d_tagtyp = mktag(NULL, $1, 1, 0); } struct_declaration { $$ = compltag(dcs->d_tagtyp, $3); } | struct error { symtyp = FVFT; $$ = gettyp(INT); } ; struct: T_SOU { symtyp = FTAG; pushdecl($1 == STRUCT ? MOS : MOU); dcs->d_offset = 0; dcs->d_stralign = CHAR_BIT; $$ = $1; } ; struct_tag: identifier { $$ = getsym($1); } ; struct_declaration: struct_decl_lbrace member_declaration_list_with_rbrace { $$ = $2; } ; struct_decl_lbrace: T_LBRACE { symtyp = FVFT; } ; member_declaration_list_with_rbrace: member_declaration_list T_SEMI T_RBRACE { $$ = $1; } | member_declaration_list T_RBRACE { if (sflag) { /* syntax req. ";" after last struct/union member */ error(66); } else { /* syntax req. ";" after last struct/union member */ warning(66); } $$ = $1; } | T_RBRACE { $$ = NULL; } ; member_declaration_list: member_declaration { $$ = $1; } | member_declaration_list T_SEMI member_declaration { $$ = lnklst($1, $3); } ; member_declaration: noclass_declmods deftyp { /* too late, i know, but getsym() compensates it */ symtyp = FMOS; } notype_member_decls { symtyp = FVFT; $$ = $4; } | noclass_declspecs deftyp { symtyp = FMOS; } type_member_decls { symtyp = FVFT; $$ = $4; } | noclass_declmods deftyp { /* struct or union member must be named */ warning(49); $$ = NULL; } | noclass_declspecs deftyp { /* struct or union member must be named */ warning(49); $$ = NULL; } | error { symtyp = FVFT; $$ = NULL; } ; noclass_declspecs: clrtyp_typespec { addtype($1); } | noclass_declmods typespec { addtype($2); } | noclass_declspecs T_QUAL { addqual($2); } | noclass_declspecs notype_typespec { addtype($2); } ; noclass_declmods: clrtyp T_QUAL { addqual($2); } | noclass_declmods T_QUAL { addqual($2); } ; notype_member_decls: notype_member_decl { $$ = decl1str($1); } | notype_member_decls { symtyp = FMOS; } T_COMMA type_member_decl { $$ = lnklst($1, decl1str($4)); } ; type_member_decls: type_member_decl { $$ = decl1str($1); } | type_member_decls { symtyp = FMOS; } T_COMMA type_member_decl { $$ = lnklst($1, decl1str($4)); } ; notype_member_decl: notype_decl { $$ = $1; } | notype_decl T_COLON constant { - $$ = bitfield($1, toicon($3)); + $$ = bitfield($1, toicon($3, 1)); } | { symtyp = FVFT; } T_COLON constant { - $$ = bitfield(NULL, toicon($3)); + $$ = bitfield(NULL, toicon($3, 1)); } ; type_member_decl: type_decl { $$ = $1; } | type_decl T_COLON constant { - $$ = bitfield($1, toicon($3)); + $$ = bitfield($1, toicon($3, 1)); } | { symtyp = FVFT; } T_COLON constant { - $$ = bitfield(NULL, toicon($3)); + $$ = bitfield(NULL, toicon($3, 1)); } ; enum_spec: enum enum_tag { $$ = mktag($2, ENUM, 0, 0); } | enum enum_tag { dcs->d_tagtyp = mktag($2, ENUM, 1, 0); } enum_declaration { $$ = compltag(dcs->d_tagtyp, $4); } | enum { dcs->d_tagtyp = mktag(NULL, ENUM, 1, 0); } enum_declaration { $$ = compltag(dcs->d_tagtyp, $3); } | enum error { symtyp = FVFT; $$ = gettyp(INT); } ; enum: T_ENUM { symtyp = FTAG; pushdecl(ENUMCON); } ; enum_tag: identifier { $$ = getsym($1); } ; enum_declaration: enum_decl_lbrace enums_with_opt_comma T_RBRACE { $$ = $2; } ; enum_decl_lbrace: T_LBRACE { symtyp = FVFT; enumval = 0; } ; enums_with_opt_comma: enums { $$ = $1; } | enums T_COMMA { if (sflag) { /* trailing "," prohibited in enum declaration */ error(54); } else { /* trailing "," prohibited in enum declaration */ (void)gnuism(54); } $$ = $1; } ; enums: enumerator { $$ = $1; } | enums T_COMMA enumerator { $$ = lnklst($1, $3); } | error { $$ = NULL; } ; enumerator: ename { $$ = ename($1, enumval, 1); } | ename T_ASSIGN constant { - $$ = ename($1, toicon($3), 0); + $$ = ename($1, toicon($3, 1), 0); } ; ename: identifier { $$ = getsym($1); } ; notype_init_decls: notype_init_decl | notype_init_decls T_COMMA type_init_decl ; type_init_decls: type_init_decl | type_init_decls T_COMMA type_init_decl ; notype_init_decl: notype_decl opt_asm_or_symbolrename { idecl($1, 0, $2); chksz($1); } | notype_decl opt_asm_or_symbolrename { idecl($1, 1, $2); } T_ASSIGN initializer { chksz($1); } ; type_init_decl: type_decl opt_asm_or_symbolrename { idecl($1, 0, $2); chksz($1); } | type_decl opt_asm_or_symbolrename { idecl($1, 1, $2); } T_ASSIGN initializer { chksz($1); } ; notype_decl: notype_direct_decl { $$ = $1; } | pointer notype_direct_decl { $$ = addptr($2, $1); } ; notype_direct_decl: T_NAME { $$ = dname(getsym($1)); } | T_LPARN type_decl T_RPARN { $$ = $2; } | notype_direct_decl T_LBRACK T_RBRACK { $$ = addarray($1, 0, 0); } | notype_direct_decl T_LBRACK constant T_RBRACK { - $$ = addarray($1, 1, toicon($3)); + $$ = addarray($1, 1, toicon($3, 0)); } | notype_direct_decl param_list { $$ = addfunc($1, $2); popdecl(); blklev--; } ; type_decl: type_direct_decl { $$ = $1; } | pointer type_direct_decl { $$ = addptr($2, $1); } ; type_direct_decl: identifier { $$ = dname(getsym($1)); } | T_LPARN type_decl T_RPARN { $$ = $2; } | type_direct_decl T_LBRACK T_RBRACK { $$ = addarray($1, 0, 0); } | type_direct_decl T_LBRACK constant T_RBRACK { - $$ = addarray($1, 1, toicon($3)); + $$ = addarray($1, 1, toicon($3, 0)); } | type_direct_decl param_list { $$ = addfunc($1, $2); popdecl(); blklev--; } ; /* * param_decl and notype_param_decl exist to avoid a conflict in * argument lists. A typename enclosed in parens should always be * treated as a typename, not an argument. * "typedef int a; f(int (a));" is "typedef int a; f(int foo(a));" * not "typedef int a; f(int a);" */ param_decl: direct_param_decl { $$ = $1; } | pointer direct_param_decl { $$ = addptr($2, $1); } ; direct_param_decl: identifier { $$ = dname(getsym($1)); } | T_LPARN notype_param_decl T_RPARN { $$ = $2; } | direct_param_decl T_LBRACK T_RBRACK { $$ = addarray($1, 0, 0); } | direct_param_decl T_LBRACK constant T_RBRACK { - $$ = addarray($1, 1, toicon($3)); + $$ = addarray($1, 1, toicon($3, 0)); } | direct_param_decl param_list { $$ = addfunc($1, $2); popdecl(); blklev--; } ; notype_param_decl: direct_notype_param_decl { $$ = $1; } | pointer direct_notype_param_decl { $$ = addptr($2, $1); } ; direct_notype_param_decl: T_NAME { $$ = dname(getsym($1)); } | T_LPARN notype_param_decl T_RPARN { $$ = $2; } | direct_notype_param_decl T_LBRACK T_RBRACK { $$ = addarray($1, 0, 0); } | direct_notype_param_decl T_LBRACK constant T_RBRACK { - $$ = addarray($1, 1, toicon($3)); + $$ = addarray($1, 1, toicon($3, 0)); } | direct_notype_param_decl param_list { $$ = addfunc($1, $2); popdecl(); blklev--; } ; pointer: asterisk { $$ = $1; } | asterisk type_qualifier_list { $$ = mergepq($1, $2); } | asterisk pointer { $$ = mergepq($1, $2); } | asterisk type_qualifier_list pointer { $$ = mergepq(mergepq($1, $2), $3); } ; asterisk: T_MULT { $$ = xcalloc(1, sizeof (pqinf_t)); $$->p_pcnt = 1; } ; type_qualifier_list: type_qualifier { $$ = $1; } | type_qualifier_list type_qualifier { $$ = mergepq($1, $2); } ; type_qualifier: T_QUAL { $$ = xcalloc(1, sizeof (pqinf_t)); if ($1 == CONST) { $$->p_const = 1; } else { $$->p_volatile = 1; } } ; param_list: id_list_lparn identifier_list T_RPARN { $$ = $2; } | abs_decl_param_list { $$ = $1; } ; id_list_lparn: T_LPARN { blklev++; pushdecl(PARG); } ; identifier_list: T_NAME { $$ = iname(getsym($1)); } | identifier_list T_COMMA T_NAME { $$ = lnklst($1, iname(getsym($3))); } | identifier_list error { $$ = $1; } ; abs_decl_param_list: abs_decl_lparn T_RPARN { $$ = NULL; } | abs_decl_lparn vararg_parameter_type_list T_RPARN { dcs->d_proto = 1; $$ = $2; } | abs_decl_lparn error T_RPARN { $$ = NULL; } ; abs_decl_lparn: T_LPARN { blklev++; pushdecl(PARG); } ; vararg_parameter_type_list: parameter_type_list { $$ = $1; } | parameter_type_list T_COMMA T_ELLIPSE { dcs->d_vararg = 1; $$ = $1; } | T_ELLIPSE { if (sflag) { /* ANSI C requires formal parameter before "..." */ error(84); } else if (!tflag) { /* ANSI C requires formal parameter before "..." */ warning(84); } dcs->d_vararg = 1; $$ = NULL; } ; parameter_type_list: parameter_declaration { $$ = $1; } | parameter_type_list T_COMMA parameter_declaration { $$ = lnklst($1, $3); } ; parameter_declaration: declmods deftyp { $$ = decl1arg(aname(), 0); } | declspecs deftyp { $$ = decl1arg(aname(), 0); } | declmods deftyp notype_param_decl { $$ = decl1arg($3, 0); } /* * param_decl is needed because of following conflict: * "typedef int a; f(int (a));" could be parsed as * "function with argument a of type int", or * "function with an abstract argument of type function". * This grammar realizes the second case. */ | declspecs deftyp param_decl { $$ = decl1arg($3, 0); } | declmods deftyp abs_decl { $$ = decl1arg($3, 0); } | declspecs deftyp abs_decl { $$ = decl1arg($3, 0); } ; opt_asm_or_symbolrename: /* expect only one */ /* empty */ { $$ = NULL; } | T_ASM T_LPARN T_STRING T_RPARN { freeyyv(&$3, T_STRING); $$ = NULL; } | T_SYMBOLRENAME T_LPARN T_NAME T_RPARN { $$ = $3; } ; initializer: init_expr ; init_expr: expr %prec T_COMMA { mkinit($1); } + | init_by_name init_expr %prec T_COMMA | init_lbrace init_expr_list init_rbrace | init_lbrace init_expr_list T_COMMA init_rbrace | error ; init_expr_list: init_expr %prec T_COMMA | init_expr_list T_COMMA init_expr ; +lorange: + constant T_ELLIPSE { + $$.lo = toicon($1, 1); + } + ; +range: + constant { + $$.lo = toicon($1, 1); + $$.hi = $$.lo + 1; + } + | lorange constant { + $$.lo = $1.lo; + $$.hi = toicon($2, 1); + } + ; + +init_by_name: + T_LBRACK range T_RBRACK T_ASSIGN { + if (!Sflag) + warning(321); + } + | point identifier T_ASSIGN { + if (!Sflag) + warning(313); + memberpush($2); + } + | identifier T_COLON { + gnuism(315); + memberpush($1); + } + ; + init_lbrace: T_LBRACE { initlbr(); } ; init_rbrace: T_RBRACE { initrbr(); } ; type_name: { pushdecl(ABSTRACT); } abstract_declaration { popdecl(); $$ = $2->s_type; } ; abstract_declaration: noclass_declmods deftyp { $$ = decl1abs(aname()); } | noclass_declspecs deftyp { $$ = decl1abs(aname()); } | noclass_declmods deftyp abs_decl { $$ = decl1abs($3); } | noclass_declspecs deftyp abs_decl { $$ = decl1abs($3); } ; abs_decl: pointer { $$ = addptr(aname(), $1); } | direct_abs_decl { $$ = $1; } | pointer direct_abs_decl { $$ = addptr($2, $1); } ; direct_abs_decl: T_LPARN abs_decl T_RPARN { $$ = $2; } | T_LBRACK T_RBRACK { $$ = addarray(aname(), 0, 0); } | T_LBRACK constant T_RBRACK { - $$ = addarray(aname(), 1, toicon($2)); + $$ = addarray(aname(), 1, toicon($2, 0)); } | direct_abs_decl T_LBRACK T_RBRACK { $$ = addarray($1, 0, 0); } | direct_abs_decl T_LBRACK constant T_RBRACK { - $$ = addarray($1, 1, toicon($3)); + $$ = addarray($1, 1, toicon($3, 0)); } | abs_decl_param_list { $$ = addfunc(aname(), $1); popdecl(); blklev--; } | direct_abs_decl abs_decl_param_list { $$ = addfunc($1, $2); popdecl(); blklev--; } ; -stmnt: +non_expr_stmnt: labeled_stmnt - | expr_stmnt | comp_stmnt | selection_stmnt | iteration_stmnt | jump_stmnt { ftflg = 0; } | asm_stmnt + +stmnt: + expr_stmnt + | non_expr_stmnt ; labeled_stmnt: label stmnt ; label: identifier T_COLON { symtyp = FLAB; label(T_NAME, getsym($1), NULL); } | T_CASE constant T_COLON { label(T_CASE, NULL, $2); ftflg = 1; - } + } + | T_CASE constant T_ELLIPSE constant T_COLON { + /* XXX: We don't fill all cases */ + label(T_CASE, NULL, $2); + ftflg = 1; + } | T_DEFAULT T_COLON { label(T_DEFAULT, NULL, NULL); ftflg = 1; } ; comp_stmnt: - compstmnt_lbrace declaration_list opt_stmnt_list compstmnt_rbrace - | compstmnt_lbrace opt_stmnt_list compstmnt_rbrace + comp_stmnt_lbrace declaration_list opt_stmnt_list comp_stmnt_rbrace + | comp_stmnt_lbrace opt_stmnt_list comp_stmnt_rbrace ; -compstmnt_lbrace: +comp_stmnt_lbrace: T_LBRACE { blklev++; mblklev++; pushdecl(AUTO); } ; -compstmnt_rbrace: +comp_stmnt_rbrace: T_RBRACE { popdecl(); freeblk(); mblklev--; blklev--; ftflg = 0; } ; opt_stmnt_list: /* empty */ | stmnt_list ; stmnt_list: stmnt | stmnt_list stmnt { RESTORE(); } | stmnt_list error T_SEMI ; expr_stmnt: expr T_SEMI { - expr($1, 0, 0); + expr($1, 0, 0, 1); ftflg = 0; } | T_SEMI { ftflg = 0; } ; +/* + * The following two productions are used to implement + * ({ [[decl-list] stmt-list] }). + * XXX: This is not well tested. + */ +expr_stmnt_val: + expr T_SEMI { + /* XXX: We should really do that only on the last name */ + if ($1->tn_op == NAME) + $1->tn_sym->s_used = 1; + $$ = $1; + expr($1, 0, 0, 0); + ftflg = 0; + } + | non_expr_stmnt { + $$ = getnode(); + $$->tn_type = gettyp(VOID); + } + ; + +expr_stmnt_list: + expr_stmnt_val + | expr_stmnt_list expr_stmnt_val { + $$ = $2; + } + | expr_stmnt_list expr_stmnt_val + ; + selection_stmnt: if_without_else { SAVE(); if2(); if3(0); } | if_without_else T_ELSE { SAVE(); if2(); } stmnt { CLRWFLGS(); if3(1); } | if_without_else T_ELSE error { CLRWFLGS(); if3(0); } | switch_expr stmnt { CLRWFLGS(); switch2(); } | switch_expr error { CLRWFLGS(); switch2(); } ; if_without_else: if_expr stmnt | if_expr error ; if_expr: T_IF T_LPARN expr T_RPARN { if1($3); CLRWFLGS(); } ; switch_expr: T_SWITCH T_LPARN expr T_RPARN { switch1($3); CLRWFLGS(); } ; do_stmnt: do stmnt { CLRWFLGS(); } ; iteration_stmnt: while_expr stmnt { CLRWFLGS(); while2(); } | while_expr error { CLRWFLGS(); while2(); } | do_stmnt do_while_expr { do2($2); ftflg = 0; } | do error { CLRWFLGS(); do2(NULL); } | for_exprs stmnt { CLRWFLGS(); for2(); } | for_exprs error { CLRWFLGS(); for2(); } ; while_expr: T_WHILE T_LPARN expr T_RPARN { while1($3); CLRWFLGS(); } ; do: T_DO { do1(); } ; do_while_expr: T_WHILE T_LPARN expr T_RPARN T_SEMI { $$ = $3; } ; for_exprs: T_FOR T_LPARN opt_expr T_SEMI opt_expr T_SEMI opt_expr T_RPARN { for1($3, $5, $7); CLRWFLGS(); } ; opt_expr: /* empty */ { $$ = NULL; } | expr { $$ = $1; } ; jump_stmnt: goto identifier T_SEMI { dogoto(getsym($2)); } | goto error T_SEMI { symtyp = FVFT; } | T_CONTINUE T_SEMI { docont(); } | T_BREAK T_SEMI { dobreak(); } | T_RETURN T_SEMI { doreturn(NULL); } | T_RETURN expr T_SEMI { doreturn($2); } ; goto: T_GOTO { symtyp = FLAB; } ; asm_stmnt: T_ASM T_LPARN read_until_rparn T_SEMI { setasm(); } | T_ASM T_QUAL T_LPARN read_until_rparn T_SEMI { setasm(); } | T_ASM error ; read_until_rparn: /* empty */ { ignuptorp(); } ; declaration_list: declaration { CLRWFLGS(); } | declaration_list declaration { CLRWFLGS(); } ; constant: expr %prec T_COMMA { $$ = $1; } ; expr: expr T_MULT expr { $$ = build(MULT, $1, $3); } | expr T_DIVOP expr { $$ = build($2, $1, $3); } | expr T_ADDOP expr { $$ = build($2, $1, $3); } | expr T_SHFTOP expr { $$ = build($2, $1, $3); } | expr T_RELOP expr { $$ = build($2, $1, $3); } | expr T_EQOP expr { $$ = build($2, $1, $3); } | expr T_AND expr { $$ = build(AND, $1, $3); } | expr T_XOR expr { $$ = build(XOR, $1, $3); } | expr T_OR expr { $$ = build(OR, $1, $3); } | expr T_LOGAND expr { $$ = build(LOGAND, $1, $3); } | expr T_LOGOR expr { $$ = build(LOGOR, $1, $3); } | expr T_QUEST expr T_COLON expr { $$ = build(QUEST, $1, build(COLON, $3, $5)); } | expr T_ASSIGN expr { $$ = build(ASSIGN, $1, $3); } | expr T_OPASS expr { $$ = build($2, $1, $3); } | expr T_COMMA expr { $$ = build(COMMA, $1, $3); } | term { $$ = $1; } ; term: T_NAME { /* XXX really necessary? */ if (yychar < 0) yychar = yylex(); $$ = getnnode(getsym($1), yychar); } | string { $$ = getsnode($1); } | T_CON { $$ = getcnode(gettyp($1->v_tspec), $1); } | T_LPARN expr T_RPARN { if ($2 != NULL) $2->tn_parn = 1; $$ = $2; } + | T_LPARN comp_stmnt_lbrace declaration_list expr_stmnt_list { + blklev--; + mblklev--; + initsym = mktempsym(duptyp($4->tn_type)); + mblklev++; + blklev++; + gnuism(320); + } comp_stmnt_rbrace T_RPARN { + $$ = getnnode(initsym, 0); + } + | T_LPARN comp_stmnt_lbrace expr_stmnt_list { + blklev--; + mblklev--; + initsym = mktempsym($3->tn_type); + mblklev++; + blklev++; + gnuism(320); + } comp_stmnt_rbrace T_RPARN { + $$ = getnnode(initsym, 0); + } | term T_INCDEC { $$ = build($2 == INC ? INCAFT : DECAFT, $1, NULL); } | T_INCDEC term { $$ = build($1 == INC ? INCBEF : DECBEF, $2, NULL); } | T_MULT term { $$ = build(STAR, $2, NULL); } | T_AND term { $$ = build(AMPER, $2, NULL); } | T_UNOP term { $$ = build($1, $2, NULL); } | T_ADDOP term { if (tflag && $1 == PLUS) { /* unary + is illegal in traditional C */ warning(100); } $$ = build($1 == PLUS ? UPLUS : UMINUS, $2, NULL); } | term T_LBRACK expr T_RBRACK { $$ = build(STAR, build(PLUS, $1, $3), NULL); } | term T_LPARN T_RPARN { $$ = funccall($1, NULL); } | term T_LPARN func_arg_list T_RPARN { $$ = funccall($1, $3); } | term point_or_arrow T_NAME { if ($1 != NULL) { sym_t *msym; /* XXX strmemb should be integrated in build() */ if ($2 == ARROW) { /* must to this before strmemb is called */ $1 = cconv($1); } msym = strmemb($1, $2, getsym($3)); $$ = build($2, $1, getnnode(msym, 0)); } else { $$ = NULL; } } | T_SIZEOF term %prec T_SIZEOF { if (($$ = $2 == NULL ? NULL : bldszof($2->tn_type)) != NULL) chkmisc($2, 0, 0, 0, 0, 0, 1); } | T_SIZEOF T_LPARN type_name T_RPARN %prec T_SIZEOF { $$ = bldszof($3); } | T_LPARN type_name T_RPARN term %prec T_UNOP { $$ = cast($4, $2); } + | T_LPARN type_name T_RPARN %prec T_UNOP { + sym_t *tmp = mktempsym($2); + idecl(tmp, 1, NULL); + } init_lbrace init_expr_list init_rbrace { + if (!Sflag) + gnuism(319); + $$ = getnnode(initsym, 0); + } ; string: T_STRING { $$ = $1; } | T_STRING string2 { $$ = catstrg($1, $2); } ; string2: T_STRING { if (tflag) { /* concatenated strings are illegal in traditional C */ warning(219); } $$ = $1; } | string2 T_STRING { $$ = catstrg($1, $2); } ; func_arg_list: expr %prec T_COMMA { $$ = funcarg(NULL, $1); } | func_arg_list T_COMMA expr { $$ = funcarg($1, $3); } ; point_or_arrow: T_STROP { symtyp = FMOS; $$ = $1; } ; +point: + T_STROP { + if ($1 != POINT) + error(249); + } + ; + identifier: T_NAME { $$ = $1; } | T_TYPENAME { $$ = $1; } ; %% /* ARGSUSED */ int yyerror(char *msg) { - error(249); if (++sytxerr >= 5) norecover(); return (0); } static __inline int uq_gt(uint64_t, uint64_t); static __inline int uq_gt(uint64_t a, uint64_t b) { return (a > b); } static __inline int q_gt(int64_t, int64_t); static __inline int q_gt(int64_t a, int64_t b) { return (a > b); } #define q_lt(a, b) q_gt(b, a) /* * Gets a node for a constant and returns the value of this constant * as integer. * Is the node not constant or too large for int or of type float, * a warning will be printed. * * toicon() should be used only inside declarations. If it is used in * expressions, it frees the memory used for the expression. */ static int -toicon(tnode_t *tn) +toicon(tnode_t *tn, int required) { int i; tspec_t t; val_t *v; - v = constant(tn); + v = constant(tn, required); /* * Abstract declarations are used inside expression. To free * the memory would be a fatal error. */ if (dcs->d_ctx != ABSTRACT) tfreeblk(); if ((t = v->v_tspec) == FLOAT || t == DOUBLE || t == LDOUBLE) { i = (int)v->v_ldbl; /* integral constant expression expected */ error(55); } else { i = (int)v->v_quad; if (isutyp(t)) { if (uq_gt((uint64_t)v->v_quad, (uint64_t)INT_MAX)) { /* integral constant too large */ warning(56); } } else { if (q_gt(v->v_quad, (int64_t)INT_MAX) || q_lt(v->v_quad, (int64_t)INT_MIN)) { /* integral constant too large */ warning(56); } } } free(v); return (i); } static void idecl(sym_t *decl, int initflg, sbuf_t *rename) { char *s; initerr = 0; initsym = decl; switch (dcs->d_ctx) { case EXTERN: if (rename != NULL) { if (decl->s_rename != NULL) - lerror("idecl() 1"); + LERROR("idecl()"); s = getlblk(1, rename->sb_len + 1); (void)memcpy(s, rename->sb_name, rename->sb_len + 1); decl->s_rename = s; freeyyv(&rename, T_NAME); } decl1ext(decl, initflg); break; case ARG: if (rename != NULL) { /* symbol renaming can't be used on function arguments */ error(310); freeyyv(&rename, T_NAME); break; } (void)decl1arg(decl, initflg); break; case AUTO: if (rename != NULL) { /* symbol renaming can't be used on automatic variables */ error(311); freeyyv(&rename, T_NAME); break; } decl1loc(decl, initflg); break; default: - lerror("idecl() 2"); + LERROR("idecl()"); } if (initflg && !initerr) prepinit(); } /* * Discard all input tokens up to and including the next * unmatched right paren */ static void ignuptorp(void) { int level; if (yychar < 0) yychar = yylex(); freeyyv(&yylval, yychar); level = 1; while (yychar != T_RPARN || --level > 0) { if (yychar == T_LPARN) { level++; } else if (yychar <= 0) { break; } freeyyv(&yylval, yychar = yylex()); } yyclearin; } diff --git a/usr.bin/xlint/lint1/decl.c b/usr.bin/xlint/lint1/decl.c index e62882ff3a32..ace50b6ed8a0 100644 --- a/usr.bin/xlint/lint1/decl.c +++ b/usr.bin/xlint/lint1/decl.c @@ -1,3050 +1,3052 @@ -/* $NetBSD: decl.c,v 1.29 2002/01/18 21:01:39 thorpej Exp $ */ +/* $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.29 2002/01/18 21:01:39 thorpej Exp $"); +__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()) * 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() 1"); + 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()"); + 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)gnuism(265, tflag ? "traditional" : "ANSI"); + (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() 1"); + 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() 1"); + LERROR("popdecl()"); di = dcs; dcs = di->d_nxt; switch (di->d_ctx) { case EXTERN: /* there is nothing after external declarations */ - lerror("popdecl() 2"); + 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() 3"); + 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() 1"); + 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() 2"); + 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() 3"); + 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() 4"); + 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]); + 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()"); + LERROR("length()"); break; } return (elem * elsz); } /* * 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() 1"); + 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()"); + 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() 1"); + LERROR("decl1str()"); if (dcs->d_rdcsym != NULL) { if ((sc = dcs->d_rdcsym->s_scl) != MOS && sc != MOU) /* should be ensured by storesym() */ - lerror("decl1str() 2"); + 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) */ 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(tp)); + 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 */ - warning(39, dsym->s_name); + 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 * 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) { - /* zero or negative array dimension */ - error(20); + /* negative array dimension */ + error(20, n); n = 0; } else if (n == 0 && dim) { - /* zero or negative array dimension */ - warning(20); + /* 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() 1"); + 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() 2"); + 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() 3"); + LERROR("dname()"); } break; default: - lerror("dname() 4"); + 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()"); + 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()"); + 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 */ 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()"); + 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)gnuism(47, ttab[t].tt_name); + (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 * 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 * 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 * 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 * 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() 1"); + 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 */ 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 * 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 * 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() 1"); + 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()"); + 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 * 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() 1"); + 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() 1"); + 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() 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() 1"); + 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() 1"); + 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() 1"); + 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); } diff --git a/usr.bin/xlint/lint1/emit1.c b/usr.bin/xlint/lint1/emit1.c index 762945feddbc..4d198d626d7b 100644 --- a/usr.bin/xlint/lint1/emit1.c +++ b/usr.bin/xlint/lint1/emit1.c @@ -1,601 +1,601 @@ -/* $NetBSD: emit1.c,v 1.11 2002/01/31 19:36:54 tv Exp $ */ +/* $NetBSD: emit1.c,v 1.14 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: emit1.c,v 1.11 2002/01/31 19:36:54 tv Exp $"); +__RCSID("$NetBSD: emit1.c,v 1.14 2004/06/20 22:20:16 jmc Exp $"); #endif __FBSDID("$FreeBSD$"); #include #include "lint1.h" static void outtt(sym_t *, sym_t *); static void outfstrg(strg_t *); /* * Write type into the output buffer. * The type is written as a sequence of substrings, each of which describes a * node of type type_t * a node is coded as follows: * char C * signed char s C * unsigned char u C * short S * unsigned short u S * int I * unsigned int u I * long L * unsigned long u L * long long Q * unsigned long long u Q * float s D * double D * long double l D * void V * * P * [n] A n * () F * (void) F 0 * (n arguments) F n arg1 arg2 ... argn * (n arguments, ...) F n arg1 arg2 ... argn-1 E * (a, b, c, ...) f n arg1 arg2 ... * enum tag e T tag_or_typename * struct tag s T tag_or_typename * union tag u T tag_or_typename * * tag_or_typename 0 no tag or type name * 1 n tag Tag * 2 n typename only type name * * spaces are only for better readability * additionally it is possible to prepend the characters 'c' (for const) * and 'v' (for volatile) */ void outtype(type_t *tp) { int t, s, na; sym_t *arg; tspec_t ts; while (tp != NULL) { if ((ts = tp->t_tspec) == INT && tp->t_isenum) ts = ENUM; switch (ts) { case CHAR: t = 'C'; s = '\0'; break; case SCHAR: t = 'C'; s = 's'; break; case UCHAR: t = 'C'; s = 'u'; break; case SHORT: t = 'S'; s = '\0'; break; case USHORT: t = 'S'; s = 'u'; break; case INT: t = 'I'; s = '\0'; break; case UINT: t = 'I'; s = 'u'; break; case LONG: t = 'L'; s = '\0'; break; case ULONG: t = 'L'; s = 'u'; break; case QUAD: t = 'Q'; s = '\0'; break; case UQUAD: t = 'Q'; s = 'u'; break; case FLOAT: t = 'D'; s = 's'; break; case DOUBLE: t = 'D'; s = '\0'; break; case LDOUBLE: t = 'D'; s = 'l'; break; case VOID: t = 'V'; s = '\0'; break; case PTR: t = 'P'; s = '\0'; break; case ARRAY: t = 'A'; s = '\0'; break; case FUNC: t = 'F'; s = '\0'; break; case ENUM: t = 'T'; s = 'e'; break; case STRUCT: t = 'T'; s = 's'; break; case UNION: t = 'T'; s = 'u'; break; default: - lerror("outtyp() 1"); + LERROR("outtyp()"); } if (tp->t_const) outchar('c'); if (tp->t_volatile) outchar('v'); if (s != '\0') outchar(s); outchar(t); if (ts == ARRAY) { outint(tp->t_dim); } else if (ts == ENUM) { outtt(tp->t_enum->etag, tp->t_enum->etdef); } else if (ts == STRUCT || ts == UNION) { outtt(tp->t_str->stag, tp->t_str->stdef); } else if (ts == FUNC && tp->t_proto) { na = 0; for (arg = tp->t_args; arg != NULL; arg = arg->s_nxt) na++; if (tp->t_vararg) na++; outint(na); for (arg = tp->t_args; arg != NULL; arg = arg->s_nxt) outtype(arg->s_type); if (tp->t_vararg) outchar('E'); } tp = tp->t_subt; } } /* * type to string * used for debugging output * * it uses its own output buffer for conversion */ const char * ttos(type_t *tp) { static ob_t tob; ob_t tmp; if (tob.o_buf == NULL) { tob.o_len = 64; tob.o_buf = tob.o_nxt = xmalloc(tob.o_len); tob.o_end = tob.o_buf + tob.o_len; } tmp = ob; ob = tob; ob.o_nxt = ob.o_buf; outtype(tp); outchar('\0'); tob = ob; ob = tmp; return (tob.o_buf); } /* * write the name of a tag or typename * * if the tag is named, the name of the * tag is written, otherwise, if a typename exists which * refers to this tag, this typename is written */ static void outtt(sym_t *tag, sym_t *tdef) { /* * 0 is no longer used. */ if (tag->s_name != unnamed) { outint(1); outname(tag->s_name); } else if (tdef != NULL) { outint(2); outname(tdef->s_name); } else { outint(3); outint(tag->s_dpos.p_line); outchar('.'); outint(getfnid(tag->s_dpos.p_file)); outchar('.'); outint(tag->s_dpos.p_uniq); } } /* * write information about a global declared/defined symbol * with storage class extern * * informations about function definitions are written in outfdef(), * not here */ void outsym(sym_t *sym, scl_t sc, def_t def) { /* * Static function declarations must also be written to the output * file. Compatibility of function declarations (for both static * and extern functions) must be checked in lint2. Lint1 can't do * this, especially not, if functions are declared at block level * before their first declaration at level 0. */ if (sc != EXTERN && !(sc == STATIC && sym->s_type->t_tspec == FUNC)) return; /* reset buffer */ outclr(); /* * line number of .c source, 'd' for declaration, Id of current * source (.c or .h), and line in current source. */ outint(csrc_pos.p_line); outchar('d'); outint(getfnid(sym->s_dpos.p_file)); outchar('.'); outint(sym->s_dpos.p_line); /* flags */ switch (def) { case DEF: /* defined */ outchar('d'); break; case TDEF: /* tentative defined */ outchar('t'); break; case DECL: /* declared */ outchar('e'); break; default: - lerror("outsym() 2"); + LERROR("outsym()"); } if (llibflg && def != DECL) { /* * mark it as used so we get no warnings from lint2 about * unused symbols in libraries. */ outchar('u'); } if (sc == STATIC) outchar('s'); /* name of the symbol */ outname(sym->s_name); /* renamed name of symbol, if necessary */ if (sym->s_rename) { outchar('r'); outname(sym->s_rename); } /* type of the symbol */ outtype(sym->s_type); } /* * write information about function definition * * this is also done for static functions so we are able to check if * they are called with proper argument types */ void outfdef(sym_t *fsym, pos_t *posp, int rval, int osdef, sym_t *args) { int narg; sym_t *arg; /* reset the buffer */ outclr(); /* * line number of .c source, 'd' for declaration, Id of current * source (.c or .h), and line in current source * * we are already at the end of the function. If we are in the * .c source, posp->p_line is correct, otherwise csrc_pos.p_line * (for functions defined in header files). */ if (posp->p_file == csrc_pos.p_file) { outint(posp->p_line); } else { outint(csrc_pos.p_line); } outchar('d'); outint(getfnid(posp->p_file)); outchar('.'); outint(posp->p_line); /* flags */ /* both SCANFLIKE and PRINTFLIKE imply VARARGS */ if (prflstrg != -1) { nvararg = prflstrg; } else if (scflstrg != -1) { nvararg = scflstrg; } if (nvararg != -1) { outchar('v'); outint(nvararg); } if (scflstrg != -1) { outchar('S'); outint(scflstrg); } if (prflstrg != -1) { outchar('P'); outint(prflstrg); } nvararg = prflstrg = scflstrg = -1; outchar('d'); if (rval) /* has return value */ outchar('r'); if (llibflg) /* * mark it as used so lint2 does not complain about * unused symbols in libraries */ outchar('u'); if (osdef) /* old style function definition */ outchar('o'); if (fsym->s_scl == STATIC) outchar('s'); /* name of function */ outname(fsym->s_name); /* renamed name of function, if necessary */ if (fsym->s_rename) { outchar('r'); outname(fsym->s_rename); } /* argument types and return value */ if (osdef) { narg = 0; for (arg = args; arg != NULL; arg = arg->s_nxt) narg++; outchar('f'); outint(narg); for (arg = args; arg != NULL; arg = arg->s_nxt) outtype(arg->s_type); outtype(fsym->s_type->t_subt); } else { outtype(fsym->s_type); } } /* * write out all information necessary for lint2 to check function * calls * * rvused is set if the return value is used (asigned to a variable) * rvdisc is set if the return value is not used and not ignored * (casted to void) */ void outcall(tnode_t *tn, int rvused, int rvdisc) { tnode_t *args, *arg; int narg, n, i; int64_t q; tspec_t t; /* reset buffer */ outclr(); /* * line number of .c source, 'c' for function call, Id of current * source (.c or .h), and line in current source */ outint(csrc_pos.p_line); outchar('c'); outint(getfnid(curr_pos.p_file)); outchar('.'); outint(curr_pos.p_line); /* * flags; 'u' and 'i' must be last to make sure a letter * is between the numeric argument of a flag and the name of * the function */ narg = 0; args = tn->tn_right; for (arg = args; arg != NULL; arg = arg->tn_right) narg++; /* informations about arguments */ for (n = 1; n <= narg; n++) { /* the last argument is the top one in the tree */ for (i = narg, arg = args; i > n; i--, arg = arg->tn_right) continue; arg = arg->tn_left; if (arg->tn_op == CON) { if (isityp(t = arg->tn_type->t_tspec)) { /* * XXX it would probably be better to * explizitly test the sign */ if ((q = arg->tn_val->v_quad) == 0) { /* zero constant */ outchar('z'); } else if (msb(q, t, 0) == 0) { /* positive if casted to signed */ outchar('p'); } else { /* negative if casted to signed */ outchar('n'); } outint(n); } } else if (arg->tn_op == AMPER && arg->tn_left->tn_op == STRING && arg->tn_left->tn_strg->st_tspec == CHAR) { /* constant string, write all format specifiers */ outchar('s'); outint(n); outfstrg(arg->tn_left->tn_strg); } } /* return value discarded/used/ignored */ outchar(rvdisc ? 'd' : (rvused ? 'u' : 'i')); /* name of the called function */ outname(tn->tn_left->tn_left->tn_sym->s_name); /* types of arguments */ outchar('f'); outint(narg); for (n = 1; n <= narg; n++) { /* the last argument is the top one in the tree */ for (i = narg, arg = args; i > n; i--, arg = arg->tn_right) continue; outtype(arg->tn_left->tn_type); } /* expected type of return value */ outtype(tn->tn_type); } /* * extracts potential format specifiers for printf() and scanf() and * writes them, enclosed in "" and qouted if necessary, to the output buffer */ static void outfstrg(strg_t *strg) { int c, oc, first; u_char *cp; if (strg->st_tspec != CHAR) - lerror("outfstrg() 1"); + LERROR("outfstrg()"); cp = strg->st_cp; outchar('"'); c = *cp++; while (c != '\0') { if (c != '%') { c = *cp++; continue; } outqchar('%'); c = *cp++; /* flags for printf and scanf and *-fieldwidth for printf */ while (c != '\0' && (c == '-' || c == '+' || c == ' ' || c == '#' || c == '0' || c == '*')) { outqchar(c); c = *cp++; } /* numeric field width */ while (c != '\0' && isdigit(c)) { outqchar(c); c = *cp++; } /* precision for printf */ if (c == '.') { outqchar(c); if ((c = *cp++) == '*') { outqchar(c); c = *cp++; } else { while (c != '\0' && isdigit(c)) { outqchar(c); c = *cp++; } } } /* h, l, L and q flags fpr printf and scanf */ if (c == 'h' || c == 'l' || c == 'L' || c == 'q') { outqchar(c); c = *cp++; } /* * The last character. It is always written so we can detect * invalid format specifiers. */ if (c != '\0') { outqchar(c); oc = c; c = *cp++; /* * handle [ for scanf. [-] means that a minus sign * was found at an undefined position. */ if (oc == '[') { if (c == '^') c = *cp++; if (c == ']') c = *cp++; first = 1; while (c != '\0' && c != ']') { if (c == '-') { if (!first && *cp != ']') outqchar(c); } first = 0; c = *cp++; } if (c == ']') { outqchar(c); c = *cp++; } } } } outchar('"'); } /* * writes a record if sym was used */ void outusg(sym_t *sym) { /* reset buffer */ outclr(); /* * line number of .c source, 'u' for used, Id of current * source (.c or .h), and line in current source */ outint(csrc_pos.p_line); outchar('u'); outint(getfnid(curr_pos.p_file)); outchar('.'); outint(curr_pos.p_line); /* necessary to delimit both numbers */ outchar('x'); /* Den Namen des Symbols ausgeben */ outname(sym->s_name); } diff --git a/usr.bin/xlint/lint1/err.c b/usr.bin/xlint/lint1/err.c index 5c56638d0637..e05305e4f8c2 100644 --- a/usr.bin/xlint/lint1/err.c +++ b/usr.bin/xlint/lint1/err.c @@ -1,514 +1,552 @@ -/* $NetBSD: err.c,v 1.17 2002/01/31 19:36:54 tv Exp $ */ +/* $NetBSD: err.c,v 1.40 2009/04/15 01:20:57 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: err.c,v 1.17 2002/01/31 19:36:54 tv Exp $"); +__RCSID("$NetBSD: err.c,v 1.40 2009/04/15 01:20:57 christos Exp $"); #endif __FBSDID("$FreeBSD$"); #include #include #include #include "lint1.h" /* number of errors found */ int nerr; /* number of syntax errors */ int sytxerr; static const char *lbasename(const char *); static void verror(int, va_list); static void vwarning(int, va_list); const char *msgs[] = { "syntax error: empty declaration", /* 0 */ "old style declaration; add int", /* 1 */ "empty declaration", /* 2 */ "%s declared in argument declaration list", /* 3 */ "illegal type combination", /* 4 */ "modifying typedef with '%s'; only qualifiers allowed", /* 5 */ "use 'double' instead of 'long float'", /* 6 */ "only one storage class allowed", /* 7 */ "illegal storage class", /* 8 */ "only register valid as formal parameter storage class", /* 9 */ "duplicate '%s'", /* 10 */ "bit-field initializer out of range", /* 11 */ "compiler takes size of function", /* 12 */ "incomplete enum type: %s", /* 13 */ "compiler takes alignment of function", /* 14 */ "function returns illegal type", /* 15 */ "array of function is illegal", /* 16 */ "null dimension", /* 17 */ "illegal use of 'void'", /* 18 */ "void type for %s", /* 19 */ - "zero or negative array dimension", /* 20 */ + "negative array dimension (%d)", /* 20 */ "redeclaration of formal parameter %s", /* 21 */ "incomplete or misplaced function definition", /* 22 */ "undefined label %s", /* 23 */ "cannot initialize function: %s", /* 24 */ "cannot initialize typedef: %s", /* 25 */ "cannot initialize extern declaration: %s", /* 26 */ "redeclaration of %s", /* 27 */ "redefinition of %s", /* 28 */ "previously declared extern, becomes static: %s", /* 29 */ "redeclaration of %s; ANSI C requires static", /* 30 */ "incomplete structure or union %s: %s", /* 31 */ "argument type defaults to 'int': %s", /* 32 */ "duplicate member name: %s", /* 33 */ "nonportable bit-field type", /* 34 */ "illegal bit-field type", /* 35 */ "illegal bit-field size", /* 36 */ "zero size bit-field", /* 37 */ "function illegal in structure or union", /* 38 */ - "illegal zero sized structure member: %s", /* 39 */ + "zero sized array in struct is a C99 extension: %s", /* 39 */ "unknown size: %s", /* 40 */ "illegal use of bit-field", /* 41 */ "forward reference to enum type", /* 42 */ "redefinition hides earlier one: %s", /* 43 */ "declaration introduces new type in ANSI C: %s %s", /* 44 */ "base type is really '%s %s'", /* 45 */ "(%s) tag redeclared", /* 46 */ - "zero sized %s", /* 47 */ + "zero sized %s is a C9X feature", /* 47 */ "overflow in enumeration values: %s", /* 48 */ "struct or union member must be named", /* 49 */ "a function is declared as an argument: %s", /* 50 */ "parameter mismatch: %d declared, %d defined", /* 51 */ "cannot initialize parameter: %s", /* 52 */ "declared argument %s is missing", /* 53 */ "trailing ',' prohibited in enum declaration", /* 54 */ "integral constant expression expected", /* 55 */ "integral constant too large", /* 56 */ "enumeration constant hides parameter: %s", /* 57 */ "type does not match prototype: %s", /* 58 */ "formal parameter lacks name: param #%d", /* 59 */ "void must be sole parameter", /* 60 */ "void parameter cannot have name: %s", /* 61 */ "function prototype parameters must have types", /* 62 */ "prototype does not match old-style definition", /* 63 */ "()-less function definition", /* 64 */ "%s has no named members", /* 65 */ "syntax requires ';' after last struct/union member", /* 66 */ "cannot return incomplete type", /* 67 */ "typedef already qualified with '%s'", /* 68 */ "inappropriate qualifiers with 'void'", /* 69 */ "%soperand of '%s' is unsigned in ANSI C", /* 70 */ "too many characters in character constant", /* 71 */ "typedef declares no type name", /* 72 */ "empty character constant", /* 73 */ "no hex digits follow \\x", /* 74 */ "overflow in hex escape", /* 75 */ "character escape does not fit in character", /* 76 */ "bad octal digit %c", /* 77 */ "nonportable character escape", /* 78 */ "dubious escape \\%c", /* 79 */ "dubious escape \\%o", /* 80 */ "\\a undefined in traditional C", /* 81 */ "\\x undefined in traditional C", /* 82 */ "storage class after type is obsolescent", /* 83 */ "ANSI C requires formal parameter before '...'", /* 84 */ "dubious tag declaration: %s %s", /* 85 */ "automatic hides external declaration: %s", /* 86 */ "static hides external declaration: %s", /* 87 */ "typedef hides external declaration: %s", /* 88 */ "typedef redeclared: %s", /* 89 */ "inconsistent redeclaration of extern: %s", /* 90 */ "declaration hides parameter: %s", /* 91 */ "inconsistent redeclaration of static: %s", /* 92 */ "dubious static function at block level: %s", /* 93 */ "function has illegal storage class: %s", /* 94 */ "declaration hides earlier one: %s", /* 95 */ "cannot dereference non-pointer type", /* 96 */ "suffix U is illegal in traditional C", /* 97 */ "suffixes F and L are illegal in traditional C", /* 98 */ "%s undefined", /* 99 */ "unary + is illegal in traditional C", /* 100 */ "undefined struct/union member: %s", /* 101 */ "illegal member use: %s", /* 102 */ "left operand of '.' must be struct/union object", /* 103 */ "left operand of '->' must be pointer to struct/union", /* 104 */ "non-unique member requires struct/union %s", /* 105 */ "left operand of '->' must be pointer", /* 106 */ "operands of '%s' have incompatible types", /* 107 */ "operand of '%s' has incompatible type", /* 108 */ "void type illegal in expression", /* 109 */ "pointer to function is not allowed here", /* 110 */ "unacceptable operand of '%s'", /* 111 */ "cannot take address of bit-field", /* 112 */ "cannot take address of register %s", /* 113 */ "%soperand of '%s' must be lvalue", /* 114 */ "%soperand of '%s' must be modifiable lvalue", /* 115 */ "illegal pointer subtraction", /* 116 */ "bitwise operation on signed value possibly nonportable", /* 117 */ "semantics of '%s' change in ANSI C; use explicit cast", /* 118 */ "conversion of '%s' to '%s' is out of range", /* 119 */ "bitwise operation on signed value nonportable", /* 120 */ "negative shift", /* 121 */ "shift greater than size of object", /* 122 */ "illegal combination of pointer and integer, op %s", /* 123 */ "illegal pointer combination, op %s", /* 124 */ "ANSI C forbids ordered comparisons of pointers to functions",/* 125 */ "incompatible types in conditional", /* 126 */ "'&' before array or function: ignored", /* 127 */ "operands have incompatible pointer types, op %s", /* 128 */ "expression has null effect", /* 129 */ "enum type mismatch, op %s", /* 130 */ "conversion to '%s' may sign-extend incorrectly", /* 131 */ - "conversion from '%s' may lose accuracy", /* 132 */ + "conversion from '%s' to '%s' may lose accuracy", /* 132 */ "conversion of pointer to '%s' loses bits", /* 133 */ "conversion of pointer to '%s' may lose bits", /* 134 */ "possible pointer alignment problem", /* 135 */ "cannot do pointer arithmetic on operand of unknown size", /* 136 */ "use of incomplete enum type, op %s", /* 137 */ "unknown operand size, op %s", /* 138 */ "division by 0", /* 139 */ "modulus by 0", /* 140 */ "integer overflow detected, op %s", /* 141 */ "floating point overflow detected, op %s", /* 142 */ "cannot take size of incomplete type", /* 143 */ "cannot take size of function", /* 144 */ "cannot take size of bit-field", /* 145 */ "cannot take size of void", /* 146 */ "invalid cast expression", /* 147 */ "improper cast of void expression", /* 148 */ "illegal function", /* 149 */ "argument mismatch: %d arg%s passed, %d expected", /* 150 */ "void expressions may not be arguments, arg #%d", /* 151 */ "argument cannot have unknown size, arg #%d", /* 152 */ "argument has incompatible pointer type, arg #%d", /* 153 */ "illegal combination of pointer and integer, arg #%d", /* 154 */ "argument is incompatible with prototype, arg #%d", /* 155 */ "enum type mismatch, arg #%d", /* 156 */ "ANSI C treats constant as unsigned", /* 157 */ "%s may be used before set", /* 158 */ "assignment in conditional context", /* 159 */ "operator '==' found where '=' was expected", /* 160 */ "constant in conditional context", /* 161 */ "comparison of %s with %s, op %s", /* 162 */ "a cast does not yield an lvalue", /* 163 */ "assignment of negative constant to unsigned type", /* 164 */ "constant truncated by assignment", /* 165 */ "precision lost in bit-field assignment", /* 166 */ "array subscript cannot be negative: %ld", /* 167 */ "array subscript cannot be > %d: %ld", /* 168 */ "precedence confusion possible: parenthesize!", /* 169 */ "first operand must have scalar type, op ? :", /* 170 */ "assignment type mismatch", /* 171 */ "too many struct/union initializers", /* 172 */ "too many array initializers", /* 173 */ "too many initializers", /* 174 */ "initialisation of an incomplete type", /* 175 */ - "invalid initializer", /* 176 */ + "invalid initializer type %s", /* 176 */ "non-constant initializer", /* 177 */ "initializer does not fit", /* 178 */ "cannot initialize struct/union with no named member", /* 179 */ "bit-field initializer does not fit", /* 180 */ "{}-enclosed initializer required", /* 181 */ "incompatible pointer types", /* 182 */ "illegal combination of pointer and integer", /* 183 */ "illegal pointer combination", /* 184 */ "initialisation type mismatch", /* 185 */ "bit-field initialisation is illegal in traditional C", /* 186 */ "non-null byte ignored in string initializer", /* 187 */ "no automatic aggregate initialization in traditional C", /* 188 */ "assignment of struct/union illegal in traditional C", /* 189 */ "empty array declaration: %s", /* 190 */ "%s set but not used in function %s", /* 191 */ "%s unused in function %s", /* 192 */ "statement not reached", /* 193 */ "label %s redefined", /* 194 */ "case not in switch", /* 195 */ "case label affected by conversion", /* 196 */ "non-constant case expression", /* 197 */ "non-integral case expression", /* 198 */ "duplicate case in switch: %ld", /* 199 */ "duplicate case in switch: %lu", /* 200 */ "default outside switch", /* 201 */ "duplicate default in switch", /* 202 */ "case label must be of type `int' in traditional C", /* 203 */ "controlling expressions must have scalar type", /* 204 */ "switch expression must have integral type", /* 205 */ "enumeration value(s) not handled in switch", /* 206 */ "loop not entered at top", /* 207 */ "break outside loop or switch", /* 208 */ "continue outside loop", /* 209 */ "enum type mismatch in initialisation", /* 210 */ "return value type mismatch", /* 211 */ "cannot return incomplete type", /* 212 */ "void function %s cannot return value", /* 213 */ "function %s expects to return value", /* 214 */ "function implicitly declared to return int", /* 215 */ "function %s has return (e); and return;", /* 216 */ "function %s falls off bottom without returning value", /* 217 */ "ANSI C treats constant as unsigned, op %s", /* 218 */ "concatenated strings are illegal in traditional C", /* 219 */ "fallthrough on case statement", /* 220 */ "initialisation of unsigned with negative constant", /* 221 */ "conversion of negative constant to unsigned type", /* 222 */ "end-of-loop code not reached", /* 223 */ "cannot recover from previous errors", /* 224 */ "static function called but not defined: %s()", /* 225 */ "static variable %s unused", /* 226 */ "const object %s should have initializer", /* 227 */ "function cannot return const or volatile object", /* 228 */ "questionable conversion of function pointer", /* 229 */ "nonportable character comparison, op %s", /* 230 */ "argument %s unused in function %s", /* 231 */ "label %s unused in function %s", /* 232 */ "struct %s never defined", /* 233 */ "union %s never defined", /* 234 */ "enum %s never defined", /* 235 */ "static function %s unused", /* 236 */ "redeclaration of formal parameter %s", /* 237 */ "initialisation of union is illegal in traditional C", /* 238 */ "constant argument to NOT", /* 239 */ "assignment of different structures", /* 240 */ "dubious operation on enum, op %s", /* 241 */ "combination of '%s' and '%s', op %s", /* 242 */ "dubious comparison of enums, op %s", /* 243 */ "illegal structure pointer combination", /* 244 */ "illegal structure pointer combination, op %s", /* 245 */ "dubious conversion of enum to '%s'", /* 246 */ "pointer casts may be troublesome", /* 247 */ "floating-point constant out of range", /* 248 */ "syntax error", /* 249 */ "unknown character \\%o", /* 250 */ "malformed integer constant", /* 251 */ "integer constant out of range", /* 252 */ "unterminated character constant", /* 253 */ "newline in string or char constant", /* 254 */ "undefined or invalid # directive", /* 255 */ "unterminated comment", /* 256 */ "extra characters in lint comment", /* 257 */ "unterminated string constant", /* 258 */ "conversion to '%s' due to prototype, arg #%d", /* 259 */ "previous declaration of %s", /* 260 */ "previous definition of %s", /* 261 */ "\\\" inside character constants undefined in traditional C", /* 262 */ "\\? undefined in traditional C", /* 263 */ "\\v undefined in traditional C", /* 264 */ "%s C does not support 'long long'", /* 265 */ "'long double' is illegal in traditional C", /* 266 */ "shift equal to size of object", /* 267 */ "variable declared inline: %s", /* 268 */ "argument declared inline: %s", /* 269 */ "function prototypes are illegal in traditional C", /* 270 */ "switch expression must be of type `int' in traditional C", /* 271 */ "empty translation unit", /* 272 */ "bit-field type '%s' invalid in ANSI C", /* 273 */ "ANSI C forbids comparison of %s with %s", /* 274 */ "cast discards 'const' from pointer target type", /* 275 */ "", /* 276 */ "initialisation of '%s' with '%s'", /* 277 */ "combination of '%s' and '%s', arg #%d", /* 278 */ "combination of '%s' and '%s' in return", /* 279 */ "must be outside function: /* %s */", /* 280 */ "duplicate use of /* %s */", /* 281 */ "must precede function definition: /* %s */", /* 282 */ "argument number mismatch with directive: /* %s */", /* 283 */ "fallthrough on default statement", /* 284 */ "prototype declaration", /* 285 */ "function definition is not a prototype", /* 286 */ "function declaration is not a prototype", /* 287 */ "dubious use of /* VARARGS */ with /* %s */", /* 288 */ "can't be used together: /* PRINTFLIKE */ /* SCANFLIKE */", /* 289 */ "static function %s declared but not defined", /* 290 */ "invalid multibyte character", /* 291 */ "cannot concatenate wide and regular string literals", /* 292 */ "argument %d must be 'char *' for PRINTFLIKE/SCANFLIKE", /* 293 */ "multi-character character constant", /* 294 */ "conversion of '%s' to '%s' is out of range, arg #%d", /* 295 */ "conversion of negative constant to unsigned type, arg #%d", /* 296 */ "conversion to '%s' may sign-extend incorrectly, arg #%d", /* 297 */ - "conversion from '%s' may lose accuracy, arg #%d", /* 298 */ + "conversion from '%s' to '%s' may lose accuracy, arg #%d", /* 298 */ "prototype does not match old style definition, arg #%d", /* 299 */ "old style definition", /* 300 */ "array of incomplete type", /* 301 */ "%s returns pointer to automatic object", /* 302 */ "ANSI C forbids conversion of %s to %s", /* 303 */ "ANSI C forbids conversion of %s to %s, arg #%d", /* 304 */ "ANSI C forbids conversion of %s to %s, op %s", /* 305 */ "constant truncated by conversion, op %s", /* 306 */ "static variable %s set but not used", /* 307 */ "", /* 308 */ "extra bits set to 0 in conversion of '%s' to '%s', op %s", /* 309 */ "symbol renaming can't be used on function arguments", /* 310 */ "symbol renaming can't be used on automatic variables", /* 311 */ "%s C does not support // comments", /* 312 */ + "struct or union member name in initializer is a C9X feature",/* 313 */ + "%s is not a structure or a union", /* 314 */ + "GCC style struct or union member name in initializer", /* 315 */ + "__FUNCTION__ is a GCC extension", /* 316 */ + "__func__ is a C9X feature", /* 317 */ + "variable array dimension is a C99/GCC extension", /* 318 */ + "compound literals are a C9X/GCC extension", /* 319 */ + "({ }) is a GCC extension", /* 320 */ + "array initializer with designators is a C9X feature", /* 321 */ + "zero sized array is a C99 extension", /* 322 */ }; /* * print a list of the messages with their ids */ void msglist(void) { - int i; + size_t i; for (i = 0; i < sizeof(msgs) / sizeof(msgs[0]); i++) - printf("%d\t%s\n", i, msgs[i]); + printf("%zu\t%s\n", i, msgs[i]); } /* * If Fflag is not set lbasename() returns a pointer to the last * component of the path, otherwise it returns the argument. */ static const char * lbasename(const char *path) { const char *cp, *cp1, *cp2; if (Fflag) return (path); cp = cp1 = cp2 = path; while (*cp != '\0') { if (*cp++ == '/') { cp2 = cp1; cp1 = cp; } } return (*cp1 == '\0' ? cp2 : cp1); } static void verror( int n, va_list ap) { const char *fn; if (ERR_ISSET(n, &msgset)) return; fn = lbasename(curr_pos.p_file); (void)printf("%s(%d): ", fn, curr_pos.p_line); (void)vprintf(msgs[n], ap); (void)printf(" [%d]\n", n); nerr++; } static void vwarning( int n, va_list ap) { const char *fn; if (ERR_ISSET(n, &msgset)) return; if (nowarn) /* this warning is suppressed by a LINTED comment */ return; fn = lbasename(curr_pos.p_file); (void)printf("%s(%d): warning: ", fn, curr_pos.p_line); (void)vprintf(msgs[n], ap); (void)printf(" [%d]\n", n); if (wflag) nerr++; } void error(int n, ...) { va_list ap; va_start(ap, n); verror(n, ap); va_end(ap); } void -lerror(const char *msg, ...) +lerror(const char *file, int line, const char *msg, ...) { va_list ap; const char *fn; va_start(ap, msg); fn = lbasename(curr_pos.p_file); - (void)fprintf(stderr, "%s(%d): lint error: ", fn, curr_pos.p_line); + (void)fprintf(stderr, "%s(%d): lint error: %s, %d", fn, curr_pos.p_line, + file, line); (void)vfprintf(stderr, msg, ap); (void)fprintf(stderr, "\n"); va_end(ap); exit(1); } void warning(int n, ...) { va_list ap; va_start(ap, n); vwarning(n, ap); va_end(ap); } void message(int n, ...) { va_list ap; const char *fn; if (ERR_ISSET(n, &msgset)) return; va_start(ap, n); fn = lbasename(curr_pos.p_file); (void)printf("%s(%d): ", fn, curr_pos.p_line); (void)vprintf(msgs[n], ap); (void)printf(" [%d]\n", n); va_end(ap); } +/* + * XXX I think the logic is possibly somewhat screwed up here. The + * question is, how do we want to interpret the -s and -S flags going + * forward? We need to answer that and then we can fix this to be + * "right"... [perry, 2 Nov 2002] +*/ +int +c99ism(int n, ...) +{ + va_list ap; + int msg; + + va_start(ap, n); + if (sflag && !(Sflag || gflag)) { + verror(n, ap); + msg = 1; + } else if (!sflag && (Sflag || gflag)) { + msg = 0; + } else { + vwarning(n, ap); + msg = 1; + } + va_end(ap); + + return (msg); +} + int gnuism(int n, ...) { va_list ap; int msg; va_start(ap, n); if (sflag && !gflag) { verror(n, ap); msg = 1; } else if (!sflag && gflag) { msg = 0; } else { vwarning(n, ap); msg = 1; } va_end(ap); return (msg); } diff --git a/usr.bin/xlint/lint1/externs1.h b/usr.bin/xlint/lint1/externs1.h index 765c1efc87d3..cf40a5d9199c 100644 --- a/usr.bin/xlint/lint1/externs1.h +++ b/usr.bin/xlint/lint1/externs1.h @@ -1,285 +1,292 @@ -/* $NetBSD: externs1.h,v 1.13 2002/01/18 21:01:39 thorpej Exp $ */ +/* $NetBSD: externs1.h,v 1.20 2002/11/02 20:09:27 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. + * + * $FreeBSD$ */ /* * main.c */ extern int aflag; extern int bflag; extern int cflag; extern int dflag; extern int eflag; extern int Fflag; extern int gflag; extern int hflag; extern int rflag; extern int sflag; extern int tflag; extern int uflag; extern int vflag; extern int yflag; extern int wflag; extern int zflag; +extern int Sflag; extern void norecover(void); /* * cgram.y */ extern int blklev; extern int mblklev; extern int yydebug; extern int yyerror(char *); extern int yyparse(void); /* * scan.l */ extern pos_t curr_pos; extern pos_t csrc_pos; extern symt_t symtyp; extern FILE *yyin; extern uint64_t qbmasks[], qlmasks[], qumasks[]; extern void initscan(void); extern int sign(int64_t, tspec_t, int); extern int msb(int64_t, tspec_t, int); extern int64_t xsign(int64_t, tspec_t, int); extern void clrwflgs(void); extern sym_t *getsym(sbuf_t *); extern void cleanup(void); extern sym_t *pushdown(sym_t *); +extern sym_t *mktempsym(type_t *); extern void rmsym(sym_t *); extern void rmsyms(sym_t *); extern void inssym(int, sym_t *); extern void freeyyv(void *, int); extern int yylex(void); /* * mem1.c */ extern const char *fnalloc(const char *); extern const char *fnnalloc(const char *, size_t); extern int getfnid(const char *); extern void initmem(void); extern void *getblk(size_t); extern void *getlblk(int, size_t); extern void freeblk(void); extern void freelblk(int); extern void *tgetblk(size_t); extern tnode_t *getnode(void); extern void tfreeblk(void); extern struct mbl *tsave(void); extern void trestor(struct mbl *); /* * err.c */ extern int nerr; extern int sytxerr; extern const char *msgs[]; extern void msglist(void); extern void error(int, ...); extern void warning(int, ...); extern void message(int, ...); extern int gnuism(int, ...); -extern void lerror(const char *, ...) - __attribute__((__noreturn__,__format__(__printf__, 1, 2))); +extern int c99ism(int, ...); +extern void lerror(const char *, int, const char *, ...) + __attribute__((__noreturn__,__format__(__printf__, 3, 4))); /* * decl.c */ extern dinfo_t *dcs; extern const char *unnamed; extern int enumval; extern void initdecl(void); extern type_t *gettyp(tspec_t); extern type_t *duptyp(const type_t *); extern type_t *tduptyp(const type_t *); extern int incompl(type_t *); extern void setcompl(type_t *, int); extern void addscl(scl_t); extern void addtype(type_t *); extern void addqual(tqual_t); extern void pushdecl(scl_t); extern void popdecl(void); extern void setasm(void); extern void clrtyp(void); extern void deftyp(void); extern int length(type_t *, const char *); extern int getbound(type_t *); extern sym_t *lnklst(sym_t *, sym_t *); extern void chktyp(sym_t *); extern sym_t *decl1str(sym_t *); extern sym_t *bitfield(sym_t *, int); extern pqinf_t *mergepq(pqinf_t *, pqinf_t *); extern sym_t *addptr(sym_t *, pqinf_t *); extern sym_t *addarray(sym_t *, int, int); extern sym_t *addfunc(sym_t *, sym_t *); extern void chkfdef(sym_t *, int); extern sym_t *dname(sym_t *); extern sym_t *iname(sym_t *); extern type_t *mktag(sym_t *, tspec_t, int, int); extern const char *scltoa(scl_t); extern type_t *compltag(type_t *, sym_t *); extern sym_t *ename(sym_t *, int, int); extern void decl1ext(sym_t *, int); extern void cpuinfo(sym_t *, sym_t *); extern int isredec(sym_t *, int *); extern int eqtype(type_t *, type_t *, int, int, int *); extern void compltyp(sym_t *, sym_t *); extern sym_t *decl1arg(sym_t *, int); extern void cluparg(void); extern void decl1loc(sym_t *, int); extern sym_t *aname(void); extern void globclup(void); extern sym_t *decl1abs(sym_t *); extern void chksz(sym_t *); extern void setsflg(sym_t *); extern void setuflg(sym_t *, int, int); extern void chkusage(dinfo_t *); extern void chkusg1(int, sym_t *); extern void chkglsyms(void); extern void prevdecl(int, sym_t *); /* * tree.c */ extern void initmtab(void); extern type_t *incref(type_t *, tspec_t); extern type_t *tincref(type_t *, tspec_t); extern tnode_t *getcnode(type_t *, val_t *); extern tnode_t *getnnode(sym_t *, int); extern tnode_t *getsnode(strg_t *); extern sym_t *strmemb(tnode_t *, op_t, sym_t *); extern tnode_t *build(op_t, tnode_t *, tnode_t *); extern tnode_t *cconv(tnode_t *); extern int typeok(op_t, int, tnode_t *, tnode_t *); extern tnode_t *promote(op_t, int, tnode_t *); extern tnode_t *convert(op_t, int, type_t *, tnode_t *); extern void cvtcon(op_t, int, type_t *, val_t *, val_t *); -extern const char *tyname(type_t *); +extern const char *tyname(char *, size_t, type_t *); +extern const char *basictyname(tspec_t); extern tnode_t *bldszof(type_t *); extern tnode_t *cast(tnode_t *, type_t *); extern tnode_t *funcarg(tnode_t *, tnode_t *); extern tnode_t *funccall(tnode_t *, tnode_t *); -extern val_t *constant(tnode_t *); -extern void expr(tnode_t *, int, int); +extern val_t *constant(tnode_t *, int); +extern void expr(tnode_t *, int, int, int); extern void chkmisc(tnode_t *, int, int, int, int, int, int); extern int conaddr(tnode_t *, sym_t **, ptrdiff_t *); extern strg_t *catstrg(strg_t *, strg_t *); /* * func.c */ extern sym_t *funcsym; extern int reached; extern int rchflg; extern int ftflg; extern int nargusg; extern pos_t aupos; extern int nvararg; extern pos_t vapos; extern int prflstrg; extern pos_t prflpos; extern int scflstrg; extern pos_t scflpos; extern int ccflg; extern int llibflg; extern int nowarn; extern int bitfieldtype_ok; extern int plibflg; extern int quadflg; extern void pushctrl(int); extern void popctrl(int); extern void chkreach(void); extern void funcdef(sym_t *); extern void funcend(void); extern void label(int, sym_t *, tnode_t *); extern void if1(tnode_t *); extern void if2(void); extern void if3(int); extern void switch1(tnode_t *); extern void switch2(void); extern void while1(tnode_t *); extern void while2(void); extern void do1(void); extern void do2(tnode_t *); extern void for1(tnode_t *, tnode_t *, tnode_t *); extern void for2(void); extern void dogoto(sym_t *); extern void docont(void); extern void dobreak(void); extern void doreturn(tnode_t *); extern void glclup(int); extern void argsused(int); extern void constcond(int); extern void fallthru(int); extern void notreach(int); extern void lintlib(int); extern void linted(int); extern void varargs(int); extern void printflike(int); extern void scanflike(int); extern void protolib(int); extern void longlong(int); extern void bitfieldtype(int); /* * init.c */ extern int initerr; extern sym_t *initsym; extern int startinit; extern void prepinit(void); extern void initrbr(void); extern void initlbr(void); extern void mkinit(tnode_t *); +extern void memberpush(sbuf_t *); /* * emit.c */ extern void outtype(type_t *); extern const char *ttos(type_t *); extern void outsym(sym_t *, scl_t, def_t); extern void outfdef(sym_t *, pos_t *, int, int, sym_t *); extern void outcall(tnode_t *, int, int); extern void outusg(sym_t *); diff --git a/usr.bin/xlint/lint1/func.c b/usr.bin/xlint/lint1/func.c index f34baa1ff2e7..118aa6812e17 100644 --- a/usr.bin/xlint/lint1/func.c +++ b/usr.bin/xlint/lint1/func.c @@ -1,1288 +1,1288 @@ -/* $NetBSD: func.c,v 1.16 2002/01/03 04:25:15 thorpej Exp $ */ +/* $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() 1"); + 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() 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() 2"); + LERROR("funcdef()"); /* formal parameter lacks name: param #%d */ error(59, n); } else { if (arg->s_name == unnamed) - lerror("funcdef() 3"); + 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() 1"); + 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() 1"); + 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); + 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); + 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); + 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() 1"); + 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() 2"); + 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 * 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); + 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); + 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); + 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); + 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); + 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); + 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; } diff --git a/usr.bin/xlint/lint1/init.c b/usr.bin/xlint/lint1/init.c index cf4cbddfa98f..9a10b3357915 100644 --- a/usr.bin/xlint/lint1/init.c +++ b/usr.bin/xlint/lint1/init.c @@ -1,515 +1,656 @@ -/* $NetBSD: init.c,v 1.9 2001/09/18 18:15:54 wiz Exp $ */ +/* $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.9 2001/09/18 18:15:54 wiz Exp $"); +__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() 1"); + LERROR("popi2()"); free(istk); istk = initstk; istk->i_cnt--; if (istk->i_cnt < 0) - lerror("popi2() 3"); + 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) { + 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() 2"); + 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() 1"); + LERROR("pushinit()"); istk->i_cnt = 1; if (istk->i_type->t_tspec != ARRAY) - lerror("pushinit() 2"); + 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() 3"); + LERROR("pushinit()"); if (istk->i_type != NULL && issclt(istk->i_type->t_tspec)) - lerror("pushinit() 4"); + 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() 5"); + 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) { + 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 aggregat initialisation. If the + * 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); + 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() 1"); + LERROR("mkinit()"); if (!typeok(INIT, 0, ln, tn)) goto end; /* * Store the tree memory. This is nessesary because otherwise * expr() would free it. */ tmem = tsave(); - expr(tn, 1, 0); + 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: - tfreeblk(); + /* + * 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->t_tspec == ARRAY) { + 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); } diff --git a/usr.bin/xlint/lint1/lint1.h b/usr.bin/xlint/lint1/lint1.h index 6cfcbb747d2e..1eb1cc2fcdf0 100644 --- a/usr.bin/xlint/lint1/lint1.h +++ b/usr.bin/xlint/lint1/lint1.h @@ -1,416 +1,424 @@ -/* $NetBSD: lint1.h,v 1.12 2002/01/31 19:33:27 tv Exp $ */ +/* $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 to structures are identical by comparing the pointers to + * 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; /* protoype with ... */ + 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 implizit decl. */ + 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_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; diff --git a/usr.bin/xlint/lint1/main1.c b/usr.bin/xlint/lint1/main1.c index 29c872f4ef88..59f6c7f99b40 100644 --- a/usr.bin/xlint/lint1/main1.c +++ b/usr.bin/xlint/lint1/main1.c @@ -1,222 +1,230 @@ -/* $NetBSD: main1.c,v 1.11 2002/01/29 02:43:38 tv Exp $ */ +/* $NetBSD: main1.c,v 1.17 2006/11/08 18:31:15 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: main1.c,v 1.11 2002/01/29 02:43:38 tv Exp $"); +__RCSID("$NetBSD: main1.c,v 1.17 2006/11/08 18:31:15 christos Exp $"); #endif __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include "lint1.h" /* set yydebug to 1*/ int yflag; /* * Print warnings if an assignment of an integertype to another integertype - * causes an implizit narrowing conversion. If aflag is 1, these warnings + * causes an implicit narrowing conversion. If aflag is 1, these warnings * are printed only if the source type is at least as wide as long. If aflag * is greater than 1, they are always printed. */ int aflag; /* Print a warning if a break statement cannot be reached. */ int bflag; /* Print warnings for pointer casts. */ int cflag; /* Print various debug information. */ int dflag; /* Perform stricter checking of enum types and operations on enum types. */ int eflag; /* Print complete pathnames, not only the basename. */ int Fflag; /* Enable some extensions of gcc */ int gflag; /* Treat warnings as errors */ int wflag; /* * Apply a number of heuristic tests to attempt to intuit bugs, improve * style, and reduce waste. */ int hflag; /* Attempt to check portability to other dialects of C. */ int pflag; /* * In case of redeclarations/redefinitions print the location of the * previous declaration/definition. */ int rflag; /* Strict ANSI C mode. */ int sflag; /* Traditional C mode. */ int tflag; +/* Enable C9X extensions */ +int Sflag; /* * Complain about functions and external variables used and not defined, * or defined and not used. */ int uflag = 1; /* Complain about unused function arguments. */ int vflag = 1; /* Complain about structures which are never defined. */ int zflag = 1; err_set msgset; static void usage(void); int main(int, char *[]); int main(int argc, char *argv[]) { int c; char *ptr; ERR_ZERO(&msgset); - while ((c = getopt(argc, argv, "abcdeghmprstuvwyzFX:")) != -1) { + while ((c = getopt(argc, argv, "abcdeghmprstuvwyzFSX:")) != -1) { switch (c) { case 'a': aflag++; break; case 'b': bflag = 1; break; case 'c': cflag = 1; break; case 'd': dflag = 1; break; case 'e': eflag = 1; break; case 'F': Fflag = 1; break; case 'g': gflag = 1; break; case 'h': hflag = 1; break; case 'p': pflag = 1; break; case 'r': rflag = 1; break; case 's': sflag = 1; break; + case 'S': Sflag = 1; break; case 't': tflag = 1; break; case 'u': uflag = 0; break; case 'w': wflag = 1; break; case 'v': vflag = 0; break; case 'y': yflag = 1; break; case 'z': zflag = 0; break; case 'm': msglist(); return(0); case 'X': for (ptr = strtok(optarg, ","); ptr; ptr = strtok(NULL, ",")) { char *eptr; - long msg = strtol(ptr, &eptr, 0); + long msg; + + errno = 0; + msg = strtol(ptr, &eptr, 0); if ((msg == LONG_MIN || msg == LONG_MAX) && errno == ERANGE) err(1, "invalid error message id '%s'", ptr); if (*eptr || ptr == eptr || msg < 0 || msg >= ERR_SETSIZE) errx(1, "invalid error message id '%s'", ptr); ERR_SET(msg, &msgset); } break; case '?': default: usage(); break; } } argc -= optind; argv += optind; if (argc != 2) usage(); /* open the input file */ if ((yyin = fopen(argv[0], "r")) == NULL) err(1, "cannot open '%s'", argv[0]); /* initialize output */ outopen(argv[1]); if (yflag) yydebug = 1; initmem(); initdecl(); initscan(); initmtab(); yyparse(); /* Following warnings cannot be suppressed by LINTED */ nowarn = 0; - +#ifdef DEBUG + printf("%s, %d: nowarn = 0\n", curr_pos.p_file, curr_pos.p_line); +#endif chkglsyms(); outclose(); return (nerr != 0); } static void usage(void) { (void)fprintf(stderr, - "usage: lint1 [-abcdeghmprstuvwyzF] [-X [,]... src dest\n"); + "usage: lint1 [-abcdeghmprstuvwyzFS] [-X [,]... src dest\n"); exit(1); } void norecover(void) { /* cannot recover from previous errors */ error(224); exit(1); } diff --git a/usr.bin/xlint/lint1/makeman b/usr.bin/xlint/lint1/makeman index 7aa060cb3faf..922fe5a13ea0 100644 --- a/usr.bin/xlint/lint1/makeman +++ b/usr.bin/xlint/lint1/makeman @@ -1,80 +1,80 @@ #!/bin/sh # $NetBSD$ # # Copyright (c) 2000 The NetBSD Foundation, Inc. # All rights reserved. # # This code is derived from software contributed to The NetBSD Foundation # by Christos Zoulas. # # 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. # # THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS # ``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 FOUNDATION OR CONTRIBUTORS # 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. cat << \__EOF .\" $NetBSD$ .\" .\" Copyright (c) 2000 The NetBSD Foundation, Inc. .\" All rights reserved. .\" .\" This code is derived from software contributed to The NetBSD Foundation .\" by Christos Zoulas. .\" .\" 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. .\" .\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS .\" ``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 FOUNDATION OR CONTRIBUTORS .\" 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$ .\" .Dd July 5, 2000 .Dt LINT 7 .Os .Sh NAME .Nm lint .Nd Lint error message list .Sh DESCRIPTION The following is a list of message IDs and messages produced by .Xr lint 1 . It is intended to be used with .Fl X flag of .Xr lint 1 . -.Bl -column -offset indent "Id#" "Message" +.Bl -column -offset indent "XXXX" __EOF "$@" | sed -e 's/\\/\\e/g' -e "s/'/\\'/" echo ".El" diff --git a/usr.bin/xlint/lint1/scan.l b/usr.bin/xlint/lint1/scan.l index 013a79520b00..5b715177a5f7 100644 --- a/usr.bin/xlint/lint1/scan.l +++ b/usr.bin/xlint/lint1/scan.l @@ -1,1476 +1,1531 @@ %{ -/* $NetBSD: scan.l,v 1.26 2002/01/31 22:30:21 tv Exp $ */ +/* $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.26 2002/01/31 22:30:21 tv Exp $"); +__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()); +{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_stdc : 1; /* STDC keyword */ - u_int kw_gcc : 1; /* GCC keyword */ + 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, 1 }, - { "__asm", T_ASM, 0, 0, 0, 0, 0 }, - { "__asm__", T_ASM, 0, 0, 0, 0, 0 }, - { "auto", T_SCLASS, AUTO, 0, 0, 0, 0 }, - { "break", T_BREAK, 0, 0, 0, 0, 0 }, - { "case", T_CASE, 0, 0, 0, 0, 0 }, - { "char", T_TYPE, 0, CHAR, 0, 0, 0 }, - { "const", T_QUAL, 0, 0, CONST, 1, 0 }, - { "__const__", T_QUAL, 0, 0, CONST, 0, 0 }, - { "__const", T_QUAL, 0, 0, CONST, 0, 0 }, - { "continue", T_CONTINUE, 0, 0, 0, 0, 0 }, - { "default", T_DEFAULT, 0, 0, 0, 0, 0 }, - { "do", T_DO, 0, 0, 0, 0, 0 }, - { "double", T_TYPE, 0, DOUBLE, 0, 0, 0 }, - { "else", T_ELSE, 0, 0, 0, 0, 0 }, - { "enum", T_ENUM, 0, 0, 0, 0, 0 }, - { "extern", T_SCLASS, EXTERN, 0, 0, 0, 0 }, - { "float", T_TYPE, 0, FLOAT, 0, 0, 0 }, - { "for", T_FOR, 0, 0, 0, 0, 0 }, - { "goto", T_GOTO, 0, 0, 0, 0, 0 }, - { "if", T_IF, 0, 0, 0, 0, 0 }, - { "inline", T_SCLASS, INLINE, 0, 0, 0, 1 }, - { "__inline__", T_SCLASS, INLINE, 0, 0, 0, 0 }, - { "__inline", T_SCLASS, INLINE, 0, 0, 0, 0 }, - { "int", T_TYPE, 0, INT, 0, 0, 0 }, - { "__symbolrename", T_SYMBOLRENAME, 0, 0, 0, 0, 0 }, - { "long", T_TYPE, 0, LONG, 0, 0, 0 }, - { "register", T_SCLASS, REG, 0, 0, 0, 0 }, - { "return", T_RETURN, 0, 0, 0, 0, 0 }, - { "short", T_TYPE, 0, SHORT, 0, 0, 0 }, - { "signed", T_TYPE, 0, SIGNED, 0, 1, 0 }, - { "__signed__", T_TYPE, 0, SIGNED, 0, 0, 0 }, - { "__signed", T_TYPE, 0, SIGNED, 0, 0, 0 }, - { "sizeof", T_SIZEOF, 0, 0, 0, 0, 0 }, - { "static", T_SCLASS, STATIC, 0, 0, 0, 0 }, - { "struct", T_SOU, 0, STRUCT, 0, 0, 0 }, - { "switch", T_SWITCH, 0, 0, 0, 0, 0 }, - { "typedef", T_SCLASS, TYPEDEF, 0, 0, 0, 0 }, - { "union", T_SOU, 0, UNION, 0, 0, 0 }, - { "unsigned", T_TYPE, 0, UNSIGN, 0, 0, 0 }, - { "void", T_TYPE, 0, VOID, 0, 0, 0 }, - { "volatile", T_QUAL, 0, 0, VOLATILE, 1, 0 }, - { "__volatile__", T_QUAL, 0, 0, VOLATILE, 0, 0 }, - { "__volatile", T_QUAL, 0, 0, VOLATILE, 0, 0 }, - { "while", T_WHILE, 0, 0, 0, 0, 0 }, - { NULL, 0, 0, 0, 0, 0, 0 } + { "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 */ 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_stdc && tflag) + 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() 1"); + 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() 1"); + 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 * 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) - lerror("fcon() 1"); + 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 < 2 && !gflag) + 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() 1"); + 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() 2"); + 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()"); + 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()"); + 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() 1"); + LERROR("fryylv()"); } free(strg); } } diff --git a/usr.bin/xlint/lint1/tree.c b/usr.bin/xlint/lint1/tree.c index 12417bad3c2b..4989322cb49b 100644 --- a/usr.bin/xlint/lint1/tree.c +++ b/usr.bin/xlint/lint1/tree.c @@ -1,3929 +1,4040 @@ -/* $NetBSD: tree.c,v 1.24 2002/01/31 22:30:20 tv Exp $ */ +/* $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.24 2002/01/31 22:30:20 tv Exp $"); +__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 { - /* %s undefined */ - error(99, sym->s_name); + 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() 1"); + 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. */ 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. */ 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() 1"); + 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 */ /* 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 * 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 ((lt = (ltp = ln->tn_type)->t_tspec) == PTR) + 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 ((rt = (rtp = rn->tn_type)->t_tspec) == PTR) + 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 */ 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. */ if (lt == FUNC || lt == VOID || ltp->t_isfield || ((lt != STRUCT && lt != UNION) && !ln->tn_lvalue)) { /* Without tflag we got already 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 */ 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 */ error(114, "", mp->m_name); return (0); } else if (ltp->t_const) { /* %soperand 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() 2"); + 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->t_isenum)) { + } 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->t_isenum)) { + } 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(ln->tn_type), tyname(rn->tn_type)); + 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(ln->tn_type), tyname(rn->tn_type), arg); + 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(ln->tn_type), tyname(rn->tn_type)); + 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(ln->tn_type), tyname(rn->tn_type), - modtab[op].m_name); + 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() 2"); + 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() 1"); + 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. */ 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() 1"); + 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 (styp(nt) != SHORT || !isityp(ot) || psize(ot) > psize(INT)) + if (!isityp(ot) || psize(ot) > psize(INT)) { /* conversion to '%s' due to prototype, arg #%d */ - warning(259, tyname(tp), arg); + 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(tp), arg); + warning(259, tyname(buf, sizeof(buf), tp), arg); } } } /* * Print warnings for conversions of integer types which my 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(tp), arg); + warning(297, tyname(lbuf, sizeof(lbuf), tp), + arg); } else { - warning(131, tyname(tp)); + 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(tn->tn_type), arg); + warning(298, + tyname(rbuf, sizeof(rbuf), tn->tn_type), + tyname(lbuf, sizeof(lbuf), tp), + arg); } else { - warning(132, tyname(tn->tn_type)); + 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. */ return; } if (psize(nt) < psize(PTR)) { if (pflag && size(nt) >= size(PTR)) { /* conv. of pointer to %s may lose bits */ - warning(134, tyname(tp)); + warning(134, tyname(buf, sizeof(buf), tp)); } else { /* conv. of pointer to %s loses bits */ - warning(133, tyname(tp)); + 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 * 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 * 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 */ case LDOUBLE: max = LDBL_MAX; min = -LDBL_MAX; break; default: - lerror("cvtcon() 1"); + LERROR("cvtcon()"); } if (v->v_ldbl > max || v->v_ldbl < min) { if (nt == LDOUBLE) - lerror("cvtcon() 2"); + LERROR("cvtcon()"); if (op == FARG) { /* conv. of %s to %s is out of rng., arg #%d */ - warning(295, tyname(gettyp(ot)), tyname(tp), - arg); + 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(gettyp(ot)), tyname(tp)); + 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(gettyp(ot)), - tyname(tp), modtab[op].m_name); + 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(gettyp(ot)), tyname(tp), - arg); + 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(gettyp(ot)), tyname(tp)); + 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(gettyp(ot)), tyname(tp), - arg); + 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(gettyp(ot)), tyname(tp)); + 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() 1"); + 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()"); + 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()"); + 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 * -tyname(type_t *tp) +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: s = "char"; break; - case UCHAR: s = "unsigned char"; break; - case SCHAR: s = "signed char"; break; - case SHORT: s = "short"; break; - case USHORT: s = "unsigned short"; break; - case INT: s = "int"; break; - case UINT: s = "unsigned int"; break; - case LONG: s = "long"; break; - case ULONG: s = "unsigned long"; break; - case QUAD: s = "long long"; break; - case UQUAD: s = "unsigned long long"; break; - case FLOAT: s = "float"; break; - case DOUBLE: s = "double"; break; - case LDOUBLE: s = "long double"; break; - case PTR: s = "pointer"; break; - case ENUM: s = "enum"; break; - case STRUCT: s = "struct"; break; - case UNION: s = "union"; break; - case FUNC: s = "function"; break; - case ARRAY: s = "array"; break; + 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()"); + LERROR("tyname()"); } - return (s); + 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() 1"); + LERROR("bldstr()"); if (rn->tn_sym->s_value.v_tspec != INT) - lerror("bldstr() 2"); + LERROR("bldstr()"); if (rn->tn_sym->s_scl != MOS && rn->tn_sym->s_scl != MOU) - lerror("bldstr() 3"); + 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() 4"); + 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() 1"); + 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() 1"); + 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() 2"); + 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() 1"); + LERROR("bldcol()"); if (ln->tn_type->t_str != rn->tn_type->t_str) - lerror("bldcol() 2"); + 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() 4"); + 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() 5"); + LERROR("bldcol()"); rtp = rn->tn_type; mrgqual(&rtp, ln->tn_type, rn->tn_type); } else { if (lt != PTR || rt != PTR) - lerror("bldcol() 6"); + LERROR("bldcol()"); /* * XXX For now we simply take the left type. This is * probably wrong, if one type contains a functionprototype * 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() 1"); + 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() 2"); + 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() 3"); + 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 || op == SHRASS) { - if (rt != INT) { - rn = convert(NOOP, 0, gettyp(INT), rn); - rt = INT; + if (op == SHLASS) { + if (psize(lt) < psize(rt)) { + if (hflag) + /* semantics of %s change in ANSI C; use ... */ + warning(118, "<<="); } - } else { + } 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() 1"); + 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() 2"); + 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() 5"); + 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() 1"); + 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() 1"); + 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() 1"); + LERROR("foldflt()"); if (t != tn->tn_left->tn_type->t_tspec) - lerror("foldflt() 2"); + LERROR("foldflt()"); if (modtab[tn->tn_op].m_binary && t != tn->tn_right->tn_type->t_tspec) - lerror("foldflt() 3"); + 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() 4"); + LERROR("foldflt()"); } if (isnan((double)v->v_ldbl)) - lerror("foldflt() 5"); + 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() 1"); + 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) +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() 1"); + 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() 2"); + 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 */ - error(55); + 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) +expr(tnode_t *tn, int vctx, int tctx, int freeblk) { if (tn == NULL && nerr == 0) - lerror("expr() 1"); + 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 */ - tfreeblk(); + 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() 1"); + 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() 1"); + 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 * 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(ln->tn_type), "negative constant", - mp->m_name); + 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(ln->tn_type), "0", mp->m_name); + 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(rn->tn_type), - mp->m_name); + 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(rn->tn_type), mp->m_name); + 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 * for static initialisation, otherwise -1. * - * Constant initialisation expressions must be costant or an address + * 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 != CON) + 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)) { + if ((!isityp(t) && t != PTR) || (!isityp(ot) && ot != PTR)) return (-1); - } else if (psize(t) != psize(ot)) { + 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); } } diff --git a/usr.bin/xlint/lint2/read.c b/usr.bin/xlint/lint2/read.c index 0ffd611a3a6b..c6e68477ea89 100644 --- a/usr.bin/xlint/lint2/read.c +++ b/usr.bin/xlint/lint2/read.c @@ -1,1242 +1,1245 @@ -/* $NetBSD: read.c,v 1.12 2002/01/21 19:49:52 tv Exp $ */ +/* $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.12 2002/01/21 19:49:52 tv Exp $"); +__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; -static void inperr(void); +#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; } 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 -inperr(void) +inperror(const char *file, size_t line) { - errx(1, "input file error: %s", fnames[srcfile]); + 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 ((c = *cp) == 't' || c == 'd' || c == 'e' || c == 'u' || - c == 'r' || c == 'o' || c == 's' || c == 'v' || - c == 'P' || c == 'S') { + 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; + 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; - tp->t_subt = TP(inptype(cp, &cp)); + sidx = inptype(cp, &cp); /* force seq. point! (ditto below) */ + tp->t_subt = TP(sidx); break; case PTR: - tp->t_subt = TP(inptype(cp, &cp)); + 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 { - tp->t_args[i] = TP(inptype(cp, &cp)); + sidx = inptype(cp, &cp); + tp->t_args[i] = TP(sidx); } } } - tp->t_subt = TP(inptype(cp, &cp)); + 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); } diff --git a/usr.bin/xlint/xlint/lint.1 b/usr.bin/xlint/xlint/lint.1 index 13ffce10dbc8..30731d2df3a3 100644 --- a/usr.bin/xlint/xlint/lint.1 +++ b/usr.bin/xlint/xlint/lint.1 @@ -1,617 +1,627 @@ -.\" $NetBSD: lint.1,v 1.21 2002/01/03 04:25:18 thorpej Exp $ +.\" $NetBSD: lint.1,v 1.29 2004/01/26 21:59:42 wiz 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. .\" .\" $FreeBSD$ .\" -.Dd May 24, 2001 +.Dd Mar 23, 2015 .Dt LINT 1 .Os .Sh NAME .Nm lint .Nd a C program verifier .Sh SYNOPSIS .Bk -words .Nm .Op Fl abceghprvwxzHFV .Op Fl s | t .Op Fl i | nu .Op Fl D Ar name Ns Op = Ns Ar def .Op Fl U Ar name .Op Fl I Ar directory .Op Fl d Ar directory .Op Fl L Ar directory +.Op Fl MD .Op Fl l Ar library .Op Fl o Ar outputfile .Op Fl B Ar directory .Op Fl X Ar id Ns Op , Ns Ar id ... .Ar .Nm .Op Fl abceghprvwzHFV .Op Fl s | t .Fl C Ar library .Op Fl D Ar name Ns Op = Ns Ar def .Op Fl U Ar name .Op Fl I Ar directory .Op Fl d Ar directory .Op Fl B Ar directory .Op Fl X Ar id Ns Op , Ns Ar id ... .Ar .Ek .Sh DESCRIPTION The .Nm utility attempts to detect features of the named C program files that are likely to be bugs, to be non-portable, or to be wasteful. It also performs stricter type checking than does the C compiler. The .Nm utility runs the C preprocessor as its first phase, with the preprocessor symbol .Dq Dv lint defined to allow certain questionable code to be altered or skipped by .Nm . Therefore, this symbol should be thought of as a reserved word for all code that is to be checked by .Nm . .Pp Among the possible problems that are currently noted are unreachable statements, loops not entered at the top, variables declared and not used, and logical expressions with constant values. Function calls are checked for inconsistencies, such as calls to functions that return values in some places and not in others, functions called with varying numbers of arguments, function calls that pass arguments of a type other than the type the function expects to receive, functions whose values are not used, and calls to functions not returning values that use the non-existent return value of the function. .Pp Filename arguments ending with .Pa .c are taken to be C source files. Filename arguments with names ending with .Pa .ln are taken to be the result of an earlier invocation of .Nm , with either the .Fl i , o , or .Fl C option in effect. The .Pa .ln files are analogous to the .Pa .o (object) files produced by .Xr cc 1 from .Pa .c files. The .Nm utility also accepts special libraries specified with the .Fl l option, which contain definitions of library routines and variables. .Pp The .Nm utility takes all the .Pa .c , .ln , and .Pa llib-l Ns Ar library Ns Pa .ln (lint library) files and processes them in command-line order. By default, .Nm appends the standard C lint library .Pq Pa llib-lc.ln to the end of the list of files. When the .Fl i option is used, the .Pa .ln files are ignored. Also, when the .Fl o or .Fl i options are used, the .Pa llib-l Ns Ar library Ns Pa .ln files are ignored. When the .Fl i option is .Em omitted the second pass of .Nm checks this list of files for mutual compatibility. At this point, if a complaint stems not from a given source file, but from one of its included files, the source filename will be printed followed by a question mark. .Pp The special input file name .Dq Pa - causes .Nm to take input from standard input (until end of file) and process it as if it were a .Pa .c file. If the .Fl i flag is given and .Dq Pa - is named as one of the input files, the .Fl o flag must also be specified to provide an output file name. The options are as follows: .Bl -tag -width indent .It Fl a Report assignments of .Vt long values to variables that are not .Vt long . .It Fl aa Additional to .Fl a , report .Em all assignments of integer values to other integer values which cause implicit narrowing conversion. .It Fl b Report .Ic break statements that cannot be reached. This is not the default because, unfortunately, most .Xr lex 1 and many .Xr yacc 1 outputs produce many such complaints. .It Fl c Complain about casts which have questionable portability. .It Fl e Complain about unusual operations on .Vt enum Ns -Types and combinations of .Vt enum Ns - and .Sy integer Ns -Types . .It Fl g Do not print warnings for some extensions of .Xr gcc 1 to the C language. Currently these are nonconstant initializers in automatic aggregate initializations, arithmetic on pointer to void, trailing commas in .Vt enum declarations, C++ -style .Dq Li // comments, zero sized structures, subscripting of non-lvalue arrays, prototypes overriding old style function declarations and long long integer types. The .Fl g flag also turns on the keywords .Ic asm and .Ic inline (alternative keywords with leading underscores for both .Ic asm and .Ic inline are always available). .It Fl h Apply a number of heuristic tests to attempt to intuit bugs, improve style, and reduce waste. .It Fl i Produce a .Pa .ln file for every .Pa .c file on the command line. These .Pa .ln files are the product of .Nm Ns 's first pass only, and are not checked for compatibility between functions. .It Fl n Do not check compatibility against the standard library. .It Fl p Attempt to check portability of code to other dialects of C. .It Fl r In case of redeclarations report the position of the previous declaration. .It Fl s Strict ANSI C mode. Issue warnings and errors required by ANSI C. Also do not produce warnings for constructs which behave differently in traditional C and ANSI C. With the .Fl s flag, .Dv __STRICT_ANSI__ is a predefined preprocessor macro. +.It Fl S +C9X mode. Currently not fully implemented. .It Fl t Traditional C mode. .Dv __STDC__ is not predefined in this mode. Warnings are printed for constructs not allowed in traditional C. Warnings for constructs which behave differently in traditional C and ANSI C are suppressed. Preprocessor macros describing the machine type (e.g., .Dv sun3 ) and machine architecture (e.g., .Dv m68k ) are defined without leading and trailing underscores. The keywords .Ic const , volatile and .Ic signed are not available in traditional C mode (although the alternative keywords with leading underscores still are). .It Fl u Do not complain about functions and external variables used and not defined, or defined and not used (this is suitable for running .Nm on a subset of files comprising part of a larger program). .It Fl v Suppress complaints about unused arguments in functions. .It Fl x Report variables referred to by .Ic extern declarations, but never used. .It Fl z Do not complain about structures that are never defined (for example, using a structure pointer without knowing its contents). .It Fl B Ar path Path to use when looking for the .Pa lint1 and .Pa lint2 binaries. Defaults to .Pa /usr/libexec . .It Fl C Ar library Create a .Nm library with the name .Pa llib-l Ns Ar library Ns Pa .ln . This library is built from all .Pa .c and .Pa .ln input files. After all global definitions of functions and variables in these files are written to the newly created library, .Nm checks all input files, including libraries specified with the .Fl l option, for mutual compatibility. .It Fl D Ar name Ns Op = Ns Ar def Define .Ar name for .Xr cpp 1 , as if by a .Ic #define directive. If no definition is given, .Ar name is defined as 1. .It Fl I Ar directory Add .Ar directory to the list of directories in which to search for include files. .It Fl d Ar directory Use .Ar directory instead of .Pa /usr/include as the default place to find include files. .It Fl l Ar library Include the lint library .Pa llib-l Ns Ar library Ns Pa .ln . .It Fl L Ar directory Search for lint libraries in .Ar directory and .Ar directory Ns Pa /lint before searching the standard place. .It Fl F Print pathnames of files. The .Nm utility normally prints the filename without the path. .It Fl H If a complaint stems from an included file .Nm prints the name of the included file instead of the source file name followed by a question mark. +.It Fl MD +Pass +.Fl MD +to +.Xr cpp 1 +causing cpp to create files containing dependency information for +each source file. .It Fl o Ar outputfile Name the output file .Ar outputfile . The output file produced is the input that is given to .Nm Ns 's second pass. The .Fl o option simply saves this file in the named output file. If the .Fl i option is also used the files are not checked for compatibility. To produce a .Pa llib-l Ns Ar library Ns Pa .ln without extraneous messages, use of the .Fl u option is suggested. The .Fl v option is useful if the source file(s) for the lint library are just external interfaces. .It Fl U Ar name Remove any initial definition of .Ar name for the preprocessor. .It Fl V Print the command lines constructed by the controller program to run the C preprocessor and .Nm Ns 's first and second pass. .It Fl w Treat warnings as errors. .It Fl X Ar id Ns Op , Ns Ar id ... Suppress error messages identified by the list of ids. A list of messages and ids can be found in .Xr lint 7 . .El .Ss Input Grammar .Nm Ns 's first pass reads standard C source files. The .Nm utility recognizes the following C comments as commands. .Bl -tag -width indent .It Li /* ARGSUSED Ns Ar n Li */ -makes +Makes .Nm check only the first .Ar n arguments for usage; a missing .Ar n is taken to be 0 (this option acts like the .Fl v option for the next function). .It Li /* BITFIELDTYPE */ Suppress error messages about illegal bitfield types if the type is an integer type, and suppress non-portable bitfield type warnings. .It Xo .Li /* CONSTCOND */ or .Li /* CONSTANTCOND */ or .Li /* CONSTANTCONDITION */ .Xc suppress complaints about constant operands for the next expression. .It Xo .Li /* FALLTHRU */ or .Li /* FALLTHROUGH */ .Xc suppress complaints about fall through to a .Ic case or .Ic default labelled statement. This directive should be placed immediately preceding the label. .It Li /* LINTLIBRARY */ At the beginning of a file, mark all functions and variables defined in this file as .Em used . Also shut off complaints about unused function arguments. .It Xo .Li /* LINTED Oo Ar comment Oc Li */ or .Li /* NOSTRICT Oo Ar comment Oc Li */ .Xc Suppresses any intra-file warning except those dealing with unused variables or functions. This directive should be placed on the line immediately preceding where the .Nm warning occurred. .It Li /* LONGLONG */ Suppress complaints about use of long long integer types. .It Li /* NOTREACHED */ At appropriate points, inhibit complaints about unreachable code. (This comment is typically placed just after calls to functions like .Xr exit 3 ) . .It Li /* PRINTFLIKE Ns Ar n Li */ makes .Nm check the first .Pq Ar n Ns -1 arguments as usual. The .Ar n Ns -th argument is interpreted as a .Xr printf 3 format string that is used to check the remaining arguments. .It Li /* PROTOLIB Ns Ar n Li */ causes .Nm to treat function declaration prototypes as function definitions if .Ar n is non-zero. This directive can only be used in conjunction with the .Li /* LINTLIBRARY */ directive. If .Ar n is zero, function prototypes will be treated normally. .It Li /* SCANFLIKE Ns Ar n Li */ makes .Nm check the first .Pq Ar n Ns -1 arguments as usual. The .Ar n Ns -th argument is interpreted as a .Xr scanf 3 format string that is used to check the remaining arguments. .It Li /* VARARGS Ns Ar n Li */ Suppress the usual checking for variable numbers of arguments in the following function declaration. The data types of the first .Ar n arguments are checked; a missing .Ar n is taken to be 0. .El .Pp The behavior of the .Fl i and the .Fl o options allows for incremental use of .Nm on a set of C source files. Generally, one invokes .Nm once for each source file with the .Fl i option. Each of these invocations produces a .Pa .ln file that corresponds to the .Pa .c file, and prints all messages that are about just that source file. After all the source files have been separately run through .Nm , it is invoked once more (without the .Fl i option), listing all the .Pa .ln files with the needed .Fl l Ar library options. This will print all the inter-file inconsistencies. This scheme works well with .Xr make 1 ; it allows .Xr make 1 to be used to .Nm only the source files that have been modified since the last time the set of source files were .Nm Ns ed . .Sh ENVIRONMENT .Bl -tag -width LIBDIR .It Ev LIBDIR the directory where the lint libraries specified by the .Bk -words .Fl l Ar library .Ek option must exist. If this environment variable is undefined, then the default path .Pa /usr/libdata/lint will be used to search for the libraries. .It Ev TMPDIR usually the path for temporary files can be redefined by setting this environment variable. .It Ev CC Location of the C compiler program. Defaults to .Pa /usr/bin/cc . .El .Sh FILES .Bl -tag -width /usr/libdata/lint/llib-lc.ln -compact .It Pa /usr/libexec/lint Ns Bq Pa 12 programs .It Pa /usr/libdata/lint/llib-l*.ln various prebuilt lint libraries .It Pa /tmp/lint* temporaries .El .Sh SEE ALSO .Xr cc 1 , .Xr cpp 1 , .Xr make 1 .Sh AUTHORS .An Jochen Pohl .Sh BUGS .Bl -item .It The routines .Xr exit 3 , .Xr longjmp 3 and other functions that do not return are not understood; this causes various incorrect diagnostics. .It Static functions which are used only before their first extern declaration are reported as unused. .It Libraries created by the .Fl o option will, when used in later .Nm runs, cause certain errors that were reported when the libraries were created to be reported again, and cause line numbers and file names from the original source used to create those libraries to be reported in error messages. For these reasons, it is recommended to use the .Fl C option to create lint libraries. .El diff --git a/usr.bin/xlint/xlint/xlint.c b/usr.bin/xlint/xlint/xlint.c index 7980d6cc1ccb..7c9994d5cc3a 100644 --- a/usr.bin/xlint/xlint/xlint.c +++ b/usr.bin/xlint/xlint/xlint.c @@ -1,873 +1,883 @@ -/* $NetBSD: xlint.c,v 1.27 2002/01/31 19:09:33 tv Exp $ */ +/* $NetBSD: xlint.c,v 1.36 2005/02/09 21:24:48 dsl 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: xlint.c,v 1.27 2002/01/31 19:09:33 tv Exp $"); +__RCSID("$NetBSD: xlint.c,v 1.36 2005/02/09 21:24:48 dsl Exp $"); #endif __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include "lint.h" #include "pathnames.h" #define DEFAULT_PATH _PATH_DEFPATH int main(int, char *[]); /* directory for temporary files */ static const char *tmpdir; /* path name for cpp output */ static char *cppout; /* file descriptor for cpp output */ static int cppoutfd = -1; /* files created by 1st pass */ static char **p1out; /* input files for 2nd pass (without libraries) */ static char **p2in; /* library which will be created by 2nd pass */ static char *p2out; /* flags always passed to cc(1) */ static char **cflags; /* flags for cc(1), controlled by sflag/tflag */ static char **lcflags; /* flags for lint1 */ static char **l1flags; /* flags for lint2 */ static char **l2flags; /* libraries for lint2 */ static char **l2libs; /* default libraries */ static char **deflibs; /* additional libraries */ static char **libs; /* search path for libraries */ static char **libsrchpath; static char *libexec_path; /* flags */ -static int iflag, oflag, Cflag, sflag, tflag, Fflag, dflag, Bflag; +static int iflag, oflag, Cflag, sflag, tflag, Fflag, dflag, Bflag, Sflag; /* print the commands executed to run the stages of compilation */ static int Vflag; /* filename for oflag */ static char *outputfn; /* reset after first .c source has been processed */ static int first = 1; /* * name of a file which is currently written by a child and should * be removed after abnormal termination of the child */ static const char *currfn; #if !defined(TARGET_PREFIX) #define TARGET_PREFIX "" #endif static const char target_prefix[] = TARGET_PREFIX; static void appstrg(char ***, char *); static void appcstrg(char ***, const char *); static void applst(char ***, char *const *); static void freelst(char ***); static char *concat2(const char *, const char *); static char *concat3(const char *, const char *, const char *); static void terminate(int) __attribute__((__noreturn__)); static const char *lbasename(const char *, int); static void appdef(char ***, const char *); static void usage(void) __dead2; static void fname(const char *); static void runchild(const char *, char *const *, const char *, int); static void findlibs(char *const *); static int rdok(const char *); static void lint2(void); static void cat(char *const *, const char *); /* * Some functions to deal with lists of strings. - * Take care that we get no surprises in case of asyncron signals. + * Take care that we get no surprises in case of asynchronous signals. */ static void appstrg(char ***lstp, char *s) { char **lst, **olst; int i; olst = *lstp; for (i = 0; olst[i] != NULL; i++) continue; lst = xrealloc(olst, (i + 2) * sizeof (char *)); lst[i] = s; lst[i + 1] = NULL; *lstp = lst; } static void appcstrg(char ***lstp, const char *s) { appstrg(lstp, xstrdup(s)); } static void applst(char ***destp, char *const *src) { int i, k; char **dest, **odest; odest = *destp; for (i = 0; odest[i] != NULL; i++) continue; for (k = 0; src[k] != NULL; k++) continue; dest = xrealloc(odest, (i + k + 1) * sizeof (char *)); for (k = 0; src[k] != NULL; k++) dest[i + k] = xstrdup(src[k]); dest[i + k] = NULL; *destp = dest; } static void freelst(char ***lstp) { char *s; int i; for (i = 0; (*lstp)[i] != NULL; i++) continue; while (i-- > 0) { s = (*lstp)[i]; (*lstp)[i] = NULL; free(s); } } static char * concat2(const char *s1, const char *s2) { char *s; s = xmalloc(strlen(s1) + strlen(s2) + 1); (void)strcpy(s, s1); (void)strcat(s, s2); return (s); } static char * concat3(const char *s1, const char *s2, const char *s3) { char *s; s = xmalloc(strlen(s1) + strlen(s2) + strlen(s3) + 1); (void)strcpy(s, s1); (void)strcat(s, s2); (void)strcat(s, s3); return (s); } /* * Clean up after a signal. */ static void terminate(int signo) { int i; if (cppoutfd != -1) (void)close(cppoutfd); if (cppout != NULL) (void)remove(cppout); if (p1out != NULL) { for (i = 0; p1out[i] != NULL; i++) (void)remove(p1out[i]); } if (p2out != NULL) (void)remove(p2out); if (currfn != NULL) (void)remove(currfn); exit(signo != 0 ? 1 : 0); } /* * Returns a pointer to the last component of strg after delim. * Returns strg if the string does not contain delim. */ static const char * lbasename(const char *strg, int delim) { const char *cp, *cp1, *cp2; cp = cp1 = cp2 = strg; while (*cp != '\0') { if (*cp++ == delim) { cp2 = cp1; cp1 = cp; } } return (*cp1 == '\0' ? cp2 : cp1); } static void appdef(char ***lstp, const char *def) { appstrg(lstp, concat2("-D__", def)); appstrg(lstp, concat3("-D__", def, "__")); } static void usage(void) { (void)fprintf(stderr, - "usage: lint [-abceghprvwxzHF] [-s|-t] [-i|-nu] [-Dname[=def]]" + "usage: lint [-abceghprvwxzHFS] [-s|-t] [-i|-nu] [-Dname[=def]]" " [-Uname] [-X [,]...\n"); (void)fprintf(stderr, "\t[-Idirectory] [-Ldirectory] [-llibrary] [-ooutputfile]" " file...\n"); (void)fprintf(stderr, - " lint [-abceghprvwzHF] [-s|-t] -Clibrary [-Dname[=def]]\n" + " lint [-abceghprvwzHFS] [-s|-t] -Clibrary [-Dname[=def]]\n" " [-X [,]...\n"); (void)fprintf(stderr, "\t[-Idirectory] [-Uname] [-Bpath] file" " ...\n"); terminate(-1); } int main(int argc, char *argv[]) { int c; char flgbuf[3], *s; const char *tmp; size_t len; if ((tmp = getenv("TMPDIR")) == NULL || (len = strlen(tmp)) == 0) { tmpdir = _PATH_TMP; } else { s = xmalloc(len + 2); (void)sprintf(s, "%s%s", tmp, tmp[len - 1] == '/' ? "" : "/"); tmpdir = s; } cppout = xmalloc(strlen(tmpdir) + sizeof ("lint0.XXXXXX")); (void)sprintf(cppout, "%slint0.XXXXXX", tmpdir); cppoutfd = mkstemp(cppout); if (cppoutfd == -1) { warn("can't make temp"); terminate(-1); } p1out = xcalloc(1, sizeof (char *)); p2in = xcalloc(1, sizeof (char *)); cflags = xcalloc(1, sizeof (char *)); lcflags = xcalloc(1, sizeof (char *)); l1flags = xcalloc(1, sizeof (char *)); l2flags = xcalloc(1, sizeof (char *)); l2libs = xcalloc(1, sizeof (char *)); deflibs = xcalloc(1, sizeof (char *)); libs = xcalloc(1, sizeof (char *)); libsrchpath = xcalloc(1, sizeof (char *)); appcstrg(&cflags, "-E"); appcstrg(&cflags, "-x"); appcstrg(&cflags, "c"); #if 0 appcstrg(&cflags, "-D__attribute__(x)="); appcstrg(&cflags, "-D__extension__(x)=/*NOSTRICT*/0"); #else appcstrg(&cflags, "-U__GNUC__"); appcstrg(&cflags, "-undef"); +#endif +#if 0 + appcstrg(&cflags, "-Wp,-$"); #endif appcstrg(&cflags, "-Wp,-C"); appcstrg(&cflags, "-Wcomment"); appcstrg(&cflags, "-D__LINT__"); appcstrg(&cflags, "-Dlint"); /* XXX don't def. with -s */ appdef(&cflags, "lint"); appcstrg(&deflibs, "c"); if (signal(SIGHUP, terminate) == SIG_IGN) (void)signal(SIGHUP, SIG_IGN); (void)signal(SIGINT, terminate); (void)signal(SIGQUIT, terminate); (void)signal(SIGTERM, terminate); - - while ((c = getopt(argc, argv, "abcd:eghil:no:prstuvwxzB:C:D:FHI:L:U:VX:")) != -1) { + while ((c = getopt(argc, argv, "abcd:eghil:no:prstuvwxzB:C:D:FHI:L:M:SU:VX:")) != -1) { switch (c) { case 'a': case 'b': case 'c': case 'e': case 'g': case 'r': case 'v': case 'w': case 'z': (void)sprintf(flgbuf, "-%c", c); appcstrg(&l1flags, flgbuf); break; case 'F': Fflag = 1; /* FALLTHROUGH */ case 'u': case 'h': (void)sprintf(flgbuf, "-%c", c); appcstrg(&l1flags, flgbuf); appcstrg(&l2flags, flgbuf); break; case 'X': (void)sprintf(flgbuf, "-%c", c); appcstrg(&l1flags, flgbuf); appcstrg(&l1flags, optarg); break; case 'i': if (Cflag) usage(); iflag = 1; break; case 'n': freelst(&deflibs); break; case 'p': appcstrg(&lcflags, "-Wtraditional"); appcstrg(&lcflags, "-Wno-system-headers"); appcstrg(&l1flags, "-p"); appcstrg(&l2flags, "-p"); if (*deflibs != NULL) { freelst(&deflibs); appcstrg(&deflibs, "c"); } break; case 's': if (tflag) usage(); freelst(&lcflags); appcstrg(&lcflags, "-trigraphs"); appcstrg(&lcflags, "-Wtrigraphs"); appcstrg(&lcflags, "-pedantic"); appcstrg(&lcflags, "-D__STRICT_ANSI__"); appcstrg(&l1flags, "-s"); appcstrg(&l2flags, "-s"); sflag = 1; break; + case 'S': + if (tflag) + usage(); + appcstrg(&l1flags, "-S"); + Sflag = 1; + break; + #if !HAVE_CONFIG_H case 't': if (sflag) usage(); freelst(&lcflags); appcstrg(&lcflags, "-traditional"); appstrg(&lcflags, concat2("-D", MACHINE)); appstrg(&lcflags, concat2("-D", MACHINE_ARCH)); appcstrg(&l1flags, "-t"); appcstrg(&l2flags, "-t"); tflag = 1; break; #endif case 'x': appcstrg(&l2flags, "-x"); break; case 'C': if (Cflag || oflag || iflag) usage(); Cflag = 1; appstrg(&l2flags, concat2("-C", optarg)); p2out = xmalloc(sizeof ("llib-l.ln") + strlen(optarg)); (void)sprintf(p2out, "llib-l%s.ln", optarg); freelst(&deflibs); break; case 'd': if (dflag) usage(); dflag = 1; appcstrg(&cflags, "-nostdinc"); appcstrg(&cflags, "-idirafter"); appcstrg(&cflags, optarg); break; case 'D': case 'I': + case 'M': case 'U': (void)sprintf(flgbuf, "-%c", c); appstrg(&cflags, concat2(flgbuf, optarg)); break; case 'l': appcstrg(&libs, optarg); break; case 'o': if (Cflag || oflag) usage(); oflag = 1; outputfn = xstrdup(optarg); break; case 'L': appcstrg(&libsrchpath, optarg); break; case 'H': appcstrg(&l2flags, "-H"); break; case 'B': Bflag = 1; libexec_path = xstrdup(optarg); break; case 'V': Vflag = 1; break; default: usage(); /* NOTREACHED */ } } argc -= optind; argv += optind; /* * To avoid modifying getopt(3)'s state engine midstream, we * explicitly accept just a few options after the first source file. * * In particular, only -l and -L (and these with a space * after -l or -L) are allowed. */ while (argc > 0) { const char *arg = argv[0]; if (arg[0] == '-') { char ***list; /* option */ switch (arg[1]) { case 'l': list = &libs; break; case 'L': list = &libsrchpath; break; default: usage(); /* NOTREACHED */ } if (arg[2]) appcstrg(list, arg + 2); else if (argc > 1) { argc--; appcstrg(list, *++argv); } else usage(); } else { /* filename */ fname(arg); first = 0; } argc--; argv++; } if (first) usage(); if (iflag) terminate(0); if (!oflag) { if ((tmp = getenv("LIBDIR")) == NULL || strlen(tmp) == 0) tmp = PATH_LINTLIB; appcstrg(&libsrchpath, tmp); findlibs(libs); findlibs(deflibs); } (void)printf("Lint pass2:\n"); lint2(); if (oflag) cat(p2in, outputfn); if (Cflag) p2out = NULL; terminate(0); /* NOTREACHED */ } /* * Read a file name from the command line * and pass it through lint1 if it is a C source. */ static void fname(const char *name) { const char *bn, *suff; char **args, *ofn, *p, *pathname; size_t len; int is_stdin; int fd; is_stdin = (strcmp(name, "-") == 0); bn = lbasename(name, '/'); suff = lbasename(bn, '.'); if (strcmp(suff, "ln") == 0) { /* only for lint2 */ if (!iflag) appcstrg(&p2in, name); return; } if (!is_stdin && strcmp(suff, "c") != 0 && (strncmp(bn, "llib-l", 6) != 0 || bn != suff)) { warnx("unknown file type: %s\n", name); return; } if (!iflag || !first) (void)printf("%s:\n", is_stdin ? "{standard input}" : Fflag ? name : bn); /* build the name of the output file of lint1 */ if (oflag) { ofn = outputfn; outputfn = NULL; oflag = 0; } else if (iflag) { if (is_stdin) { warnx("-i not supported without -o for standard input"); return; } ofn = xmalloc(strlen(bn) + (bn == suff ? 4 : 2)); len = bn == suff ? strlen(bn) : (size_t)((suff - 1) - bn); (void)sprintf(ofn, "%.*s", (int)len, bn); (void)strcat(ofn, ".ln"); } else { ofn = xmalloc(strlen(tmpdir) + sizeof ("lint1.XXXXXX")); (void)sprintf(ofn, "%slint1.XXXXXX", tmpdir); fd = mkstemp(ofn); if (fd == -1) { warn("can't make temp"); terminate(-1); } close(fd); } if (!iflag) appcstrg(&p1out, ofn); args = xcalloc(1, sizeof (char *)); /* run cc */ if (getenv("CC") == NULL) { pathname = xmalloc(strlen(PATH_USRBIN) + sizeof ("/cc")); (void)sprintf(pathname, "%s/cc", PATH_USRBIN); appcstrg(&args, pathname); } else { pathname = strdup(getenv("CC")); for (p = strtok(pathname, " \t"); p; p = strtok(NULL, " \t")) appcstrg(&args, p); } applst(&args, cflags); applst(&args, lcflags); appcstrg(&args, name); /* we reuse the same tmp file for cpp output, so rewind and truncate */ if (lseek(cppoutfd, (off_t)0, SEEK_SET) != 0) { warn("lseek"); terminate(-1); } if (ftruncate(cppoutfd, (off_t)0) != 0) { warn("ftruncate"); terminate(-1); } runchild(pathname, args, cppout, cppoutfd); free(pathname); freelst(&args); /* run lint1 */ if (!Bflag) { pathname = xmalloc(strlen(PATH_LIBEXEC) + sizeof ("/lint1") + strlen(target_prefix)); (void)sprintf(pathname, "%s/%slint1", PATH_LIBEXEC, target_prefix); } else { /* * XXX Unclear whether we should be using target_prefix * XXX here. --thorpej@wasabisystems.com */ pathname = xmalloc(strlen(libexec_path) + sizeof ("/lint1")); (void)sprintf(pathname, "%s/lint1", libexec_path); } appcstrg(&args, pathname); applst(&args, l1flags); appcstrg(&args, cppout); appcstrg(&args, ofn); runchild(pathname, args, ofn, -1); free(pathname); freelst(&args); appcstrg(&p2in, ofn); free(ofn); free(args); } static void runchild(const char *path, char *const *args, const char *crfn, int fdout) { int status, rv, signo, i; if (Vflag) { for (i = 0; args[i] != NULL; i++) (void)printf("%s ", args[i]); (void)printf("\n"); } currfn = crfn; (void)fflush(stdout); switch (vfork()) { case -1: warn("cannot fork"); terminate(-1); /* NOTREACHED */ default: /* parent */ break; case 0: /* child */ /* setup the standard output if necessary */ if (fdout != -1) { dup2(fdout, STDOUT_FILENO); close(fdout); } (void)execvp(path, args); warn("cannot exec %s", path); _exit(1); /* NOTREACHED */ } while ((rv = wait(&status)) == -1 && errno == EINTR) ; if (rv == -1) { warn("wait"); terminate(-1); } if (WIFSIGNALED(status)) { signo = WTERMSIG(status); #if HAVE_DECL_SYS_SIGNAME warnx("%s got SIG%s", path, sys_signame[signo]); #else warnx("%s got signal %d", path, signo); #endif terminate(-1); } if (WEXITSTATUS(status) != 0) terminate(-1); currfn = NULL; } static void findlibs(char *const *liblst) { int i, k; const char *lib, *path; char *lfn; size_t len; lfn = NULL; for (i = 0; (lib = liblst[i]) != NULL; i++) { for (k = 0; (path = libsrchpath[k]) != NULL; k++) { len = strlen(path) + strlen(lib); lfn = xrealloc(lfn, len + sizeof ("/llib-l.ln")); (void)sprintf(lfn, "%s/llib-l%s.ln", path, lib); if (rdok(lfn)) break; lfn = xrealloc(lfn, len + sizeof ("/lint/llib-l.ln")); (void)sprintf(lfn, "%s/lint/llib-l%s.ln", path, lib); if (rdok(lfn)) break; } if (path != NULL) { appstrg(&l2libs, concat2("-l", lfn)); } else { warnx("cannot find llib-l%s.ln", lib); } } free(lfn); } static int rdok(const char *path) { struct stat sbuf; if (stat(path, &sbuf) == -1) return (0); if (!S_ISREG(sbuf.st_mode)) return (0); if (access(path, R_OK) == -1) return (0); return (1); } static void lint2(void) { char *path, **args; args = xcalloc(1, sizeof (char *)); if (!Bflag) { path = xmalloc(strlen(PATH_LIBEXEC) + sizeof ("/lint2") + strlen(target_prefix)); (void)sprintf(path, "%s/%slint2", PATH_LIBEXEC, target_prefix); } else { /* * XXX Unclear whether we should be using target_prefix * XXX here. --thorpej@wasabisystems.com */ path = xmalloc(strlen(libexec_path) + sizeof ("/lint2")); (void)sprintf(path, "%s/lint2", libexec_path); } appcstrg(&args, path); applst(&args, l2flags); applst(&args, l2libs); applst(&args, p2in); runchild(path, args, p2out, -1); free(path); freelst(&args); free(args); } static void cat(char *const *srcs, const char *dest) { int ifd, ofd, i; char *src, *buf; ssize_t rlen; if ((ofd = open(dest, O_WRONLY | O_CREAT | O_TRUNC, 0666)) == -1) { warn("cannot open %s", dest); terminate(-1); } buf = xmalloc(MBLKSIZ); for (i = 0; (src = srcs[i]) != NULL; i++) { if ((ifd = open(src, O_RDONLY)) == -1) { free(buf); warn("cannot open %s", src); terminate(-1); } do { if ((rlen = read(ifd, buf, MBLKSIZ)) == -1) { free(buf); warn("read error on %s", src); terminate(-1); } if (write(ofd, buf, (size_t)rlen) == -1) { free(buf); warn("write error on %s", dest); terminate(-1); } } while (rlen == MBLKSIZ); (void)close(ifd); } (void)close(ofd); free(buf); }