1 #include <tools/ccanlint/ccanlint.h>
2 #include <tools/tools.h>
3 #include <ccan/str/str.h>
16 static void fix_name(char *name)
20 for (i = 0; name[i]; i++) {
21 if (cisalnum(name[i]))
22 name[i] = toupper(name[i]);
28 static void handle_idem(struct manifest *m, struct score *score)
32 list_for_each(&score->per_file_errors, e, list) {
33 char *name, *q, *tmpname;
37 /* Main header gets CCAN_FOO_H, others CCAN_FOO_XXX_H */
38 if (strstarts(e->file->name, m->basename)
39 || strlen(e->file->name) == strlen(m->basename) + 2)
40 name = tal_fmt(score, "CCAN_%s_H", m->modname);
42 name = tal_fmt(score, "CCAN_%s_%s",
43 m->modname, e->file->name);
47 "Should I wrap %s in #ifndef/#define %s for you?",
52 tmpname = temp_file(score, ".h", e->file->name);
53 out = fopen(tmpname, "w");
55 err(1, "Opening %s", tmpname);
56 if (fprintf(out, "#ifndef %s\n#define %s\n", name, name) < 0)
57 err(1, "Writing %s", tmpname);
59 for (i = 0; e->file->lines[i]; i++)
60 if (fprintf(out, "%s\n", e->file->lines[i]) < 0)
61 err(1, "Writing %s", tmpname);
63 if (fprintf(out, "#endif /* %s */\n", name) < 0)
64 err(1, "Writing %s", tmpname);
67 err(1, "Closing %s", tmpname);
69 if (!move_file(tmpname, e->file->fullname))
70 err(1, "Moving %s to %s", tmpname, e->file->fullname);
74 static void check_idem(struct ccan_file *f, struct score *score)
76 struct line_info *line_info;
77 unsigned int i, first_preproc_line;
78 const char *line, *sym;
80 line_info = get_ccan_line_info(f);
81 if (tal_count(f->lines) < 4)
82 /* FIXME: We assume small headers probably uninteresting. */
85 for (i = 0; f->lines[i]; i++) {
86 if (line_info[i].type == DOC_LINE
87 || line_info[i].type == COMMENT_LINE)
89 if (line_info[i].type == CODE_LINE) {
90 score_file_error(score, f, i+1,
91 "Expect first non-comment line to be"
94 } else if (line_info[i].type == PREPROC_LINE)
98 /* No code at all? Don't complain. */
102 first_preproc_line = i;
103 for (i = first_preproc_line+1; f->lines[i]; i++) {
104 if (line_info[i].type == DOC_LINE
105 || line_info[i].type == COMMENT_LINE)
107 if (line_info[i].type == CODE_LINE) {
108 score_file_error(score, f, i+1,
109 "Expect second non-comment line to be"
112 } else if (line_info[i].type == PREPROC_LINE)
116 /* No code at all? Weird. */
120 /* We expect a condition around this line. */
121 if (!line_info[i].cond) {
122 score_file_error(score, f, first_preproc_line+1,
129 /* We expect the condition to be ! IFDEF <symbol>. */
130 if (line_info[i].cond->type != PP_COND_IFDEF
131 || !line_info[i].cond->inverse) {
132 score_file_error(score, f, first_preproc_line+1,
137 /* And this to be #define <symbol> */
138 if (!get_token(&line, "#"))
140 if (!get_token(&line, "define")) {
141 score_file_error(score, f, i+1,
142 "expected '#define %s'",
143 line_info[i].cond->symbol);
146 sym = get_symbol_token(f, &line);
147 if (!sym || !streq(sym, line_info[i].cond->symbol)) {
148 score_file_error(score, f, i+1,
149 "expected '#define %s'",
150 line_info[i].cond->symbol);
154 /* Record this for use in depends_accurate */
155 f->idempotent_cond = line_info[i].cond;
157 /* Rest of code should all be covered by that conditional. */
158 for (i++; f->lines[i]; i++) {
159 unsigned int val = 0;
160 if (line_info[i].type == DOC_LINE
161 || line_info[i].type == COMMENT_LINE)
163 if (get_ccan_line_pp(line_info[i].cond, sym, &val, NULL)
165 score_file_error(score, f, i+1, "code outside"
166 " idempotent region");
172 static void check_idempotent(struct manifest *m,
173 unsigned int *timeleft UNNEEDED,
178 /* We don't fail ccanlint for this. */
181 list_for_each(&m->h_files, f, list) {
182 check_idem(f, score);
185 score->score = score->total;
189 struct ccanlint headers_idempotent = {
190 .key = "headers_idempotent",
191 .name = "Module headers are #ifndef/#define wrapped",
192 .check = check_idempotent,
193 .handle = handle_idem,
194 .needs = "info_exists main_header_exists"
197 REGISTER_TEST(headers_idempotent);