aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAl Viro <viro@zeniv.linux.org.uk>2010-10-21 04:02:59 -0400
committerAl Viro <viro@zeniv.linux.org.uk>2012-04-13 20:55:34 -0400
commit519a65d26c4c1a9a72046ab4dcb594a64d19a662 (patch)
treeed75790f9bc147b3129c3a3132c83140b04aa7b1
parentd6b8d38f0ef16a1c24b62411e1aecfff86073f17 (diff)
downloadsparse-519a65d26c4c1a9a72046ab4dcb594a64d19a662.tar.gz
Fix ,##__VA_ARGS__ kludge
a) it actually allows any number of ##<arg>##... in between, as long as all those args are empty. b) it does *not* allow ## after __VA_ARGS__ - if it's there magic disappears Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
-rw-r--r--pre-process.c157
-rw-r--r--validation/preprocessor/preprocessor14.c1
-rw-r--r--validation/preprocessor/preprocessor23.c47
3 files changed, 167 insertions, 38 deletions
diff --git a/pre-process.c b/pre-process.c
index 8a16f8b3..f8cdf1f7 100644
--- a/pre-process.c
+++ b/pre-process.c
@@ -502,6 +502,27 @@ static struct token **copy(struct token **where, struct token *list, int *count)
return where;
}
+static int handle_kludge(struct token **p, struct arg *args)
+{
+ struct token *t = (*p)->next->next;
+ while (1) {
+ struct arg *v = &args[t->argnum];
+ if (token_type(t->next) != TOKEN_CONCAT) {
+ if (v->arg) {
+ /* ignore the first ## */
+ *p = (*p)->next;
+ return 0;
+ }
+ /* skip the entire thing */
+ *p = t;
+ return 1;
+ }
+ if (v->arg && !eof_token(v->arg))
+ return 0; /* no magic */
+ t = t->next->next;
+ }
+}
+
static struct token **substitute(struct token **list, struct token *body, struct arg *args)
{
struct token *token = *list;
@@ -513,6 +534,7 @@ static struct token **substitute(struct token **list, struct token *body, struct
for (; !eof_token(body); body = body->next, pos = &body->pos) {
struct token *added, *arg;
struct token **tail;
+ struct token *t;
switch (token_type(body)) {
case TOKEN_GNU_KLUDGE:
@@ -520,13 +542,20 @@ static struct token **substitute(struct token **list, struct token *body, struct
* GNU kludge: if we had <comma>##<vararg>, behaviour
* depends on whether we had enough arguments to have
* a vararg. If we did, ## is just ignored. Otherwise
- * both , and ## are ignored. Comma should come from
- * the body of macro and not be an argument of earlier
- * concatenation.
+ * both , and ## are ignored. Worse, there can be
+ * an arbitrary number of ##<arg> in between; if all of
+ * those are empty, we act as if they hadn't been there,
+ * otherwise we act as if the kludge didn't exist.
*/
- if (!args[body->next->argnum].arg)
+ t = body;
+ if (handle_kludge(&body, args)) {
+ if (state == Concat)
+ state = Normal;
+ else
+ state = Placeholder;
continue;
- added = dup_token(body, base_pos, pos);
+ }
+ added = dup_token(t, base_pos, pos);
token_type(added) = TOKEN_SPECIAL;
tail = &added->next;
break;
@@ -1035,6 +1064,10 @@ static int try_arg(struct token *token, enum token_type type, struct token *argl
}
if (n)
return count->vararg ? 2 : 1;
+ /*
+ * XXX - need saner handling of that
+ * (>= 1024 instances of argument)
+ */
token_type(token) = TOKEN_ERROR;
return -1;
}
@@ -1042,49 +1075,103 @@ static int try_arg(struct token *token, enum token_type type, struct token *argl
return 0;
}
+static struct token *handle_hash(struct token **p, struct token *arglist)
+{
+ struct token *token = *p;
+ if (arglist) {
+ struct token *next = token->next;
+ if (!try_arg(next, TOKEN_STR_ARGUMENT, arglist))
+ goto Equote;
+ next->pos.whitespace = token->pos.whitespace;
+ __free_token(token);
+ token = *p = next;
+ } else {
+ token->pos.noexpand = 1;
+ }
+ return token;
+
+Equote:
+ sparse_error(token->pos, "'#' is not followed by a macro parameter");
+ return NULL;
+}
+
+/* token->next is ## */
+static struct token *handle_hashhash(struct token *token, struct token *arglist)
+{
+ struct token *last = token;
+ struct token *concat;
+ int state = match_op(token, ',');
+
+ try_arg(token, TOKEN_QUOTED_ARGUMENT, arglist);
+
+ while (1) {
+ struct token *t;
+ int is_arg;
+
+ /* eat duplicate ## */
+ concat = token->next;
+ while (match_op(t = concat->next, SPECIAL_HASHHASH)) {
+ token->next = t;
+ __free_token(concat);
+ concat = t;
+ }
+ token_type(concat) = TOKEN_CONCAT;
+
+ if (eof_token(t))
+ goto Econcat;
+
+ if (match_op(t, '#')) {
+ t = handle_hash(&concat->next, arglist);
+ if (!t)
+ return NULL;
+ }
+
+ is_arg = try_arg(t, TOKEN_QUOTED_ARGUMENT, arglist);
+
+ if (state == 1 && is_arg) {
+ state = is_arg;
+ } else {
+ last = t;
+ state = match_op(t, ',');
+ }
+
+ token = t;
+ if (!match_op(token->next, SPECIAL_HASHHASH))
+ break;
+ }
+ /* handle GNU ,##__VA_ARGS__ kludge, in all its weirdness */
+ if (state == 2)
+ token_type(last) = TOKEN_GNU_KLUDGE;
+ return token;
+
+Econcat:
+ sparse_error(concat->pos, "'##' cannot appear at the ends of macro expansion");
+ return NULL;
+}
+
static struct token *parse_expansion(struct token *expansion, struct token *arglist, struct ident *name)
{
struct token *token = expansion;
struct token **p;
- struct token *last = NULL;
if (match_op(token, SPECIAL_HASHHASH))
goto Econcat;
for (p = &expansion; !eof_token(token); p = &token->next, token = *p) {
if (match_op(token, '#')) {
- if (arglist) {
- struct token *next = token->next;
- if (!try_arg(next, TOKEN_STR_ARGUMENT, arglist))
- goto Equote;
- next->pos.whitespace = token->pos.whitespace;
- token = *p = next;
- } else {
- token->pos.noexpand = 1;
- }
- } else if (match_op(token, SPECIAL_HASHHASH)) {
- struct token *next = token->next;
- int arg = try_arg(next, TOKEN_QUOTED_ARGUMENT, arglist);
- token_type(token) = TOKEN_CONCAT;
- if (arg) {
- token = next;
- /* GNU kludge */
- if (arg == 2 && last && match_op(last, ',')) {
- token_type(last) = TOKEN_GNU_KLUDGE;
- last->next = token;
- }
- } else if (match_op(next, SPECIAL_HASHHASH))
- token = next;
- else if (eof_token(next))
- goto Econcat;
- } else if (match_op(token->next, SPECIAL_HASHHASH)) {
- try_arg(token, TOKEN_QUOTED_ARGUMENT, arglist);
+ token = handle_hash(p, arglist);
+ if (!token)
+ return NULL;
+ }
+ if (match_op(token->next, SPECIAL_HASHHASH)) {
+ token = handle_hashhash(token, arglist);
+ if (!token)
+ return NULL;
} else {
try_arg(token, TOKEN_MACRO_ARGUMENT, arglist);
}
if (token_type(token) == TOKEN_ERROR)
goto Earg;
- last = token;
}
token = alloc_token(&expansion->pos);
token_type(token) = TOKEN_UNTAINT;
@@ -1093,10 +1180,6 @@ static struct token *parse_expansion(struct token *expansion, struct token *argl
*p = token;
return expansion;
-Equote:
- sparse_error(token->pos, "'#' is not followed by a macro parameter");
- return NULL;
-
Econcat:
sparse_error(token->pos, "'##' cannot appear at the ends of macro expansion");
return NULL;
diff --git a/validation/preprocessor/preprocessor14.c b/validation/preprocessor/preprocessor14.c
index 05fc248b..027af040 100644
--- a/validation/preprocessor/preprocessor14.c
+++ b/validation/preprocessor/preprocessor14.c
@@ -7,7 +7,6 @@ A(,1)
B(,1)
/*
* check-name: Preprocessor #14
- * check-known-to-fail
* check-command: sparse -E $file
*
* check-output-start
diff --git a/validation/preprocessor/preprocessor23.c b/validation/preprocessor/preprocessor23.c
new file mode 100644
index 00000000..25be5085
--- /dev/null
+++ b/validation/preprocessor/preprocessor23.c
@@ -0,0 +1,47 @@
+#define H(x,...) ,##x##__VA_ARGS__##,##__VA_ARGS__
+H()
+H(x)
+H(,)
+H(x,)
+H(,x)
+H(x,x)
+#define I(x,...) ,##x##__VA_ARGS__
+I()
+I(x)
+I(,)
+I(x,)
+I(,x)
+I(x,x)
+/*
+ * check-name: Preprocessor #23
+ * check-command: sparse -E $file
+ *
+ * check-output-start
+
+,
+,x
+,,
+,x,
+,x,x
+,xx,x
+,x
+,
+,x
+,x
+,xx
+ * check-output-end
+ *
+ * check-error-start
+preprocessor/preprocessor23.c:3:1: error: '##' failed: concatenation is not a valid token
+preprocessor/preprocessor23.c:4:1: error: '##' failed: concatenation is not a valid token
+preprocessor/preprocessor23.c:5:1: error: '##' failed: concatenation is not a valid token
+preprocessor/preprocessor23.c:5:1: error: '##' failed: concatenation is not a valid token
+preprocessor/preprocessor23.c:6:1: error: '##' failed: concatenation is not a valid token
+preprocessor/preprocessor23.c:6:1: error: '##' failed: concatenation is not a valid token
+preprocessor/preprocessor23.c:7:1: error: '##' failed: concatenation is not a valid token
+preprocessor/preprocessor23.c:7:1: error: '##' failed: concatenation is not a valid token
+preprocessor/preprocessor23.c:10:1: error: '##' failed: concatenation is not a valid token
+preprocessor/preprocessor23.c:12:1: error: '##' failed: concatenation is not a valid token
+preprocessor/preprocessor23.c:14:1: error: '##' failed: concatenation is not a valid token
+ * check-error-end
+ */