Browse Source

ccanlint: yet more modifications to example extraction and compile.

Now (with fixes) every ccanlint example compiles!
Rusty Russell 15 years ago
parent
commit
ab4a6fd9a3
2 changed files with 66 additions and 48 deletions
  1. 56 42
      tools/ccanlint/tests/examples_compile.c
  2. 10 6
      tools/ccanlint/tests/has_examples.c

+ 56 - 42
tools/ccanlint/tests/examples_compile.c

@@ -37,6 +37,10 @@ static char *add_dep(const struct manifest *m, char *list, const char *mod)
 	char **deps, *obj;
 	unsigned int i;
 
+	/* Not ourselves. */
+	if (streq(m->basename, mod))
+		return list;
+
 	/* Not if there's no object file for that module */
 	if (!expect_obj_file(talloc_asprintf(list, "%s/ccan/%s", ccan_dir,mod)))
 		return list;
@@ -87,9 +91,6 @@ static char *obj_list(const struct manifest *m, struct ccan_file *f)
 			mod = *lines + preflen + strlen("#include <ccan/");
 			modlen = strcspn(mod, "/");
 			mod = talloc_strndup(f, mod, modlen);
-			/* Not ourselves. */
-			if (streq(m->basename, mod))
-				continue;
 			list = add_dep(m, list, mod);
 		}
 	}
@@ -179,35 +180,53 @@ static void strip_leading_whitespace(char **lines)
 static bool looks_internal(char **lines)
 {
 	unsigned int i;
+	bool last_ended = true; /* Did last line finish a statement? */
 
 	for (i = 0; lines[i]; i++) {
-		unsigned len = strspn(lines[i], IDENT_CHARS);
+		/* Skip leading whitespace. */
+		const char *line = lines[i] + strspn(lines[i], " \t");
+		unsigned len = strspn(line, IDENT_CHARS);
+
+		if (!line[0] || isblank(line[0]) || strstarts(line, "//"))
+			continue;
 
 		/* The winners. */
-		if (strstarts(lines[i], "if") && len == 2)
+		if (strstarts(line, "if") && len == 2)
 			return true;
-		if (strstarts(lines[i], "for") && len == 3)
+		if (strstarts(line, "for") && len == 3)
 			return true;
-		if (strstarts(lines[i], "while") && len == 5)
+		if (strstarts(line, "while") && len == 5)
 			return true;
-		if (strstarts(lines[i], "do") && len == 2)
+		if (strstarts(line, "do") && len == 2)
 			return true;
 
 		/* The losers. */
-		if (strchr(lines[i], '(')) {
-			if (strstarts(lines[i], "static"))
+		if (strstarts(line, "#include"))
+			return false;
+
+		if (last_ended && strchr(line, '(')) {
+			if (strstarts(line, "static"))
 				return false;
-			if (strends(lines[i], ")"))
+			if (strends(line, ")"))
 				return false;
 		}
+
+		/* Single identifier then operator == inside function. */
+		if (last_ended && len
+		    && ispunct(line[len+strspn(line+len, " ")]))
+			return true;
+
+		last_ended = (strends(line, "}")
+			      || strends(line, ";")
+			      || streq(line, "..."));
 	}
 
-	/* No idea... Say no? */
-	return false;
+	/* No idea... Say yes? */
+	return true;
 }
 
 /* Examples will often build on prior ones.  Try combining them. */
-static char **combine(char **lines, char **prev)
+static char **combine(const void *ctx, char **lines, char **prev)
 {
 	unsigned int i, lines_total, prev_total, count;
 	char **ret;
@@ -215,8 +234,6 @@ static char **combine(char **lines, char **prev)
 	if (!prev)
 		return NULL;
 
-	strip_leading_whitespace(lines);
-
 	/* If it looks internal, put prev at start. */
 	if (looks_internal(lines)) {
 		count = 0;
@@ -239,7 +256,7 @@ static char **combine(char **lines, char **prev)
 	for (i = 0; prev[i]; i++);
 	prev_total = i;
 
-	ret = talloc_array(lines, char *, lines_total + prev_total + 1);
+	ret = talloc_array(ctx, char *, lines_total + prev_total + 1);
 	memcpy(ret, lines, count * sizeof(ret[0]));
 	memcpy(ret + count, prev, prev_total * sizeof(ret[0]));
 	memcpy(ret + count + prev_total, lines + count,
@@ -272,10 +289,11 @@ static char *mangle(struct manifest *m, char **lines)
 				     m->basename, m->basename);
 
 	ret = talloc_asprintf_append(ret, "/* Useful dummy functions. */\n"
-				     "int somefunc(void);\n"
-				     "int somefunc(void) { return 0; }\n");
+				     "extern int somefunc(void);\n"
+				     "int somefunc(void) { return 0; }\n"
+				     "extern char somestring[];\n"
+				     "char somestring[] = \"hello world\";\n");
 
-	strip_leading_whitespace(lines);
 	if (looks_internal(lines)) {
 		/* Wrap it all in main(). */
 		ret = start_main(ret);
@@ -306,12 +324,10 @@ static char *mangle(struct manifest *m, char **lines)
 				}
 			}
 		}
