]> git.draconx.ca Git - gob-dx.git/blob - src/checks.c
Release 1.0.9
[gob-dx.git] / src / checks.c
1 /* GOB C Preprocessor
2  * Copyright (C) 1999-2000 the Free Software Foundation.
3  * Copyright (C) 2000 Eazel, Inc.
4  *
5  * Author: George Lebl
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the  Free Software
19  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
20  * USA.
21  */
22
23 #include "config.h"
24 #include <string.h>
25 #include <stdio.h>
26 #include <glib.h>
27
28 #include "treefuncs.h"
29 #include "main.h"
30 #include "util.h"
31
32 #include "checks.h"
33
34 static void
35 check_duplicate(Class *c, Node *node, const char *id, int line_no,
36                 gboolean underscore)
37 {
38         GList *l;
39         for(l = c->nodes; l != NULL; l = g_list_next(l)) {
40                 Node *n = l->data;
41                 const char *nid;
42                 int nline_no;
43                 gboolean here_underscore = FALSE;
44                 if(n->type == METHOD_NODE) {
45                         Method *m = (Method *)n;
46
47                         /* override methods are checked separately */
48                         if(m->method == OVERRIDE_METHOD)
49                                 continue;
50
51                         nid = get_real_id(m->id);
52                         nline_no = m->line_no;
53
54                         if(m->id[0] == '_' && m->id[1] != '\0')
55                                 here_underscore = TRUE;
56                 } else if(n->type == VARIABLE_NODE) {
57                         Variable *v = (Variable *)n;
58                         nid = v->id;
59                         nline_no = v->line_no;
60                 } else
61                         continue;
62                 if(n == node ||
63                    line_no > nline_no ||
64                    n->type != node->type ||
65                    strcmp(nid, id) != 0)
66                         continue;
67                 /* this can only happen if the things were methods and
68                  * one had an underscore and the other one didn't */
69                 if( ! no_kill_underscores && underscore != here_underscore)
70                         error_printf(GOB_ERROR, nline_no,
71                                      "symbol '%s' ('_%s') redefined, "
72                                      "first defined on line %d. "
73                                      "Note that '%s' and '_%s' are "
74                                      "eqivalent.",
75                                      id, id, line_no, id, id);
76                 else
77                         error_printf(GOB_ERROR, nline_no,
78                                      "symbol '%s' redefined, "
79                                      "first defined on line %d",
80                                      id, line_no);
81         }
82 }
83
84 void
85 check_duplicate_symbols(Class *c)
86 {
87         GList *l;
88         for(l = c->nodes; l != NULL; l = g_list_next(l)) {
89                 Node *n = l->data;
90                 if(n->type == METHOD_NODE) {
91                         Method *m = (Method *)n;
92                         gboolean underscore = FALSE;
93                         /* override methods are checked separately */
94                         if(m->method == OVERRIDE_METHOD)
95                                 continue;
96                         if(m->id[0] == '_' && m->id[1] != '\0')
97                                 underscore = TRUE;
98                         check_duplicate(c, n, get_real_id(m->id), m->line_no,
99                                         underscore);
100                 } else if(n->type == VARIABLE_NODE) {
101                         Variable *v = (Variable *)n;
102                         check_duplicate(c, n, v->id, v->line_no, FALSE);
103                 }
104         }
105 }
106
107 static void
108 check_duplicate_override(Class *c, Method *method)
109 {
110         GList *l;
111         for(l = c->nodes; l != NULL; l = g_list_next(l)) {
112                 Node *n = l->data;
113                 Method *m = (Method *)n;
114                 if(n->type != METHOD_NODE ||
115                    m->method != OVERRIDE_METHOD)
116                         continue;
117
118                 if(method == m ||
119                    method->line_no > m->line_no ||
120                    strcmp(get_real_id(m->id), get_real_id(method->id)) != 0 ||
121                    strcmp(m->otype, method->otype) != 0)
122                         continue;
123                 error_printf(GOB_ERROR, m->line_no,
124                              "override '%s(%s)' redefined, "
125                              "first defined on line %d",
126                              get_real_id(m->id), m->otype, method->line_no);
127         }
128 }
129
130 void
131 check_duplicate_overrides(Class *c)
132 {
133         GList *l;
134         for(l = c->nodes; l != NULL; l = g_list_next(l)) {
135                 Node *n = l->data;
136                 Method *m = (Method *)n;
137                 if(n->type != METHOD_NODE ||
138                    m->method != OVERRIDE_METHOD)
139                         continue;
140                 check_duplicate_override(c, m);
141         }
142 }
143
144 void
145 check_bad_symbols(Class *c)
146 {
147         GList *l;
148         for(l = c->nodes; l != NULL; l = g_list_next(l)) {
149                 Node *n = l->data;
150                 if(n->type == METHOD_NODE) {
151                         Method *m = (Method *)n;
152                         if((m->method == SIGNAL_LAST_METHOD ||
153                             m->method == SIGNAL_FIRST_METHOD ||
154                             m->method == VIRTUAL_METHOD) &&
155                            (strcmp(m->id, "_epv")==0 ||
156                             strcmp(m->id, "__parent__")==0 ||
157                             strcmp(m->id, "___parent__")==0)) {
158                                 error_printf(GOB_ERROR, m->line_no,
159                                              "'%s' not allowed as an "
160                                              "identifier of signal "
161                                              "or virtual methods",
162                                              m->id);
163                         }
164                         if(m->method != INIT_METHOD &&
165                            m->method != CLASS_INIT_METHOD &&
166                            (strcmp(get_real_id(m->id), "init")==0 ||
167                             strcmp(get_real_id(m->id), "class_init")==0)) {
168                                 error_print(GOB_ERROR, m->line_no,
169                                             "init, or class_init not "
170                                             "allowed as an "
171                                             "identifier of non-"
172                                             "constructor methods");
173                         }
174                 } else if(n->type == VARIABLE_NODE) {
175                         Variable *v = (Variable *)n;
176                         if(strcmp(v->id, "_priv")==0 ||
177                            strcmp(v->id, "__parent__")==0) {
178                                 error_printf(GOB_ERROR, v->line_no,
179                                              "'%s' not allowed as a "
180                                              "data member name", v->id);
181                         }
182                 }
183         }
184 }
185
186 static void
187 check_duplicate_named(Class *c, Node *node, const char *id, int line_no)
188 {
189         GList *l;
190         for(l = c->nodes; l != NULL; l = g_list_next(l)) {
191                 Node *n = l->data;
192                 const char *nid;
193                 int nline_no;
194                 if(n->type == METHOD_NODE) {
195                         Method *m = (Method *)n;
196                         if(m->method == SIGNAL_LAST_METHOD ||
197                            m->method == SIGNAL_FIRST_METHOD) {
198                                 nid = get_real_id(m->id);
199                                 nline_no = m->line_no;
200                         } else
201                                 continue;
202                 } else if(n->type == ARGUMENT_NODE) {
203                         Argument *a = (Argument *)n;
204                         nid = a->name;
205                         nline_no = a->line_no;
206                 } else
207                         continue;
208                 if(n == node ||
209                    line_no >= nline_no ||
210                    g_strcasecmp(nid, id)!=0)
211                         continue;
212                 error_printf(GOB_ERROR, nline_no,
213                              "named symbol (argument or signal) '%s' "
214                              "redefined, first defined on line %d "
215                              "(case insensitive)",
216                              id, line_no);
217         }
218 }
219
220 void
221 check_duplicate_signals_args(Class *c)
222 {
223         GList *l;
224         for(l = c->nodes; l != NULL; l = g_list_next(l)) {
225                 Node *n = l->data;
226                 if(n->type == METHOD_NODE) {
227                         Method *m = (Method *)n;
228                         if(m->method == SIGNAL_LAST_METHOD ||
229                            m->method == SIGNAL_FIRST_METHOD)
230                                 check_duplicate_named(c, n, get_real_id(m->id),
231                                                       m->line_no);
232                 } else if(n->type == ARGUMENT_NODE) {
233                         Argument *a = (Argument *)n;
234                         check_duplicate_named(c, n, a->name, a->line_no);
235                 }
236         }
237 }
238
239 void
240 check_public_new(Class *c)
241 {
242         GList *l;
243         for(l = c->nodes; l != NULL; l = g_list_next(l)) {
244                 Node *n = l->data;
245                 if(n->type == METHOD_NODE) {
246                         Method *m = (Method *)n;
247                         if((strcmp(get_real_id(m->id), "new")==0) &&
248                            (m->method != REGULAR_METHOD ||
249                             m->scope != PUBLIC_SCOPE))
250                                 error_print(GOB_WARN, m->line_no,
251                                             "'new' should be a regular\n"
252                                             "public method");
253                 }
254         }
255 }
256
257 void
258 check_vararg(Class *c)
259 {
260         GList *l;
261         for(l = c->nodes; l != NULL; l = g_list_next(l)) {
262                 Node *n = l->data;
263                 if(n->type == METHOD_NODE) {
264                         Method *m = (Method *)n;
265                         if( ! m->vararg)
266                                 continue;
267                         if(m->method == OVERRIDE_METHOD ||
268                            m->method == SIGNAL_LAST_METHOD ||
269                            m->method == SIGNAL_FIRST_METHOD ||
270                            m->method == VIRTUAL_METHOD) {
271                                 error_print(GOB_ERROR, m->line_no,
272                                             "signals, overrides and virtuals, "
273                                             "can't have variable argument "
274                                             "lists");
275                         }
276                 }
277         }
278 }
279
280 void
281 check_firstarg(Class *c)
282 {
283         GList *l;
284         for(l = c->nodes; l != NULL; l = g_list_next(l)) {
285                 Node *n = l->data;
286                 if(n->type == METHOD_NODE) {
287                         Method *m = (Method *)n;
288                         if(m->args)
289                                 continue;
290                         if(m->method == OVERRIDE_METHOD ||
291                            m->method == SIGNAL_LAST_METHOD ||
292                            m->method == SIGNAL_FIRST_METHOD ||
293                            m->method == VIRTUAL_METHOD) {
294                                 error_print(GOB_ERROR, m->line_no,
295                                             "signals, overrides and virtuals, "
296                                             "can't have no arguments");
297                         }
298                 }
299         }
300 }
301
302 void
303 check_nonvoidempty(Class *c)
304 {
305         GList *li;
306         for(li = c->nodes; li != NULL; li = g_list_next(li)) {
307                 Node *n = li->data;
308                 if(n->type == METHOD_NODE) {
309                         Method *m = (Method *)n;
310                         if(m->method != REGULAR_METHOD)
311                                 continue;
312                         if(!(strcmp(m->mtype->name, "void")==0 &&
313                              m->mtype->pointer == NULL) &&
314                            !m->cbuf) {
315                                 error_print(GOB_WARN, m->line_no,
316                                             "non-void empty method found, "
317                                             "regular non-void function should "
318                                             "not be empty.");
319                                 /* add a body here, so that the user will also
320                                    get a warning from gcc, and so that it will
321                                    at least point him to the prototype of the
322                                    function in the .gob file */
323                                 m->cbuf = g_strdup("/*empty*/");
324                                 m->ccode_line = m->line_no;
325                         }
326                 }
327         }
328 }
329
330 void
331 check_signal_args(Class *c)
332 {
333         GList *li;
334         for(li = c->nodes; li != NULL; li = g_list_next(li)) {
335                 Node *n = li->data;
336                 if(n->type == METHOD_NODE) {
337                         Method *m = (Method *)n;
338                         GList *l;
339                         if(m->method != SIGNAL_LAST_METHOD &&
340                            m->method != SIGNAL_FIRST_METHOD)
341                                 continue;
342
343                         for(l=m->gtktypes;l;l=l->next) {
344                                 if(get_cast(l->data, FALSE))
345                                         continue;
346                                 error_printf(GOB_ERROR, m->line_no,
347                                              "Unknown GTK+ type '%s' "
348                                              "among signal types",
349                                              (char *)l->data);
350                         }
351                 }
352         }
353 }
354
355 void
356 check_argument_types(Class *c)
357 {
358         GList *l;
359         for(l = c->nodes; l != NULL; l = g_list_next(l)) {
360                 Node *n = l->data;
361                 if(n->type == ARGUMENT_NODE) {
362                         Argument *a = (Argument *)n;
363                         if(get_cast(a->gtktype, FALSE))
364                                 continue;
365                         /* this could perhaps be a warning, but
366                            can there really be a type beyond the
367                            fundementals? */
368                         error_printf(GOB_ERROR, a->line_no,
369                                      "Unknown GTK+ type '%s' "
370                                      "as argument type",
371                                      a->gtktype);
372                 }
373         }
374 }
375
376 static void
377 check_func_arg_check_func_arg(Method *m, FuncArg *fa)
378 {
379         GList *li;
380         char *s;
381
382         if( ! fa->checks)
383                 return;
384
385         if(strcmp(fa->atype->name, "void") == 0 &&
386            fa->atype->pointer == NULL) {
387                 error_print(GOB_ERROR, m->line_no,
388                             "Running checks on a void function argument");
389                 return;
390         }
391         
392         for(li = fa->checks; li; li = g_list_next(li)) {
393                 Check *ch = li->data;
394                 if(ch->chtype == TYPE_CHECK) {
395                         char *p;
396                         gboolean got_type = FALSE;
397                         s = g_strdup(fa->atype->name);
398                         p = strtok(s, " ");
399                         if( ! p) {
400                                 g_free(s);
401                                 goto type_check_error;
402                         }
403                         while(p) {
404                                 if(strcmp(p, "const") != 0) {
405                                         if(got_type) {
406                                                 g_free(s);
407                                                 goto type_check_error;
408                                         }
409                                         got_type = TRUE;
410                                 }
411                                 p = strtok(NULL, " ");
412                         }
413                         g_free(s);
414                         if( ! got_type)
415                                 goto type_check_error;
416
417                         if(fa->atype->pointer == NULL ||
418                            (strcmp(fa->atype->pointer, "*") != 0 &&
419                             strcmp(fa->atype->pointer, "* const") != 0 &&
420                             strcmp(fa->atype->pointer, "const *") != 0))
421                                 goto type_check_error;
422                 }
423         }
424         return;
425
426 type_check_error:
427         if(fa->atype->pointer)
428                 error_printf(GOB_ERROR, m->line_no,
429                              "Cannot check the type of '%s %s'",
430                              fa->atype->name, fa->atype->pointer);
431         else
432                 error_printf(GOB_ERROR, m->line_no,
433                              "Cannot check the type of '%s'",
434                              fa->atype->name);
435 }
436
437 static void
438 check_func_arg_check_method(Method *m)
439 {
440         GList *li;
441         for(li = m->args; li; li = g_list_next(li)) {
442                 FuncArg *fa = li->data;
443                 check_func_arg_check_func_arg(m, fa);
444         }
445 }
446
447 void
448 check_func_arg_checks(Class *c)
449 {
450         GList *li;
451         for(li = c->nodes; li != NULL; li = g_list_next(li)) {
452                 Node *n = li->data;
453                 if(n->type == METHOD_NODE) {
454                         Method *m = (Method *)n;
455                         check_func_arg_check_method(m);
456                 }
457         }
458 }
459
460 int
461 count_signals(Class *c)
462 {
463         int num = 0;
464         GList *l;
465         for(l = c->nodes; l != NULL; l = g_list_next(l)) {
466                 Node *n = l->data;
467                 if(n->type == METHOD_NODE) {
468                         Method *m = (Method *)n;
469                         if(m->method == SIGNAL_LAST_METHOD ||
470                            m->method == SIGNAL_FIRST_METHOD)
471                                 num++;
472                 }
473         }
474         return num;
475 }
476
477 int
478 count_set_arguments(Class *c)
479 {
480         int num = 0;
481         GList *li;
482         for(li = c->nodes; li != NULL; li = g_list_next(li)) {
483                 Node *n = li->data;
484                 Argument *a = li->data;
485                 if(n->type == ARGUMENT_NODE &&
486                    a->set)
487                         num ++;
488         }
489         return num;
490 }
491
492 int
493 count_get_arguments(Class *c)
494 {
495         int num = 0;
496         GList *li;
497         for(li = c->nodes; li != NULL; li = g_list_next(li)) {
498                 Node *n = li->data;
499                 Argument *a = li->data;
500                 if(n->type == ARGUMENT_NODE &&
501                    a->get)
502                         num ++;
503         }
504         return num;
505 }
506
507 int
508 count_overrides(Class *c)
509 {
510         int num = 0;
511         GList *l;
512         for(l = c->nodes; l != NULL; l = g_list_next(l)) {
513                 Node *n = l->data;
514                 if(n->type == METHOD_NODE) {
515                         Method *m = (Method *)n;
516                         if(m->method == OVERRIDE_METHOD)
517                                 num++;
518                 }
519         }
520         return num;
521 }
522
523 int
524 count_privates(Class *c)
525 {
526         int num = 0;
527         GList *l;
528         for(l = c->nodes; l != NULL; l = g_list_next(l)) {
529                 Node *n = l->data;
530                 if(n->type == VARIABLE_NODE) {
531                         Variable *v = (Variable *)n;
532                         if(v->scope == PRIVATE_SCOPE)
533                                 num++;
534                 }
535         }
536         return num;
537 }
538
539 int
540 count_protecteds(Class *c)
541 {
542         int num = 0;
543         GList *l;
544         for(l = c->nodes; l != NULL; l = g_list_next(l)) {
545                 Node *n = l->data;
546                 if(n->type == METHOD_NODE) {
547                         Method *m = (Method *)n;
548                         if(m->scope == PROTECTED_SCOPE)
549                                 num++;
550                 }
551         }
552         return num;
553 }
554
555 int
556 count_destructors(Class *c)
557 {
558         int num = 0;
559         GList *l;
560         for(l = c->nodes; l != NULL; l = g_list_next(l)) {
561                 Node *n = l->data;
562                 if(n->type == VARIABLE_NODE) {
563                         Variable *v = (Variable *)n;
564                         if(v->destructor)
565                                 num++;
566                 }
567         }
568         return num;
569 }
570
571 int
572 count_initializers(Class *c)
573 {
574         int num = 0;
575         GList *l;
576         for(l = c->nodes; l != NULL; l = g_list_next(l)) {
577                 Node *n = l->data;
578                 if(n->type == VARIABLE_NODE) {
579                         Variable *v = (Variable *)n;
580                         if(v->initializer)
581                                 num++;
582                 }
583         }
584         return num;
585 }
586
587 gboolean
588 find_get_type (Class *c)
589 {
590         GList *l;
591         for(l = c->nodes; l != NULL; l = g_list_next(l)) {
592                 Node *n = l->data;
593                 Method *m = (Method *)n;
594                 if(n->type == METHOD_NODE &&
595                    strcmp (m->id, "get_type") == 0) {
596                         if (m->method != REGULAR_METHOD ||
597                             m->scope != PUBLIC_SCOPE ||
598                             m->args != NULL) {
599                                 error_printf (GOB_ERROR, m->line_no,
600                                               "get_type method must be a "
601                                               "regular public method with "
602                                               "no arguments");
603                         }
604                         return TRUE;
605                 }
606         }
607
608         return FALSE;
609 }