1 #include <tools/ccanlint/ccanlint.h>
2 #include <tools/tools.h>
3 #include <ccan/talloc/talloc.h>
4 #include <ccan/str/str.h>
17 static const char explain[]
18 = "Headers usually start with the C preprocessor lines to prevent multiple\n"
19 "inclusions. These look like the following:\n"
20 "#ifndef MY_HEADER_H\n"
21 "#define MY_HEADER_H\n"
23 "#endif /* MY_HEADER_H */\n";
25 static char *report_idem(struct ccan_file *f, char *sofar)
27 struct line_info *line_info;
28 unsigned int i, first_preproc_line;
29 const char *line, *sym;
31 line_info = get_ccan_line_info(f);
33 /* FIXME: We assume small headers probably uninteresting. */
36 for (i = 0; i < f->num_lines; i++) {
37 if (line_info[i].type == DOC_LINE
38 || line_info[i].type == COMMENT_LINE)
40 if (line_info[i].type == CODE_LINE)
41 return talloc_asprintf_append(sofar,
42 "%s:%u:expect first non-comment line to be #ifndef.\n", f->name, i+1);
43 else if (line_info[i].type == PREPROC_LINE)
47 /* No code at all? Don't complain. */
48 if (i == f->num_lines)
51 first_preproc_line = i;
52 for (i = first_preproc_line+1; i < f->num_lines; i++) {
53 if (line_info[i].type == DOC_LINE
54 || line_info[i].type == COMMENT_LINE)
56 if (line_info[i].type == CODE_LINE)
57 return talloc_asprintf_append(sofar,
58 "%s:%u:expect second line to be #define.\n", f->name, i+1);
59 else if (line_info[i].type == PREPROC_LINE)
63 /* No code at all? Weird. */
64 if (i == f->num_lines)
67 /* We expect a condition on this line. */
68 if (!line_info[i].cond) {
69 return talloc_asprintf_append(sofar,
70 "%s:%u:expected #ifndef.\n",
71 f->name, first_preproc_line+1);
76 /* We expect the condition to be ! IFDEF <symbol>. */
77 if (line_info[i].cond->type != PP_COND_IFDEF
78 || !line_info[i].cond->inverse) {
79 return talloc_asprintf_append(sofar,
80 "%s:%u:expected #ifndef.\n",
81 f->name, first_preproc_line+1);
84 /* And this to be #define <symbol> */
85 if (!get_token(&line, "#"))
87 if (!get_token(&line, "define")) {
88 return talloc_asprintf_append(sofar,
89 "%s:%u:expected '#define %s'.\n",
90 f->name, i+1, line_info[i].cond->symbol);
92 sym = get_symbol_token(f, &line);
93 if (!sym || !streq(sym, line_info[i].cond->symbol)) {
94 return talloc_asprintf_append(sofar,
95 "%s:%u:expected '#define %s'.\n",
96 f->name, i+1, line_info[i].cond->symbol);
99 /* Rest of code should all be covered by that conditional. */
100 for (i++; i < f->num_lines; i++) {
101 unsigned int val = 0;
102 if (line_info[i].type == DOC_LINE
103 || line_info[i].type == COMMENT_LINE)
105 if (get_ccan_line_pp(line_info[i].cond, sym, &val, NULL)
107 return talloc_asprintf_append(sofar,
108 "%s:%u:code outside idempotent region.\n",
115 static void *check_idempotent(struct manifest *m, unsigned int *timeleft)
120 list_for_each(&m->h_files, f, list)
121 report = report_idem(f, report);
126 static const char *describe_idempotent(struct manifest *m, void *check_result)
128 return talloc_asprintf(check_result,
129 "Some headers not idempotent:\n"
130 "%s\n%s", (char *)check_result,
134 struct ccanlint idempotent = {
136 .name = "Module headers are #ifndef/#define wrapped",
138 .check = check_idempotent,
139 .describe = describe_idempotent,
142 REGISTER_TEST(idempotent, NULL);