diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2010-10-21 04:02:59 -0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2012-04-13 20:55:34 -0400 |
commit | 519a65d26c4c1a9a72046ab4dcb594a64d19a662 (patch) | |
tree | ed75790f9bc147b3129c3a3132c83140b04aa7b1 | |
parent | d6b8d38f0ef16a1c24b62411e1aecfff86073f17 (diff) | |
download | sparse-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.c | 157 | ||||
-rw-r--r-- | validation/preprocessor/preprocessor14.c | 1 | ||||
-rw-r--r-- | validation/preprocessor/preprocessor23.c | 47 |
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 + */ |