X-Git-Url: http://git.ozlabs.org/?p=ccan;a=blobdiff_plain;f=tools%2Fccanlint%2Ftests%2Fidempotent.c;fp=tools%2Fccanlint%2Ftests%2Fidempotent.c;h=0aa7d8b3ae22c5ad741187bb3b7905abefe5e63b;hp=0000000000000000000000000000000000000000;hb=8f61c0bccb152b2365baf70deac1e59264d7feb7;hpb=15290c3feb9b61967311659a865fb3b5d05b1a49 diff --git a/tools/ccanlint/tests/idempotent.c b/tools/ccanlint/tests/idempotent.c new file mode 100644 index 00000000..0aa7d8b3 --- /dev/null +++ b/tools/ccanlint/tests/idempotent.c @@ -0,0 +1,139 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static const char explain[] += "Headers usually start with the C preprocessor lines to prevent multiple\n" + "inclusions. These look like the following:\n" + "#ifndef MY_HEADER_H\n" + "#define MY_HEADER_H\n" + "...\n" + "#endif /* MY_HEADER_H */\n"; + +static char *report_idem(struct ccan_file *f, char *sofar) +{ + struct line_info *line_info; + unsigned int i, first_preproc_line; + const char *line, *sym; + + line_info = get_ccan_line_info(f); + if (f->num_lines < 3) + /* FIXME: We assume small headers probably uninteresting. */ + return sofar; + + 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:%u:expected #ifndef.\n", + f->name, first_preproc_line+1); + } + + line = f->lines[i]; + + /* We expect the condition to be ! IFDEF . */ + if (line_info[i].cond->type != PP_COND_IFDEF + || !line_info[i].cond->inverse) { + return talloc_asprintf_append(sofar, + "%s:%u:expected #ifndef.\n", + f->name, first_preproc_line+1); + } + + /* And this to be #define */ + 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; +} + +static void *check_idempotent(struct manifest *m) +{ + struct ccan_file *f; + char *report = NULL; + + list_for_each(&m->h_files, f, list) + report = report_idem(f, report); + + return report; +} + +static const char *describe_idempotent(struct manifest *m, void *check_result) +{ + return talloc_asprintf(check_result, + "Some headers not idempotent:\n" + "%s\n%s", (char *)check_result, + explain); +} + +struct ccanlint idempotent = { + .name = "Headers are #ifndef/#define idempotent wrapped", + .total_score = 1, + .check = check_idempotent, + .describe = describe_idempotent, +};