ccanlint: check for #ifdef
authorRusty Russell <rusty@rustcorp.com.au>
Tue, 1 Mar 2011 07:20:32 +0000 (17:50 +1030)
committerRusty Russell <rusty@rustcorp.com.au>
Tue, 1 Mar 2011 07:20:32 +0000 (17:50 +1030)
Old habits die hard; it's better to use #if <FEATURE> than #ifdef <FEATURE>;
they're similar, because undefined identifiers evaluate to zero, but with
GCC's -Wundef flag you can detect mis-spelled or missing features with
#if.

autoconf-style config.h leave unset features undefined, so this works for
those config.h too.

tools/ccanlint/tests/hash_if.c [new file with mode: 0644]

diff --git a/tools/ccanlint/tests/hash_if.c b/tools/ccanlint/tests/hash_if.c
new file mode 100644 (file)
index 0000000..3eefd1f
--- /dev/null
@@ -0,0 +1,75 @@
+#include <tools/ccanlint/ccanlint.h>
+#include <tools/tools.h>
+#include <ccan/talloc/talloc.h>
+#include <ccan/str/str.h>
+#include <ccan/str_talloc/str_talloc.h>
+#include <ccan/foreach/foreach.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <limits.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <err.h>
+#include <string.h>
+#include <ctype.h>
+
+static void check_hash_if(struct manifest *m,
+                                  bool keep,
+                                  unsigned int *timeleft, struct score *score)
+{
+       struct list_head *list;
+       const char *explanation =
+       "\n\t(#if works like #ifdef, but with gcc's -Wundef, we can detect\n"
+       "\tmistyped or unknown configuration options)";
+
+       foreach_ptr(list, &m->c_files, &m->h_files,
+                   &m->run_tests, &m->api_tests,
+                   &m->compile_ok_tests, &m->compile_fail_tests,
+                   &m->other_test_c_files) {
+               struct ccan_file *f;
+
+               list_for_each(list, f, list) {
+                       unsigned int i;
+                       char **lines = get_ccan_file_lines(f);
+
+                       for (i = 0; lines[i]; i++) {
+                               const char *line = lines[i];
+                               char *sym;
+
+                               if (!get_token(&line, "#"))
+                                       continue;
+                               if (!(get_token(&line, "if")
+                                     && get_token(&line, "defined")
+                                     && get_token(&line, "("))
+                                   && !get_token(&line, "ifdef"))
+                                       continue;
+
+                               sym = get_symbol_token(lines, &line);
+                               if (!sym || !strstarts(sym, "HAVE_"))
+                                       continue;
+                               score_file_error(score, f, i+1,
+                                                "%s should be tested with #if"
+                                                "%s",
+                                                sym, explanation);
+                               explanation = "";
+                       }
+               }
+       }
+
+       if (!score->error) {
+               score->pass = true;
+               score->score = score->total;
+       }
+}
+
+struct ccanlint hash_if = {
+       .key = "hash_if",
+       .name = "Features are checked with #if not #ifdef",
+       .check = check_hash_if,
+       .needs = ""
+};
+
+REGISTER_TEST(hash_if);