1 /* $NetBSD: emit1.c,v 1.97 2026/01/11 18:11:38 rillig Exp $ */ 2 3 /* 4 * Copyright (c) 1996 Christopher G. Demetriou. All Rights Reserved. 5 * Copyright (c) 1994, 1995 Jochen Pohl 6 * All Rights Reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. All advertising materials mentioning features or use of this software 17 * must display the following acknowledgement: 18 * This product includes software developed by Jochen Pohl for 19 * The NetBSD Project. 20 * 4. The name of the author may not be used to endorse or promote products 21 * derived from this software without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 24 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 25 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 26 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 27 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 28 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 29 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 30 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 31 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 32 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 33 */ 34 35 #if HAVE_NBTOOL_CONFIG_H 36 #include "nbtool_config.h" 37 #endif 38 39 #include <sys/cdefs.h> 40 #if defined(__RCSID) 41 __RCSID("$NetBSD: emit1.c,v 1.97 2026/01/11 18:11:38 rillig Exp $"); 42 #endif 43 44 #include <stdlib.h> 45 46 #include "lint1.h" 47 48 static void outtt(sym_t *, sym_t *); 49 static void outfstrg(const char *); 50 51 /* 52 * Write type into the output file, encoded as follows: 53 * const c 54 * volatile v 55 * _Bool B 56 * _Complex float s X 57 * _Complex double X 58 * _Complex long double l X 59 * char C 60 * signed char s C 61 * unsigned char u C 62 * short S 63 * unsigned short u S 64 * int I 65 * unsigned int u I 66 * long L 67 * unsigned long u L 68 * long long Q 69 * unsigned long long u Q 70 * float s D 71 * double D 72 * long double l D 73 * void V 74 * * P 75 * [n] A n 76 * () F 77 * (void) F 0 78 * (n parameters) F n arg1 arg2 ... argn 79 * (n parameters, ...) F n arg1 arg2 ... argn E 80 * enum tag e T tag_or_typename 81 * struct tag s T tag_or_typename 82 * union tag u T tag_or_typename 83 * 84 * tag_or_typename 0 (obsolete) no tag or type name 85 * 1 n tag tagged type 86 * 2 n typename only typedef name 87 * 3 line.file.uniq anonymous types 88 */ 89 void 90 outtype(const type_t *tp) 91 { 92 /* Available letters: ------GH--K-MNO--R--U-W-YZ */ 93 static const char tt[NTSPEC] = "???BCCCSSIILLQQJJDDD?XXXV?TTTPAF"; 94 static const char ss[NTSPEC] = "??? su u u u u us l?s l ?sue "; 95 int na; 96 tspec_t ts; 97 98 while (tp != NULL) { 99 if ((ts = tp->t_tspec) == INT && tp->t_is_enum) 100 ts = ENUM; 101 lint_assert(tt[ts] != '?' && ss[ts] != '?'); 102 if (tp->t_const) 103 outchar('c'); 104 if (tp->t_volatile) 105 outchar('v'); 106 if (ss[ts] != ' ') 107 outchar(ss[ts]); 108 outchar(tt[ts]); 109 110 if (ts == ARRAY) { 111 outint(tp->u.dimension); 112 } else if (ts == ENUM) { 113 outtt(tp->u.enumer->en_tag, 114 tp->u.enumer->en_first_typedef); 115 } else if (is_struct_or_union(ts)) { 116 outtt(tp->u.sou->sou_tag, 117 tp->u.sou->sou_first_typedef); 118 } else if (ts == FUNC && tp->t_proto) { 119 na = 0; 120 for (const sym_t *param = tp->u.params; 121 param != NULL; param = param->s_next) 122 na++; 123 if (tp->t_vararg) 124 na++; 125 outint(na); 126 for (const sym_t *param = tp->u.params; 127 param != NULL; param = param->s_next) 128 outtype(param->s_type); 129 if (tp->t_vararg) 130 outchar('E'); 131 } 132 tp = tp->t_subt; 133 } 134 } 135 136 /* 137 * write the name of a tag or typename 138 * 139 * if the tag is named, the name of the tag is written, 140 * otherwise, if a typename exists which refers to this tag, 141 * this typename is written 142 */ 143 static void 144 outtt(sym_t *tag, sym_t *tdef) 145 { 146 147 /* 0 is no longer used. */ 148 149 if (tag->s_name != unnamed) { 150 outint(1); 151 outname(tag->s_name); 152 } else if (tdef != NULL) { 153 outint(2); 154 outname(tdef->s_name); 155 } else { 156 outint(3); 157 outint(tag->s_def_pos.p_line); 158 outchar('.'); 159 outint(get_filename_id(tag->s_def_pos.p_file)); 160 outchar('.'); 161 outint(tag->s_def_pos.p_uniq); 162 } 163 } 164 165 /* 166 * write information about a globally declared/defined symbol 167 * with storage class extern 168 * 169 * information about function definitions are written in outfdef(), 170 * not here 171 */ 172 void 173 outsym(const sym_t *sym, scl_t sc, def_t def) 174 { 175 176 /* 177 * Static function declarations must also be written to the output 178 * file. Compatibility of function declarations (for both static and 179 * extern functions) must be checked in lint2. Lint1 can't do this, 180 * especially not if functions are declared at block level before their 181 * first declaration at level 0. 182 */ 183 if (sc != EXTERN && !(sc == STATIC && sym->s_type->t_tspec == FUNC)) 184 return; 185 if (ch_isdigit(sym->s_name[0])) /* see mktempsym */ 186 return; 187 188 outint(csrc_pos.p_line); 189 outchar('d'); /* declaration */ 190 outint(get_filename_id(sym->s_def_pos.p_file)); 191 outchar('.'); 192 outint(sym->s_def_pos.p_line); 193 194 /* flags */ 195 196 if (def == DEF) 197 outchar('d'); /* defined */ 198 else if (def == TDEF) 199 outchar('t'); /* tentative defined */ 200 else { 201 lint_assert(def == DECL); 202 outchar('e'); /* declared */ 203 } 204 205 if (llibflg && def != DECL) { 206 /* 207 * mark it as used so lint2 does not complain about unused 208 * symbols in libraries 209 */ 210 outchar('u'); 211 } 212 213 if (sc == STATIC) 214 outchar('s'); 215 216 outname(sym->s_name); 217 218 if (sym->s_rename != NULL) { 219 outchar('r'); 220 outname(sym->s_rename); 221 } 222 223 outtype(sym->s_type); 224 outchar('\n'); 225 } 226 227 /* 228 * Write information about a function definition. This is also done for static 229 * functions, to later check if they are called with proper argument types. 230 */ 231 void 232 outfdef(const sym_t *fsym, const pos_t *posp, bool rval, bool osdef, 233 const sym_t *args) 234 { 235 int narg; 236 237 if (posp->p_file == csrc_pos.p_file) { 238 outint(posp->p_line); 239 } else { 240 outint(csrc_pos.p_line); 241 } 242 outchar('d'); /* declaration */ 243 outint(get_filename_id(posp->p_file)); 244 outchar('.'); 245 outint(posp->p_line); 246 247 /* both SCANFLIKE and PRINTFLIKE imply VARARGS */ 248 if (printflike_argnum != -1) { 249 nvararg = printflike_argnum; 250 } else if (scanflike_argnum != -1) { 251 nvararg = scanflike_argnum; 252 } 253 254 if (nvararg != -1) { 255 outchar('v'); 256 outint(nvararg); 257 } 258 if (scanflike_argnum != -1) { 259 outchar('S'); 260 outint(scanflike_argnum); 261 } 262 if (printflike_argnum != -1) { 263 outchar('P'); 264 outint(printflike_argnum); 265 } 266 nvararg = printflike_argnum = scanflike_argnum = -1; 267 268 outchar('d'); 269 270 if (rval) 271 outchar('r'); /* has return value */ 272 273 if (llibflg) 274 /* 275 * mark it as used so lint2 does not complain about unused 276 * symbols in libraries 277 */ 278 outchar('u'); 279 280 if (osdef) 281 outchar('o'); /* old-style function definition */ 282 283 if (fsym->s_inline) 284 outchar('i'); 285 286 if (fsym->s_scl == STATIC) 287 outchar('s'); 288 289 outname(fsym->s_name); 290 291 if (fsym->s_rename != NULL) { 292 outchar('r'); 293 outname(fsym->s_rename); 294 } 295 296 /* parameter types and return value */ 297 if (osdef) { 298 narg = 0; 299 for (const sym_t *arg = args; arg != NULL; arg = arg->s_next) 300 narg++; 301 outchar('f'); 302 outint(narg); 303 for (const sym_t *arg = args; arg != NULL; arg = arg->s_next) 304 outtype(arg->s_type); 305 outtype(fsym->s_type->t_subt); 306 } else { 307 outtype(fsym->s_type); 308 } 309 outchar('\n'); 310 } 311 312 /* 313 * write out all information necessary for lint2 to check function 314 * calls 315 * 316 * retval_used is set if the return value is used (assigned to a variable) 317 * retval_discarded is set if the return value is neither used nor ignored 318 * (that is, cast to void) 319 */ 320 void 321 outcall(const tnode_t *tn, bool retval_used, bool retval_discarded) 322 { 323 outint(csrc_pos.p_line); 324 outchar('c'); /* function call */ 325 outint(get_filename_id(curr_pos.p_file)); 326 outchar('.'); 327 outint(curr_pos.p_line); 328 329 /* 330 * flags; 'u' and 'i' must be last to make sure a letter is between the 331 * numeric argument of a flag and the name of the function 332 */ 333 const function_call *call = tn->u.call; 334 335 for (size_t i = 0, n = call->args_len; i < n; i++) { 336 const tnode_t *arg = call->args[i]; 337 if (arg->tn_op == CON) { 338 tspec_t t = arg->tn_type->t_tspec; 339 if (is_integer(t)) { 340 /* 341 * XXX it would probably be better to 342 * explicitly test the sign 343 */ 344 int64_t si = arg->u.value.u.integer; 345 if (si == 0) 346 /* zero constant */ 347 outchar('z'); 348 else if (!msb(si, t)) 349 /* positive if cast to signed */ 350 outchar('p'); 351 else 352 /* negative if cast to signed */ 353 outchar('n'); 354 outint((int)i + 1); 355 } 356 } else if (arg->tn_op == ADDR && 357 arg->u.ops.left->tn_op == STRING && 358 arg->u.ops.left->u.str_literals->data != NULL) { 359 buffer buf; 360 buf_init(&buf); 361 quoted_iterator it = { .end = 0 }; 362 while (quoted_next(arg->u.ops.left->u.str_literals, &it)) 363 buf_add_char(&buf, (char)it.value); 364 365 /* string literal, write all format specifiers */ 366 outchar('s'); 367 outint((int)i + 1); 368 outfstrg(buf.data); 369 free(buf.data); 370 } 371 } 372 outchar((char)(retval_discarded ? 'd' : retval_used ? 'u' : 'i')); 373 374 outname(call->func->u.ops.left->u.sym->s_name); 375 376 /* types of arguments */ 377 outchar('f'); 378 outint((int)call->args_len); 379 for (size_t i = 0, n = call->args_len; i < n; i++) 380 outtype(call->args[i]->tn_type); 381 /* expected type of return value */ 382 outtype(tn->tn_type); 383 outchar('\n'); 384 } 385 386 /* write a character to the output file, quoted if necessary */ 387 static void 388 outqchar(char c) 389 { 390 391 if (ch_isprint(c) && c != '\\' && c != '"' && c != '\'') { 392 outchar(c); 393 return; 394 } 395 396 outchar('\\'); 397 switch (c) { 398 case '\\': 399 outchar('\\'); 400 break; 401 case '"': 402 outchar('"'); 403 break; 404 case '\'': 405 outchar('\''); 406 break; 407 case '\b': 408 outchar('b'); 409 break; 410 case '\t': 411 outchar('t'); 412 break; 413 case '\n': 414 outchar('n'); 415 break; 416 case '\f': 417 outchar('f'); 418 break; 419 case '\r': 420 outchar('r'); 421 break; 422 case '\v': 423 outchar('v'); 424 break; 425 case '\a': 426 outchar('a'); 427 break; 428 default: 429 outchar((char)((((unsigned char)c >> 6) & 07) + '0')); 430 outchar((char)((((unsigned char)c >> 3) & 07) + '0')); 431 outchar((char)((c & 07) + '0')); 432 break; 433 } 434 } 435 436 /* 437 * extracts potential format specifiers for printf() and scanf() and 438 * writes them, enclosed in "" and quoted if necessary, to the output file 439 */ 440 static void 441 outfstrg(const char *cp) 442 { 443 444 outchar('"'); 445 446 char c = *cp++; 447 while (c != '\0') { 448 449 if (c != '%') { 450 c = *cp++; 451 continue; 452 } 453 454 outchar('%'); 455 c = *cp++; 456 457 /* flags for printf and scanf and *-fieldwidth for printf */ 458 while (c == '-' || c == '+' || c == ' ' || 459 c == '#' || c == '0' || c == '*') { 460 outchar(c); 461 c = *cp++; 462 } 463 464 /* numeric field width */ 465 while (ch_isdigit(c)) { 466 outchar(c); 467 c = *cp++; 468 } 469 470 /* precision for printf */ 471 if (c == '.') { 472 outchar(c); 473 c = *cp++; 474 if (c == '*') { 475 outchar(c); 476 c = *cp++; 477 } else { 478 while (ch_isdigit(c)) { 479 outchar(c); 480 c = *cp++; 481 } 482 } 483 } 484 485 /* h, l, L and q flags for printf and scanf */ 486 if (c == 'h' || c == 'l' || c == 'L' || c == 'q') { 487 outchar(c); 488 c = *cp++; 489 } 490 491 /* 492 * The last character. It is always written, so we can detect 493 * invalid format specifiers. 494 */ 495 if (c != '\0') { 496 outqchar(c); 497 char oc = c; 498 c = *cp++; 499 /* 500 * handle [ for scanf. [-] means that a minus sign was 501 * found at an undefined position. 502 */ 503 if (oc == '[') { 504 if (c == '^') 505 c = *cp++; 506 if (c == ']') 507 c = *cp++; 508 bool first = true; 509 while (c != '\0' && c != ']') { 510 if (c == '-') { 511 if (!first && *cp != ']') 512 outchar(c); 513 } 514 first = false; 515 c = *cp++; 516 } 517 if (c == ']') { 518 outchar(c); 519 c = *cp++; 520 } 521 } 522 } 523 } 524 525 outchar('"'); 526 } 527 528 /* writes a record if sym was used */ 529 void 530 outusg(const sym_t *sym) 531 { 532 if (ch_isdigit(sym->s_name[0])) /* see mktempsym */ 533 return; 534 535 outint(csrc_pos.p_line); 536 outchar('u'); /* used */ 537 outint(get_filename_id(curr_pos.p_file)); 538 outchar('.'); 539 outint(curr_pos.p_line); 540 outchar('x'); /* separate the two numbers */ 541 outname(sym->s_name); 542 outchar('\n'); 543 } 544