-		/* ... means elided code.  If followed by spaced line, means
-		 * next part is supposed to be inside a function. */
+		/* ... means elided code. */
 		if (strcmp(lines[i], "...") == 0) {
-			if (!in_function
-			    && lines[i+1]
-			    && isblank(lines[i+1][0])) {
+			if (!in_function && !has_main
+			    && looks_internal(lines + i + 1)) {
 				/* This implies we start a function here. */
 				ret = start_main(ret);
 				has_main = true;
@@ -394,6 +410,8 @@ static void *build_examples(struct manifest *m, bool keep,
 		char *ret;
 
 		examples_compile.total_score++;
+		/* Simplify our dumb parsing. */
+		strip_leading_whitespace(get_ccan_file_lines(i));
 		ret = compile(score, m, i, keep);
 		if (!ret) {
 			prev = get_ccan_file_lines(i);
@@ -401,33 +419,30 @@ static void *build_examples(struct manifest *m, bool keep,
 			continue;
 		}
 
-		talloc_free(ret);
-		mangle = mangle_example(m, i, get_ccan_file_lines(i), keep);
-		ret = compile(score, m, mangle, keep);
-		if (!ret) {
-			prev = get_ccan_file_lines(i);
-			score->score++;
-			continue;
-		}
-
 		/* Try combining with previous (successful) example... */
 		if (prev) {
-			prev = combine(get_ccan_file_lines(i), prev);
+			char **new = combine(i, get_ccan_file_lines(i), prev);
 			talloc_free(ret);
 
-			/* We're going to replace this failure. */
-			if (keep)
-				unlink(mangle->fullname);
-			talloc_free(mangle);
-
-			mangle = mangle_example(m, i, prev, keep);
+			mangle = mangle_example(m, i, new, keep);
 			ret = compile(score, m, mangle, keep);
 			if (!ret) {
+				prev = new;
 				score->score++;
 				continue;
 			}
 		}
 
+		/* Try standalone. */
+		talloc_free(ret);
+		mangle = mangle_example(m, i, get_ccan_file_lines(i), keep);
+		ret = compile(score, m, mangle, keep);
+		if (!ret) {
+			prev = get_ccan_file_lines(i);
+			score->score++;
+			continue;
+		}
+
 		if (!score->errors)
 			score->errors = ret;
 		else {
@@ -436,7 +451,6 @@ static void *build_examples(struct manifest *m, bool keep,
 			talloc_free(ret);
 		}
 		/* This didn't work, so not a candidate for combining. */
-		talloc_free(prev);
 		prev = NULL;
 	}
 	return score;

+ 10 - 6
tools/ccanlint/tests/has_examples.c

@@ -24,12 +24,16 @@ static char *add_example(struct manifest *m, struct ccan_file *source,
 	int fd;
 	struct ccan_file *f;
 
-	name = maybe_temp_file(m, ".c", keep, 
-			       talloc_asprintf(m, "%s/example-%s-%s.c",
-					       talloc_dirname(m,
-							      source->fullname),
-					       source->name,
-					       example->function));
+	name = talloc_asprintf(m, "%s/example-%s-%s.c",
+			       talloc_dirname(m,
+					      source->fullname),
+			       source->name,
+			       example->function);
+	/* example->function == 'struct foo' */
+	while (strchr(name, ' '))
+		*strchr(name, ' ') = '_';
+
+	name = maybe_temp_file(m, ".c", keep, name);
 	f = new_ccan_file(m, talloc_dirname(m, name), talloc_basename(m, name));
 	talloc_steal(f, name);
 	list_add_tail(&m->examples, &f->list);