]> git.ozlabs.org Git - ccan/blobdiff - tools/ccanlint/idempotent.c
Joey found a bug. Bad Rusty, use sentinal attribute.
[ccan] / tools / ccanlint / idempotent.c
index 81984ec713198abed4f94baa88a421d7f9efa042..fa65b05faa8559e4158335c7c57ae9665c45b54a 100644 (file)
@@ -11,6 +11,8 @@
 #include <stdio.h>
 #include <err.h>
 #include <string.h>
+#include <ctype.h>
+#include "../tools.h"
 
 static const char explain[] 
 = "Headers usually start with the C preprocessor lines to prevent multiple\n"
@@ -22,24 +24,90 @@ static const char explain[]
 
 static char *report_idem(struct ccan_file *f, char *sofar)
 {
-       char **lines;
-       char *secondline;
+       struct line_info *line_info;
+       unsigned int i, first_preproc_line;
+       const char *line, *sym;
 
-       lines = get_ccan_file_lines(f);
+       line_info = get_ccan_line_info(f);
        if (f->num_lines < 3)
                /* FIXME: We assume small headers probably uninteresting. */
-               return NULL;
+               return sofar;
 
-       if (!strstarts(lines[0], "#ifndef "))
+       for (i = 0; i < f->num_lines; i++) {
+               if (line_info[i].type == DOC_LINE
+                   || line_info[i].type == COMMENT_LINE)
+                       continue;
+               if (line_info[i].type == CODE_LINE)
+                       return talloc_asprintf_append(sofar,
+                             "%s:%u:expect first non-comment line to be #ifndef.\n", f->name, i+1);
+               else if (line_info[i].type == PREPROC_LINE)
+                       break;
+       }
+
+       /* No code at all?  Don't complain. */
+       if (i == f->num_lines)
+               return sofar;
+
+       first_preproc_line = i;
+       for (i = first_preproc_line+1; i < f->num_lines; i++) {
+               if (line_info[i].type == DOC_LINE
+                   || line_info[i].type == COMMENT_LINE)
+                       continue;
+               if (line_info[i].type == CODE_LINE)
+                       return talloc_asprintf_append(sofar,
+                             "%s:%u:expect second line to be #define.\n", f->name, i+1);
+               else if (line_info[i].type == PREPROC_LINE)
+                       break;
+       }
+
+       /* No code at all?  Weird. */
+       if (i == f->num_lines)
+               return sofar;
+
+       /* We expect a condition on this line. */
+       if (!line_info[i].cond) {
                return talloc_asprintf_append(sofar,
-                       "%s:1:expect first line to be #ifndef.\n", f->name);
+                                             "%s:%u:expected #ifndef.\n",
+                                             f->name, first_preproc_line+1);
+       }
+
+       line = f->lines[i];
 
-       secondline = talloc_asprintf(f, "#define %s",
-                                    lines[0] + strlen("#ifndef "));
-       if (!streq(lines[1], secondline))
+       /* We expect the condition to be ! IFDEF <symbol>. */
+       if (line_info[i].cond->type != PP_COND_IFDEF
+           || !line_info[i].cond->inverse) {
                return talloc_asprintf_append(sofar,
-                       "%s:2:expect second line to be '%s'.\n",
-                       f->name, secondline);
+                                             "%s:%u:expected #ifndef.\n",
+                                             f->name, first_preproc_line+1);
+       }
+
+       /* And this to be #define <symbol> */
+       if (!get_token(&line, "#"))
+               abort();
+       if (!get_token(&line, "define")) {
+               return talloc_asprintf_append(sofar,
+                             "%s:%u:expected '#define %s'.\n",
+                             f->name, i+1, line_info[i].cond->symbol);
+       }
+       sym = get_symbol_token(f, &line);
+       if (!sym || !streq(sym, line_info[i].cond->symbol)) {
+               return talloc_asprintf_append(sofar,
+                             "%s:%u:expected '#define %s'.\n",
+                             f->name, i+1, line_info[i].cond->symbol);
+       }
+
+       /* Rest of code should all be covered by that conditional. */
+       for (i++; i < f->num_lines; i++) {
+               unsigned int val = 0;
+               if (line_info[i].type == DOC_LINE
+                   || line_info[i].type == COMMENT_LINE)
+                       continue;
+               if (get_ccan_line_pp(line_info[i].cond, sym, &val, NULL)
+                   != NOT_COMPILED)
+                       return talloc_asprintf_append(sofar,
+                             "%s:%u:code outside idempotent region.\n",
+                             f->name, i+1);
+       }
 
        return sofar;
 }