1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 * 25 * check.c -- routines for checking the prop tree 26 * 27 * this module provides semantic checks on the parse tree. most of 28 * these checks happen during the construction of the parse tree, 29 * when the various tree_X() routines call the various check_X() 30 * routines. in a couple of special cases, a check function will 31 * process the parse tree after it has been fully constructed. these 32 * cases are noted in the comments above the check function. 33 */ 34 35 #pragma ident "%Z%%M% %I% %E% SMI" 36 37 #include <stdio.h> 38 #include "out.h" 39 #include "stable.h" 40 #include "literals.h" 41 #include "lut.h" 42 #include "tree.h" 43 #include "ptree.h" 44 #include "check.h" 45 46 static int check_reportlist(enum nodetype t, const char *s, struct node *np); 47 static int check_num(enum nodetype t, const char *s, struct node *np); 48 static int check_quote(enum nodetype t, const char *s, struct node *np); 49 static int check_action(enum nodetype t, const char *s, struct node *np); 50 static int check_num_func(enum nodetype t, const char *s, struct node *np); 51 static int check_fru_asru(enum nodetype t, const char *s, struct node *np); 52 static int check_engine(enum nodetype t, const char *s, struct node *np); 53 static int check_count(enum nodetype t, const char *s, struct node *np); 54 static int check_timeval(enum nodetype t, const char *s, struct node *np); 55 static int check_id(enum nodetype t, const char *s, struct node *np); 56 static int check_serd_method(enum nodetype t, const char *s, struct node *np); 57 static int check_serd_id(enum nodetype t, const char *s, struct node *np); 58 static int check_nork(struct node *np); 59 static void check_cycle_lhs(struct node *stmtnp, struct node *arrow); 60 static void check_cycle_lhs_try(struct node *stmtnp, struct node *lhs, 61 struct node *rhs); 62 static void check_cycle_rhs(struct node *rhs); 63 static void check_proplists_lhs(enum nodetype t, struct node *lhs); 64 65 static struct { 66 enum nodetype t; 67 const char *name; 68 int required; 69 int (*checker)(enum nodetype t, const char *s, struct node *np); 70 int outflags; 71 } Allowednames[] = { 72 { T_FAULT, "FITrate", 1, check_num_func, O_ERR }, 73 { T_FAULT, "FRU", 0, check_fru_asru, O_ERR }, 74 { T_FAULT, "ASRU", 0, check_fru_asru, O_ERR }, 75 { T_FAULT, "message", 0, check_num_func, O_ERR }, 76 { T_FAULT, "action", 0, check_action, O_ERR }, 77 { T_FAULT, "count", 0, check_count, O_ERR }, 78 { T_UPSET, "engine", 0, check_engine, O_ERR }, 79 { T_DEFECT, "FRU", 0, check_fru_asru, O_ERR }, 80 { T_DEFECT, "ASRU", 0, check_fru_asru, O_ERR }, 81 { T_EREPORT, "poller", 0, check_id, O_ERR }, 82 { T_EREPORT, "delivery", 0, check_timeval, O_ERR }, 83 { T_SERD, "N", 1, check_num, O_ERR }, 84 { T_SERD, "T", 1, check_timeval, O_ERR }, 85 { T_SERD, "method", 1, check_serd_method, O_ERR }, 86 { T_SERD, "trip", 1, check_reportlist, O_ERR }, 87 { T_SERD, "FRU", 0, check_fru_asru, O_ERR }, 88 { T_SERD, "id", 0, check_serd_id, O_ERR }, 89 { T_ERROR, "ASRU", 0, check_fru_asru, O_ERR }, 90 { T_CONFIG, NULL, 0, check_quote, O_ERR }, 91 { 0, NULL, 0 }, 92 }; 93 94 void 95 check_init(void) 96 { 97 int i; 98 99 for (i = 0; Allowednames[i].t; i++) 100 if (Allowednames[i].name != NULL) 101 Allowednames[i].name = stable(Allowednames[i].name); 102 } 103 104 void 105 check_fini(void) 106 { 107 } 108 109 /*ARGSUSED*/ 110 void 111 check_report_combination(struct node *np) 112 { 113 /* nothing to check for here. poller is only prop and it is optional */ 114 } 115 116 /* 117 * check_path_iterators -- verify all iterators are explicit 118 */ 119 static void 120 check_path_iterators(struct node *np) 121 { 122 if (np == NULL) 123 return; 124 125 switch (np->t) { 126 case T_ARROW: 127 check_path_iterators(np->u.arrow.lhs); 128 check_path_iterators(np->u.arrow.rhs); 129 break; 130 131 case T_LIST: 132 check_path_iterators(np->u.expr.left); 133 check_path_iterators(np->u.expr.right); 134 break; 135 136 case T_EVENT: 137 check_path_iterators(np->u.event.epname); 138 break; 139 140 case T_NAME: 141 if (np->u.name.child == NULL) 142 outfl(O_DIE, np->file, np->line, 143 "internal error: check_path_iterators: " 144 "unexpected implicit iterator: %s", 145 np->u.name.s); 146 check_path_iterators(np->u.name.next); 147 break; 148 149 default: 150 outfl(O_DIE, np->file, np->line, 151 "internal error: check_path_iterators: " 152 "unexpected type: %s", 153 ptree_nodetype2str(np->t)); 154 } 155 } 156 157 void 158 check_arrow(struct node *np) 159 { 160 ASSERTinfo(np->t == T_ARROW, ptree_nodetype2str(np->t)); 161 162 if (np->u.arrow.lhs->t != T_ARROW && 163 np->u.arrow.lhs->t != T_LIST && 164 np->u.arrow.lhs->t != T_EVENT) { 165 outfl(O_ERR, 166 np->u.arrow.lhs->file, np->u.arrow.lhs->line, 167 "%s not allowed on left-hand side of arrow", 168 ptree_nodetype2str(np->u.arrow.lhs->t)); 169 } 170 171 if (!check_nork(np->u.arrow.nnp) || 172 !check_nork(np->u.arrow.knp)) 173 outfl(O_ERR, np->file, np->line, 174 "counts associated with propagation arrows " 175 "must be integers"); 176 177 check_path_iterators(np); 178 } 179 180 /* 181 * make sure the nork values are valid. 182 * Nork values must be "A" for all(T_NAME), 183 * a number(T_NUM), or a simple 184 * expression(T_SUB, T_ADD, T_MUL, T_DIV) 185 */ 186 static int 187 check_nork(struct node *np) 188 { 189 int rval = 0; 190 191 /* NULL means no nork value which is allowed */ 192 if (np == NULL) { 193 rval = 1; 194 } 195 else 196 { 197 /* if the nork is a name it must be A for "All" */ 198 if (np->t == T_NAME) 199 if (*np->u.name.s == 'A') 200 return (1); 201 202 /* T_NUM allowed */ 203 if (np->t == T_NUM) 204 rval = 1; 205 206 /* simple expressions allowed */ 207 if (np->t == T_SUB || 208 np->t == T_ADD || 209 np->t == T_MUL || 210 np->t == T_DIV) 211 rval = 1; 212 } 213 214 return (rval); 215 } 216 217 static int 218 check_reportlist(enum nodetype t, const char *s, struct node *np) 219 { 220 if (np == NULL) 221 return (1); 222 else if (np->t == T_EVENT) { 223 if (np->u.event.ename->u.name.t != N_EREPORT) { 224 outfl(O_ERR, np->file, np->line, 225 "%s %s property must begin with \"ereport.\"", 226 ptree_nodetype2str(t), s); 227 } else if (tree_event2np_lut_lookup(Ereports, np) == NULL) { 228 outfl(O_ERR, np->file, np->line, 229 "%s %s property contains undeclared name", 230 ptree_nodetype2str(t), s); 231 } 232 check_type_iterator(np); 233 } else if (np->t == T_LIST) { 234 (void) check_reportlist(t, s, np->u.expr.left); 235 (void) check_reportlist(t, s, np->u.expr.right); 236 } 237 return (1); 238 } 239 240 static int 241 check_num(enum nodetype t, const char *s, struct node *np) 242 { 243 ASSERTinfo(np != NULL, ptree_nodetype2str(t)); 244 if (np->t != T_NUM) 245 outfl(O_ERR, np->file, np->line, 246 "%s %s property must be a single number", 247 ptree_nodetype2str(t), s); 248 return (1); 249 } 250 251 /*ARGSUSED1*/ 252 static int 253 check_quote(enum nodetype t, const char *s, struct node *np) 254 { 255 ASSERTinfo(np != NULL, ptree_nodetype2str(t)); 256 if (np->t != T_QUOTE) 257 outfl(O_ERR, np->file, np->line, 258 "%s properties must be quoted strings", 259 ptree_nodetype2str(t)); 260 return (1); 261 } 262 263 static int 264 check_action(enum nodetype t, const char *s, struct node *np) 265 { 266 ASSERTinfo(np != NULL, ptree_nodetype2str(t)); 267 268 if (np->t != T_FUNC) 269 outfl(O_ERR, np->file, np->line, 270 "%s %s property must be a function or list of functions", 271 ptree_nodetype2str(t), s); 272 return (1); 273 } 274 275 static int 276 check_num_func(enum nodetype t, const char *s, struct node *np) 277 { 278 ASSERTinfo(np != NULL, ptree_nodetype2str(t)); 279 if (np->t != T_NUM && np->t != T_FUNC) 280 outfl(O_ERR, np->file, np->line, 281 "%s %s property must be a number or function", 282 ptree_nodetype2str(t), s); 283 return (1); 284 } 285 286 static int 287 check_fru_asru(enum nodetype t, const char *s, struct node *np) 288 { 289 ASSERT(s != NULL); 290 291 /* make sure it is a node type T_NAME? */ 292 if (np->t == T_NAME) { 293 if (s == L_ASRU) { 294 if (tree_name2np_lut_lookup_name(ASRUs, np) == NULL) 295 outfl(O_ERR, np->file, np->line, 296 "ASRU property contains undeclared asru"); 297 } else if (s == L_FRU) { 298 if (tree_name2np_lut_lookup_name(FRUs, np) == NULL) 299 outfl(O_ERR, np->file, np->line, 300 "FRU property contains undeclared fru"); 301 } else { 302 outfl(O_ERR, np->file, np->line, 303 "illegal property name in %s declaration: %s", 304 ptree_nodetype2str(t), s); 305 } 306 check_type_iterator(np); 307 } else 308 outfl(O_ERR, np->file, np->line, 309 "illegal type used for %s property: %s", 310 s, ptree_nodetype2str(np->t)); 311 return (1); 312 } 313 314 static int 315 check_engine(enum nodetype t, const char *s, struct node *np) 316 { 317 ASSERTinfo(np != NULL, ptree_nodetype2str(t)); 318 if (np->t != T_EVENT) 319 outfl(O_ERR, np->file, np->line, 320 "%s %s property must be an engine name " 321 "(i.e. serd.x or serd.x@a/b)", 322 ptree_nodetype2str(t), s); 323 324 return (1); 325 } 326 327 static int 328 check_count(enum nodetype t, const char *s, struct node *np) 329 { 330 ASSERTinfo(np != NULL, ptree_nodetype2str(t)); 331 if (np->t != T_EVENT) 332 outfl(O_ERR, np->file, np->line, 333 "%s %s property must be an engine name " 334 "(i.e. stat.x or stat.x@a/b)", 335 ptree_nodetype2str(t), s); 336 337 /* XXX confirm engine has been declared */ 338 return (1); 339 } 340 341 static int 342 check_timeval(enum nodetype t, const char *s, struct node *np) 343 { 344 ASSERTinfo(np != NULL, ptree_nodetype2str(t)); 345 if (np->t != T_TIMEVAL) 346 outfl(O_ERR, np->file, np->line, 347 "%s %s property must be a number with time units", 348 ptree_nodetype2str(t), s); 349 return (1); 350 } 351 352 static int 353 check_id(enum nodetype t, const char *s, struct node *np) 354 { 355 ASSERTinfo(np != NULL, ptree_nodetype2str(t)); 356 if (np->t != T_NAME || np->u.name.next || np->u.name.child) 357 outfl(O_ERR, np->file, np->line, 358 "%s %s property must be simple name", 359 ptree_nodetype2str(t), s); 360 return (1); 361 } 362 363 static int 364 check_serd_method(enum nodetype t, const char *s, struct node *np) 365 { 366 ASSERTinfo(np != NULL, ptree_nodetype2str(t)); 367 if (np->t != T_NAME || np->u.name.next || np->u.name.child || 368 (np->u.name.s != L_volatile && 369 np->u.name.s != L_persistent)) 370 outfl(O_ERR, np->file, np->line, 371 "%s %s property must be \"volatile\" or \"persistent\"", 372 ptree_nodetype2str(t), s); 373 return (1); 374 } 375 376 static int 377 check_serd_id(enum nodetype t, const char *s, struct node *np) 378 { 379 ASSERTinfo(np != NULL, ptree_nodetype2str(t)); 380 if (np->t != T_GLOBID) 381 outfl(O_ERR, np->file, np->line, 382 "%s %s property must be a global ID", 383 ptree_nodetype2str(t), s); 384 return (1); 385 } 386 387 void 388 check_stmt_required_properties(struct node *stmtnp) 389 { 390 struct lut *lutp = stmtnp->u.stmt.lutp; 391 struct node *np = stmtnp->u.stmt.np; 392 int i; 393 394 for (i = 0; Allowednames[i].t; i++) 395 if (stmtnp->t == Allowednames[i].t && 396 Allowednames[i].required && 397 tree_s2np_lut_lookup(lutp, Allowednames[i].name) == NULL) 398 outfl(Allowednames[i].outflags, 399 np->file, np->line, 400 "%s statement missing property: %s", 401 ptree_nodetype2str(stmtnp->t), 402 Allowednames[i].name); 403 } 404 405 void 406 check_stmt_allowed_properties(enum nodetype t, 407 struct node *nvpairnp, struct lut *lutp) 408 { 409 int i; 410 const char *s = nvpairnp->u.expr.left->u.name.s; 411 struct node *np; 412 413 for (i = 0; Allowednames[i].t; i++) 414 if (t == Allowednames[i].t && Allowednames[i].name == NULL) { 415 /* NULL name means just call checker */ 416 (*Allowednames[i].checker)(t, s, 417 nvpairnp->u.expr.right); 418 return; 419 } else if (t == Allowednames[i].t && s == Allowednames[i].name) 420 break; 421 if (Allowednames[i].name == NULL) 422 outfl(O_ERR, nvpairnp->file, nvpairnp->line, 423 "illegal property name in %s declaration: %s", 424 ptree_nodetype2str(t), s); 425 else if ((np = tree_s2np_lut_lookup(lutp, s)) != NULL) { 426 /* 427 * redeclaring prop is allowed if value is the same 428 */ 429 if (np->t != nvpairnp->u.expr.right->t) 430 outfl(O_ERR, nvpairnp->file, nvpairnp->line, 431 "property redeclared (with differnt type) " 432 "in %s declaration: %s", 433 ptree_nodetype2str(t), s); 434 switch (np->t) { 435 case T_NUM: 436 case T_TIMEVAL: 437 if (np->u.ull == nvpairnp->u.expr.right->u.ull) 438 return; 439 break; 440 441 case T_NAME: 442 if (tree_namecmp(np, 443 nvpairnp->u.expr.right) == 0) 444 return; 445 break; 446 447 case T_EVENT: 448 if (tree_eventcmp(np, 449 nvpairnp->u.expr.right) == 0) 450 return; 451 break; 452 453 default: 454 outfl(O_ERR, nvpairnp->file, nvpairnp->line, 455 "value for property \"%s\" is an " 456 "invalid type: %s", 457 nvpairnp->u.expr.left->u.name.s, 458 ptree_nodetype2str(np->t)); 459 return; 460 } 461 outfl(O_ERR, nvpairnp->file, nvpairnp->line, 462 "property redeclared in %s declaration: %s", 463 ptree_nodetype2str(t), s); 464 } else 465 (*Allowednames[i].checker)(t, s, nvpairnp->u.expr.right); 466 } 467 468 void 469 check_propnames(enum nodetype t, struct node *np, int from, int to) 470 { 471 struct node *dnp; 472 struct lut *lutp; 473 474 ASSERT(np != NULL); 475 ASSERTinfo(np->t == T_EVENT || np->t == T_LIST || np->t == T_ARROW, 476 ptree_nodetype2str(np->t)); 477 478 if (np->t == T_EVENT) { 479 switch (np->u.event.ename->u.name.t) { 480 case N_UNSPEC: 481 outfl(O_ERR, np->file, np->line, 482 "name in %s statement must begin with " 483 "type (example: \"error.\")", 484 ptree_nodetype2str(t)); 485 return; 486 case N_FAULT: 487 lutp = Faults; 488 if (to) { 489 outfl(O_ERR, np->file, np->line, 490 "%s has fault on right side of \"->\"", 491 ptree_nodetype2str(t)); 492 return; 493 } 494 if (!from) { 495 outfl(O_DIE, np->file, np->line, 496 "internal error: %s has fault without " 497 "from flag", 498 ptree_nodetype2str(t)); 499 } 500 break; 501 case N_UPSET: 502 lutp = Upsets; 503 if (to) { 504 outfl(O_ERR, np->file, np->line, 505 "%s has upset on right side of \"->\"", 506 ptree_nodetype2str(t)); 507 return; 508 } 509 if (!from) 510 outfl(O_DIE, np->file, np->line, 511 "internal error: %s has upset without " 512 "from flag", 513 ptree_nodetype2str(t)); 514 break; 515 case N_DEFECT: 516 lutp = Defects; 517 if (to) { 518 outfl(O_ERR, np->file, np->line, 519 "%s has defect on right side of \"->\"", 520 ptree_nodetype2str(t)); 521 return; 522 } 523 if (!from) { 524 outfl(O_DIE, np->file, np->line, 525 "internal error: %s has defect without " 526 "from flag", 527 ptree_nodetype2str(t)); 528 } 529 break; 530 case N_ERROR: 531 lutp = Errors; 532 if (!from && !to) 533 outfl(O_DIE, np->file, np->line, 534 "%s has error without from or to flags", 535 ptree_nodetype2str(t)); 536 break; 537 case N_EREPORT: 538 lutp = Ereports; 539 if (from) { 540 outfl(O_ERR, np->file, np->line, 541 "%s has report on left side of \"->\"", 542 ptree_nodetype2str(t)); 543 return; 544 } 545 if (!to) 546 outfl(O_DIE, np->file, np->line, 547 "internal error: %s has report without " 548 "to flag", 549 ptree_nodetype2str(t)); 550 break; 551 default: 552 outfl(O_DIE, np->file, np->line, 553 "internal error: check_propnames: " 554 "unexpected type: %d", np->u.name.t); 555 } 556 557 if ((dnp = tree_event2np_lut_lookup(lutp, np)) == NULL) { 558 outfl(O_ERR, np->file, np->line, 559 "%s statement contains undeclared event", 560 ptree_nodetype2str(t)); 561 } else 562 dnp->u.stmt.flags |= STMT_REF; 563 np->u.event.declp = dnp; 564 } else if (np->t == T_LIST) { 565 check_propnames(t, np->u.expr.left, from, to); 566 check_propnames(t, np->u.expr.right, from, to); 567 } else if (np->t == T_ARROW) { 568 check_propnames(t, np->u.arrow.lhs, 1, to); 569 check_propnames(t, np->u.arrow.rhs, from, 1); 570 } 571 } 572 573 static struct lut * 574 record_iterators(struct node *np, struct lut *ex) 575 { 576 if (np == NULL) 577 return (ex); 578 579 switch (np->t) { 580 case T_ARROW: 581 ex = record_iterators(np->u.arrow.lhs, ex); 582 ex = record_iterators(np->u.arrow.rhs, ex); 583 break; 584 585 case T_LIST: 586 ex = record_iterators(np->u.expr.left, ex); 587 ex = record_iterators(np->u.expr.right, ex); 588 break; 589 590 case T_EVENT: 591 ex = record_iterators(np->u.event.epname, ex); 592 break; 593 594 case T_NAME: 595 if (np->u.name.child && np->u.name.child->t == T_NAME) 596 ex = lut_add(ex, (void *) np->u.name.child->u.name.s, 597 (void *) np, NULL); 598 ex = record_iterators(np->u.name.next, ex); 599 break; 600 601 default: 602 outfl(O_DIE, np->file, np->line, 603 "record_iterators: internal error: unexpected type: %s", 604 ptree_nodetype2str(np->t)); 605 } 606 607 return (ex); 608 } 609 610 void 611 check_exprscope(struct node *np, struct lut *ex) 612 { 613 if (np == NULL) 614 return; 615 616 switch (np->t) { 617 case T_EVENT: 618 check_exprscope(np->u.event.eexprlist, ex); 619 break; 620 621 case T_ARROW: 622 check_exprscope(np->u.arrow.lhs, ex); 623 check_exprscope(np->u.arrow.rhs, ex); 624 break; 625 626 case T_NAME: 627 if (np->u.name.child && np->u.name.child->t == T_NAME) { 628 if (lut_lookup(ex, 629 (void *) np->u.name.child->u.name.s, NULL) == NULL) 630 outfl(O_ERR, np->file, np->line, 631 "constraint contains undefined" 632 " iterator: %s", 633 np->u.name.child->u.name.s); 634 } 635 check_exprscope(np->u.name.next, ex); 636 break; 637 638 case T_QUOTE: 639 case T_GLOBID: 640 break; 641 642 case T_ASSIGN: 643 case T_NE: 644 case T_EQ: 645 case T_LIST: 646 case T_AND: 647 case T_OR: 648 case T_NOT: 649 case T_ADD: 650 case T_SUB: 651 case T_MUL: 652 case T_DIV: 653 case T_MOD: 654 case T_LT: 655 case T_LE: 656 case T_GT: 657 case T_GE: 658 case T_BITAND: 659 case T_BITOR: 660 case T_BITXOR: 661 case T_BITNOT: 662 case T_LSHIFT: 663 case T_RSHIFT: 664 case T_CONDIF: 665 case T_CONDELSE: 666 check_exprscope(np->u.expr.left, ex); 667 check_exprscope(np->u.expr.right, ex); 668 break; 669 670 case T_FUNC: 671 check_exprscope(np->u.func.arglist, ex); 672 break; 673 674 case T_NUM: 675 case T_TIMEVAL: 676 break; 677 678 default: 679 outfl(O_DIE, np->file, np->line, 680 "check_exprscope: internal error: unexpected type: %s", 681 ptree_nodetype2str(np->t)); 682 } 683 } 684 685 /* 686 * check_propscope -- check constraints for out of scope variable refs 687 */ 688 void 689 check_propscope(struct node *np) 690 { 691 struct lut *ex; 692 693 ex = record_iterators(np, NULL); 694 check_exprscope(np, ex); 695 lut_free(ex, NULL, NULL); 696 } 697 698 /* 699 * check_upset_engine -- validate the engine property in an upset statement 700 * 701 * we do this after the full parse tree has been constructed rather than while 702 * building the parse tree because it is inconvenient for the user if we 703 * require SERD engines to be declared before used in an upset "engine" 704 * property. 705 */ 706 707 /*ARGSUSED*/ 708 void 709 check_upset_engine(struct node *lhs, struct node *rhs, void *arg) 710 { 711 enum nodetype t = (enum nodetype)arg; 712 struct node *engnp; 713 struct node *declp; 714 715 ASSERTeq(rhs->t, t, ptree_nodetype2str); 716 717 if ((engnp = tree_s2np_lut_lookup(rhs->u.stmt.lutp, L_engine)) == NULL) 718 return; 719 720 ASSERT(engnp->t == T_EVENT); 721 722 if ((declp = tree_event2np_lut_lookup(SERDs, engnp)) == NULL) { 723 outfl(O_ERR, engnp->file, engnp->line, 724 "%s %s property contains undeclared name", 725 ptree_nodetype2str(t), L_engine); 726 return; 727 } 728 engnp->u.event.declp = declp; 729 } 730 731 /* 732 * check_refcount -- see if declared names are used 733 * 734 * this is run after the entire parse tree is constructed, so a refcount 735 * of zero means the name has been declared but otherwise not used. 736 */ 737 738 void 739 check_refcount(struct node *lhs, struct node *rhs, void *arg) 740 { 741 enum nodetype t = (enum nodetype)arg; 742 743 ASSERTeq(rhs->t, t, ptree_nodetype2str); 744 745 if (rhs->u.stmt.flags & STMT_REF) 746 return; 747 748 outfl(O_WARN|O_NONL, rhs->file, rhs->line, 749 "%s name declared but not used: ", ptree_nodetype2str(t)); 750 ptree_name(O_WARN|O_NONL, lhs); 751 out(O_WARN, NULL); 752 } 753 754 /* 755 * set check_cycle_warninglevel only for val >= 0 756 */ 757 int 758 check_cycle_level(long long val) 759 { 760 static int check_cycle_warninglevel = -1; 761 762 if (val == 0) 763 check_cycle_warninglevel = 0; 764 else if (val > 0) 765 check_cycle_warninglevel = 1; 766 767 return (check_cycle_warninglevel); 768 } 769 770 /* 771 * check_cycle -- see props from an error have cycles 772 * 773 * this is run after the entire parse tree is constructed, for 774 * each error that has been declared. 775 */ 776 777 /*ARGSUSED*/ 778 void 779 check_cycle(struct node *lhs, struct node *rhs, void *arg) 780 { 781 struct node *np; 782 783 ASSERTeq(rhs->t, T_ERROR, ptree_nodetype2str); 784 785 if (rhs->u.stmt.flags & STMT_CYCLE) 786 return; /* already reported this cycle */ 787 788 if (rhs->u.stmt.flags & STMT_CYMARK) { 789 #ifdef ESC 790 int warninglevel; 791 792 warninglevel = check_cycle_level(-1); 793 if (warninglevel <= 0) { 794 int olevel = O_ERR; 795 796 if (warninglevel == 0) 797 olevel = O_WARN; 798 799 out(olevel|O_NONL, "cycle in propagation tree: "); 800 ptree_name(olevel|O_NONL, rhs->u.stmt.np); 801 out(olevel, NULL); 802 } 803 #endif /* ESC */ 804 805 rhs->u.stmt.flags |= STMT_CYCLE; 806 } 807 808 rhs->u.stmt.flags |= STMT_CYMARK; 809 810 /* for each propagation */ 811 for (np = Props; np; np = np->u.stmt.next) 812 check_cycle_lhs(rhs, np->u.stmt.np); 813 814 rhs->u.stmt.flags &= ~STMT_CYMARK; 815 } 816 817 /* 818 * check_cycle_lhs -- find the lhs of an arrow for cycle checking 819 */ 820 821 static void 822 check_cycle_lhs(struct node *stmtnp, struct node *arrow) 823 { 824 struct node *trylhs; 825 struct node *tryrhs; 826 827 /* handle cascaded arrows */ 828 switch (arrow->u.arrow.lhs->t) { 829 case T_ARROW: 830 /* first recurse left */ 831 check_cycle_lhs(stmtnp, arrow->u.arrow.lhs); 832 833 /* 834 * return if there's a list of events internal to 835 * cascaded props (which is not allowed) 836 */ 837 if (arrow->u.arrow.lhs->u.arrow.rhs->t != T_EVENT) 838 return; 839 840 /* then try this arrow (thing cascaded *to*) */ 841 trylhs = arrow->u.arrow.lhs->u.arrow.rhs; 842 tryrhs = arrow->u.arrow.rhs; 843 break; 844 845 case T_EVENT: 846 case T_LIST: 847 trylhs = arrow->u.arrow.lhs; 848 tryrhs = arrow->u.arrow.rhs; 849 break; 850 851 default: 852 out(O_DIE, "lhs: unexpected type: %s", 853 ptree_nodetype2str(arrow->u.arrow.lhs->t)); 854 /*NOTREACHED*/ 855 } 856 857 check_cycle_lhs_try(stmtnp, trylhs, tryrhs); 858 } 859 860 /* 861 * check_cycle_lhs_try -- try matching an event name on lhs of an arrow 862 */ 863 864 static void 865 check_cycle_lhs_try(struct node *stmtnp, struct node *lhs, struct node *rhs) 866 { 867 if (lhs->t == T_LIST) { 868 check_cycle_lhs_try(stmtnp, lhs->u.expr.left, rhs); 869 check_cycle_lhs_try(stmtnp, lhs->u.expr.right, rhs); 870 return; 871 } 872 873 ASSERT(lhs->t == T_EVENT); 874 875 if (tree_eventcmp(stmtnp->u.stmt.np, lhs) != 0) 876 return; /* no match */ 877 878 check_cycle_rhs(rhs); 879 } 880 881 /* 882 * check_cycle_rhs -- foreach error on rhs, see if we cycle to a marked error 883 */ 884 885 static void 886 check_cycle_rhs(struct node *rhs) 887 { 888 struct node *dnp; 889 890 if (rhs->t == T_LIST) { 891 check_cycle_rhs(rhs->u.expr.left); 892 check_cycle_rhs(rhs->u.expr.right); 893 return; 894 } 895 896 ASSERT(rhs->t == T_EVENT); 897 898 if (rhs->u.event.ename->u.name.t != N_ERROR) 899 return; 900 901 if ((dnp = tree_event2np_lut_lookup(Errors, rhs)) == NULL) { 902 outfl(O_ERR|O_NONL, 903 rhs->file, rhs->line, 904 "unexpected undeclared event during cycle check"); 905 ptree_name(O_ERR|O_NONL, rhs); 906 out(O_ERR, NULL); 907 return; 908 } 909 check_cycle(NULL, dnp, 0); 910 } 911 912 /* 913 * Force iterators to be simple names, expressions, or numbers 914 */ 915 void 916 check_name_iterator(struct node *np) 917 { 918 if (np->u.name.child->t != T_NUM && 919 np->u.name.child->t != T_NAME && 920 np->u.name.child->t != T_CONDIF && 921 np->u.name.child->t != T_SUB && 922 np->u.name.child->t != T_ADD && 923 np->u.name.child->t != T_MUL && 924 np->u.name.child->t != T_DIV && 925 np->u.name.child->t != T_MOD && 926 np->u.name.child->t != T_LSHIFT && 927 np->u.name.child->t != T_RSHIFT) { 928 outfl(O_ERR|O_NONL, np->file, np->line, 929 "invalid iterator: "); 930 ptree_name_iter(O_ERR|O_NONL, np); 931 out(O_ERR, NULL); 932 } 933 } 934 935 /* 936 * Iterators on a declaration may only be implicit 937 */ 938 void 939 check_type_iterator(struct node *np) 940 { 941 while (np != NULL) { 942 if (np->t == T_EVENT) { 943 np = np->u.event.epname; 944 } else if (np->t == T_NAME) { 945 if (np->u.name.child != NULL && 946 np->u.name.child->t != T_NUM) { 947 outfl(O_ERR|O_NONL, np->file, np->line, 948 "explicit iterators disallowed " 949 "in declarations: "); 950 ptree_name_iter(O_ERR|O_NONL, np); 951 out(O_ERR, NULL); 952 } 953 np = np->u.name.next; 954 } else { 955 break; 956 } 957 } 958 } 959 960 void 961 check_func(struct node *np) 962 { 963 ASSERTinfo(np->t == T_FUNC, ptree_nodetype2str(np->t)); 964 965 if (np->u.func.s == L_within) { 966 struct node *arglist = np->u.func.arglist; 967 switch (arglist->t) { 968 case T_NUM: 969 if (arglist->u.ull != 0ULL) 970 outfl(O_ERR, 971 arglist->file, arglist->line, 972 "parameter of within must be 0" 973 ", \"infinity\" or a time value."); 974 break; 975 case T_NAME: 976 if (arglist->u.name.s != L_infinity) 977 outfl(O_ERR, 978 arglist->file, arglist->line, 979 "parameter of within must be 0" 980 ", \"infinity\" or a time value."); 981 break; 982 case T_LIST: 983 /* 984 * if two parameters, the left or min must be 985 * either T_NUM or T_TIMEVAL 986 */ 987 if (arglist->u.expr.left->t != T_NUM && 988 arglist->u.expr.left->t != T_TIMEVAL) 989 outfl(O_ERR, 990 arglist->file, arglist->line, 991 "first parameter of within must be" 992 " either a time value or zero."); 993 994 /* 995 * if two parameters, the right or max must 996 * be either T_NUM, T_NAME or T_TIMEVAL 997 */ 998 if (arglist->u.expr.right->t != T_NUM && 999 arglist->u.expr.right->t != T_TIMEVAL && 1000 arglist->u.expr.right->t != T_NAME) 1001 outfl(O_ERR, 1002 arglist->file, arglist->line, 1003 "second parameter of within must " 1004 "be 0, \"infinity\" " 1005 "or time value."); 1006 1007 /* 1008 * if right or left is a T_NUM it must 1009 * be zero 1010 */ 1011 if (arglist->u.expr.left->t == T_NUM) 1012 if (arglist->u.expr.left->u.ull != 0ULL) 1013 outfl(O_ERR, arglist->file, 1014 arglist->line, 1015 "within parameter must be " 1016 "0 or a time value."); 1017 if (arglist->u.expr.right->t == T_NUM) 1018 if (arglist->u.expr.right->u.ull 1019 != 0ULL) 1020 outfl(O_ERR, 1021 arglist->file, 1022 arglist->line, 1023 "within parameter must be " 1024 "0 or a time value."); 1025 1026 /* if right is a T_NAME it must be "infinity" */ 1027 if (arglist->u.expr.right->t == T_NAME) 1028 if (arglist->u.expr.right->u.name.s 1029 != L_infinity) 1030 outfl(O_ERR, 1031 arglist->file, 1032 arglist->line, 1033 "\"infinity\" is the only " 1034 "valid name for within " 1035 "parameter."); 1036 1037 /* 1038 * the first parameter [min] must not be greater 1039 * than the second parameter [max]. 1040 */ 1041 if (arglist->u.expr.left->u.ull > 1042 arglist->u.expr.right->u.ull) 1043 outfl(O_ERR, 1044 arglist->file, arglist->line, 1045 "the first value (min) of" 1046 " within must be less than" 1047 " the second (max) value"); 1048 break; 1049 case T_TIMEVAL: 1050 break; /* no restrictions on T_TIMEVAL */ 1051 default: 1052 outfl(O_ERR, arglist->file, arglist->line, 1053 "parameter of within must be 0" 1054 ", \"infinity\" or a time value."); 1055 } 1056 } else if (np->u.func.s == L_call) { 1057 if (np->u.func.arglist->t != T_QUOTE && 1058 np->u.func.arglist->t != T_LIST && 1059 np->u.func.arglist->t != T_GLOBID && 1060 np->u.func.arglist->t != T_CONDIF && 1061 np->u.func.arglist->t != T_LIST && 1062 np->u.func.arglist->t != T_FUNC) 1063 outfl(O_ERR, np->u.func.arglist->file, 1064 np->u.func.arglist->line, 1065 "invalid first argument to call()"); 1066 } else if (np->u.func.s == L_fru) { 1067 if (np->u.func.arglist->t != T_NAME) 1068 outfl(O_ERR, np->u.func.arglist->file, 1069 np->u.func.arglist->line, 1070 "argument to fru() must be a path"); 1071 } else if (np->u.func.s == L_asru) { 1072 if (np->u.func.arglist->t != T_NAME) 1073 outfl(O_ERR, np->u.func.arglist->file, 1074 np->u.func.arglist->line, 1075 "argument to asru() must be a path"); 1076 } else if (np->u.func.s == L_is_connected || 1077 np->u.func.s == L_is_under) { 1078 if (np->u.func.arglist->t == T_LIST && 1079 (np->u.func.arglist->u.expr.left->t == T_NAME || 1080 (np->u.func.arglist->u.expr.left->t == T_FUNC && 1081 (np->u.func.arglist->u.expr.left->u.func.s == L_fru || 1082 np->u.func.arglist->u.expr.left->u.func.s == L_asru))) && 1083 (np->u.func.arglist->u.expr.right->t == T_NAME || 1084 (np->u.func.arglist->u.expr.right->t == T_FUNC && 1085 (np->u.func.arglist->u.expr.right->u.func.s == L_fru || 1086 np->u.func.arglist->u.expr.right->u.func.s == L_asru)))) { 1087 if (np->u.func.arglist->u.expr.left->t == T_FUNC) 1088 check_func(np->u.func.arglist->u.expr.left); 1089 if (np->u.func.arglist->u.expr.right->t == T_FUNC) 1090 check_func(np->u.func.arglist->u.expr.right); 1091 } else { 1092 outfl(O_ERR, np->u.func.arglist->file, 1093 np->u.func.arglist->line, 1094 "%s() must have paths or calls to " 1095 "fru() and/or asru() as arguments", 1096 np->u.func.s); 1097 } 1098 } else if (np->u.func.s == L_is_on) { 1099 if (np->u.func.arglist->t == T_FUNC && 1100 (np->u.func.arglist->u.func.s == L_fru || 1101 np->u.func.arglist->u.func.s == L_asru)) { 1102 check_func(np->u.func.arglist); 1103 } else { 1104 outfl(O_ERR, np->u.func.arglist->file, 1105 np->u.func.arglist->line, 1106 "argument to is_on() must be a call to " 1107 "fru() or asru()"); 1108 } 1109 } else if (np->u.func.s == L_is_present) { 1110 if (np->u.func.arglist->t == T_FUNC && 1111 (np->u.func.arglist->u.func.s == L_fru || 1112 np->u.func.arglist->u.func.s == L_asru)) { 1113 check_func(np->u.func.arglist); 1114 } else { 1115 outfl(O_ERR, np->u.func.arglist->file, 1116 np->u.func.arglist->line, 1117 "argument to is_present() must be a call to " 1118 "fru() or asru()"); 1119 } 1120 } else if (np->u.func.s == L_is_type) { 1121 if (np->u.func.arglist->t == T_FUNC && 1122 (np->u.func.arglist->u.func.s == L_fru || 1123 np->u.func.arglist->u.func.s == L_asru)) { 1124 check_func(np->u.func.arglist); 1125 } else { 1126 outfl(O_ERR, np->u.func.arglist->file, 1127 np->u.func.arglist->line, 1128 "argument to is_type() must be a call to " 1129 "fru() or asru()"); 1130 } 1131 } else if (np->u.func.s == L_confcall) { 1132 if (np->u.func.arglist->t != T_QUOTE && 1133 (np->u.func.arglist->t != T_LIST || 1134 np->u.func.arglist->u.expr.left->t != T_QUOTE)) 1135 outfl(O_ERR, np->u.func.arglist->file, 1136 np->u.func.arglist->line, 1137 "confcall(): first argument must be a string " 1138 "(the name of the operation)"); 1139 } else if (np->u.func.s == L_confprop || 1140 np->u.func.s == L_confprop_defined) { 1141 if (np->u.func.arglist->t == T_LIST && 1142 (np->u.func.arglist->u.expr.left->t == T_FUNC && 1143 (np->u.func.arglist->u.expr.left->u.func.s == L_fru || 1144 np->u.func.arglist->u.expr.left->u.func.s == L_asru)) && 1145 np->u.func.arglist->u.expr.right->t == T_QUOTE) { 1146 check_func(np->u.func.arglist->u.expr.left); 1147 } else { 1148 outfl(O_ERR, np->u.func.arglist->file, 1149 np->u.func.arglist->line, 1150 "%s(): first argument must be a call to " 1151 "fru() or asru(); " 1152 "second argument must be a string", np->u.func.s); 1153 } 1154 } else if (np->u.func.s == L_count) { 1155 if (np->u.func.arglist->t != T_EVENT) { 1156 outfl(O_ERR, np->u.func.arglist->file, 1157 np->u.func.arglist->line, 1158 "count(): argument must be an engine name"); 1159 } 1160 } else if (np->u.func.s == L_defined) { 1161 if (np->u.func.arglist->t != T_GLOBID) 1162 outfl(O_ERR, np->u.func.arglist->file, 1163 np->u.func.arglist->line, 1164 "argument to defined() must be a global"); 1165 } else if (np->u.func.s == L_payloadprop) { 1166 if (np->u.func.arglist->t != T_QUOTE) 1167 outfl(O_ERR, np->u.func.arglist->file, 1168 np->u.func.arglist->line, 1169 "argument to payloadprop() must be a string"); 1170 } else if (np->u.func.s == L_payloadprop_contains) { 1171 if (np->u.func.arglist->t != T_LIST || 1172 np->u.func.arglist->u.expr.left->t != T_QUOTE || 1173 np->u.func.arglist->u.expr.right == NULL) 1174 outfl(O_ERR, np->u.func.arglist->file, 1175 np->u.func.arglist->line, 1176 "args to payloadprop_contains(): must be a quoted " 1177 "string (property name) and an expression " 1178 "(to match)"); 1179 } else if (np->u.func.s == L_payloadprop_defined) { 1180 if (np->u.func.arglist->t != T_QUOTE) 1181 outfl(O_ERR, np->u.func.arglist->file, 1182 np->u.func.arglist->line, 1183 "arg to payloadprop_defined(): must be a quoted " 1184 "string"); 1185 } else if (np->u.func.s == L_setpayloadprop) { 1186 if (np->u.func.arglist->t == T_LIST && 1187 np->u.func.arglist->u.expr.left->t == T_QUOTE) { 1188 if (np->u.func.arglist->u.expr.right->t == T_FUNC) 1189 check_func(np->u.func.arglist->u.expr.right); 1190 } else { 1191 outfl(O_ERR, np->u.func.arglist->file, 1192 np->u.func.arglist->line, 1193 "setpayloadprop(): " 1194 "first arg must be a string, " 1195 "second arg a value"); 1196 } 1197 } else if (np->u.func.s == L_envprop) { 1198 if (np->u.func.arglist->t != T_QUOTE) 1199 outfl(O_ERR, np->u.func.arglist->file, 1200 np->u.func.arglist->line, 1201 "argument to envprop() must be a string"); 1202 } else 1203 outfl(O_WARN, np->file, np->line, 1204 "possible platform-specific function: %s", 1205 np->u.func.s); 1206 } 1207 1208 void 1209 check_expr(struct node *np) 1210 { 1211 ASSERT(np != NULL); 1212 1213 switch (np->t) { 1214 case T_ASSIGN: 1215 ASSERT(np->u.expr.left != NULL); 1216 if (np->u.expr.left->t != T_GLOBID) 1217 outfl(O_ERR, np->file, np->line, 1218 "assignment only allowed to globals (e.g. $a)"); 1219 break; 1220 } 1221 } 1222 1223 void 1224 check_event(struct node *np) 1225 { 1226 ASSERT(np != NULL); 1227 ASSERTinfo(np->t == T_EVENT, ptree_nodetype2str(np->t)); 1228 1229 if (np->u.event.epname == NULL) { 1230 outfl(O_ERR|O_NONL, np->file, np->line, 1231 "pathless events not allowed: "); 1232 ptree_name(O_ERR|O_NONL, np->u.event.ename); 1233 out(O_ERR, NULL); 1234 } 1235 } 1236 1237 /* 1238 * check for properties that are required on declarations. This 1239 * should be done after all declarations since they can be 1240 * redeclared with a different set of properties. 1241 */ 1242 /*ARGSUSED*/ 1243 void 1244 check_required_props(struct node *lhs, struct node *rhs, void *arg) 1245 { 1246 ASSERTeq(rhs->t, (enum nodetype)arg, ptree_nodetype2str); 1247 1248 check_stmt_required_properties(rhs); 1249 } 1250 1251 /* 1252 * check that cascading prop statements do not contain lists internally. 1253 * the first and last event lists in the cascading prop may be single 1254 * events or lists of events. 1255 */ 1256 /*ARGSUSED*/ 1257 void 1258 check_proplists(enum nodetype t, struct node *np) 1259 { 1260 ASSERT(np->t == T_ARROW); 1261 /* 1262 * not checking the right hand side of the top level prop 1263 * since it is the last part of the propagation and can be 1264 * an event or list of events 1265 */ 1266 check_proplists_lhs(t, np->u.arrow.lhs); 1267 } 1268 1269 /*ARGSUSED*/ 1270 static void 1271 check_proplists_lhs(enum nodetype t, struct node *lhs) 1272 { 1273 if (lhs->t == T_ARROW) { 1274 if (lhs->u.arrow.rhs->t == T_LIST) { 1275 outfl(O_ERR, lhs->file, lhs->line, 1276 "lists are not allowed internally on cascading %s", 1277 (t == T_PROP) ? "propagations" : "masks"); 1278 } 1279 check_proplists_lhs(t, lhs->u.arrow.lhs); 1280 } 1281 } 1282