idempotent.c 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. #include <tools/ccanlint/ccanlint.h>
  2. #include <tools/tools.h>
  3. #include <ccan/talloc/talloc.h>
  4. #include <ccan/str/str.h>
  5. #include <sys/types.h>
  6. #include <sys/stat.h>
  7. #include <fcntl.h>
  8. #include <unistd.h>
  9. #include <limits.h>
  10. #include <errno.h>
  11. #include <stdlib.h>
  12. #include <stdio.h>
  13. #include <err.h>
  14. #include <string.h>
  15. #include <ctype.h>
  16. static const char explain[]
  17. = "Headers usually start with the C preprocessor lines to prevent multiple\n"
  18. "inclusions. These look like the following:\n"
  19. "#ifndef MY_HEADER_H\n"
  20. "#define MY_HEADER_H\n"
  21. "...\n"
  22. "#endif /* MY_HEADER_H */\n";
  23. static char *report_idem(struct ccan_file *f, char *sofar)
  24. {
  25. struct line_info *line_info;
  26. unsigned int i, first_preproc_line;
  27. const char *line, *sym;
  28. line_info = get_ccan_line_info(f);
  29. if (f->num_lines < 3)
  30. /* FIXME: We assume small headers probably uninteresting. */
  31. return sofar;
  32. for (i = 0; i < f->num_lines; i++) {
  33. if (line_info[i].type == DOC_LINE
  34. || line_info[i].type == COMMENT_LINE)
  35. continue;
  36. if (line_info[i].type == CODE_LINE)
  37. return talloc_asprintf_append(sofar,
  38. "%s:%u:expect first non-comment line to be #ifndef.\n", f->name, i+1);
  39. else if (line_info[i].type == PREPROC_LINE)
  40. break;
  41. }
  42. /* No code at all? Don't complain. */
  43. if (i == f->num_lines)
  44. return sofar;
  45. first_preproc_line = i;
  46. for (i = first_preproc_line+1; i < f->num_lines; i++) {
  47. if (line_info[i].type == DOC_LINE
  48. || line_info[i].type == COMMENT_LINE)
  49. continue;
  50. if (line_info[i].type == CODE_LINE)
  51. return talloc_asprintf_append(sofar,
  52. "%s:%u:expect second line to be #define.\n", f->name, i+1);
  53. else if (line_info[i].type == PREPROC_LINE)
  54. break;
  55. }
  56. /* No code at all? Weird. */
  57. if (i == f->num_lines)
  58. return sofar;
  59. /* We expect a condition on this line. */
  60. if (!line_info[i].cond) {
  61. return talloc_asprintf_append(sofar,
  62. "%s:%u:expected #ifndef.\n",
  63. f->name, first_preproc_line+1);
  64. }
  65. line = f->lines[i];
  66. /* We expect the condition to be ! IFDEF <symbol>. */
  67. if (line_info[i].cond->type != PP_COND_IFDEF
  68. || !line_info[i].cond->inverse) {
  69. return talloc_asprintf_append(sofar,
  70. "%s:%u:expected #ifndef.\n",
  71. f->name, first_preproc_line+1);
  72. }
  73. /* And this to be #define <symbol> */
  74. if (!get_token(&line, "#"))
  75. abort();
  76. if (!get_token(&line, "define")) {
  77. return talloc_asprintf_append(sofar,
  78. "%s:%u:expected '#define %s'.\n",
  79. f->name, i+1, line_info[i].cond->symbol);
  80. }
  81. sym = get_symbol_token(f, &line);
  82. if (!sym || !streq(sym, line_info[i].cond->symbol)) {
  83. return talloc_asprintf_append(sofar,
  84. "%s:%u:expected '#define %s'.\n",
  85. f->name, i+1, line_info[i].cond->symbol);
  86. }
  87. /* Rest of code should all be covered by that conditional. */
  88. for (i++; i < f->num_lines; i++) {
  89. unsigned int val = 0;
  90. if (line_info[i].type == DOC_LINE
  91. || line_info[i].type == COMMENT_LINE)
  92. continue;
  93. if (get_ccan_line_pp(line_info[i].cond, sym, &val, NULL)
  94. != NOT_COMPILED)
  95. return talloc_asprintf_append(sofar,
  96. "%s:%u:code outside idempotent region.\n",
  97. f->name, i+1);
  98. }
  99. return sofar;
  100. }
  101. static void *check_idempotent(struct manifest *m)
  102. {
  103. struct ccan_file *f;
  104. char *report = NULL;
  105. list_for_each(&m->h_files, f, list)
  106. report = report_idem(f, report);
  107. return report;
  108. }
  109. static const char *describe_idempotent(struct manifest *m, void *check_result)
  110. {
  111. return talloc_asprintf(check_result,
  112. "Some headers not idempotent:\n"
  113. "%s\n%s", (char *)check_result,
  114. explain);
  115. }
  116. struct ccanlint idempotent = {
  117. .name = "Headers are #ifndef/#define idempotent wrapped",
  118. .total_score = 1,
  119. .check = check_idempotent,
  120. .describe = describe_idempotent,
  121. };
  122. REGISTER_TEST(idempotent, &trailing_whitespace, NULL);