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