From 5c2338f694cda83c28a96e198f8de13bc1cde8b4 Mon Sep 17 00:00:00 2001 From: Luc Van Oostenryck Date: Thu, 12 Mar 2020 08:07:05 +0100 Subject: cpp: silently allow conditional directives within a macro The presence of preprocessor directives within the arguments of a macro invocation is Undefined Behaviour [6.10.3p11]. However, conditional directives are harmless here and are useful (and commonly used in the kernel). So, relax the warning by restricting it to non-conditional directives. Signed-off-by: Luc Van Oostenryck --- pre-process.c | 17 ++++++++-- validation/preprocessor/directive-within-macro.c | 40 ++++++++++++++++++++++++ validation/preprocessor/preprocessor22.c | 2 +- 3 files changed, 55 insertions(+), 4 deletions(-) create mode 100644 validation/preprocessor/directive-within-macro.c diff --git a/pre-process.c b/pre-process.c index 82bfa402..a5a3c5fb 100644 --- a/pre-process.c +++ b/pre-process.c @@ -48,6 +48,7 @@ static struct ident_list *macros; // only needed for -dD static int false_nesting = 0; static int counter_macro = 0; // __COUNTER__ expansion static int include_level = 0; +static int expanding = 0; #define INCLUDEPATHS 300 const char *includepath[INCLUDEPATHS+1] = { @@ -232,8 +233,13 @@ static int expand_one_symbol(struct token **list) sym->expander(token); return 1; } else { + int rc; + sym->used_in = file_scope; - return expand(list, sym); + expanding = 1; + rc = expand(list, sym); + expanding = 0; + return rc; } } @@ -271,8 +277,6 @@ static struct token *collect_arg(struct token *prev, int vararg, struct position while (!eof_token(next = scan_next(p))) { if (next->pos.newline && match_op(next, '#')) { if (!next->pos.noexpand) { - warning(next->pos, - "directive in macro's argument list"); preprocessor_line(stream, p); __free_token(next); /* Free the '#' token */ continue; @@ -2073,6 +2077,7 @@ static void handle_preprocessor_line(struct stream *stream, struct token **line, int (*handler)(struct stream *, struct token **, struct token *); struct token *token = start->next; int is_normal = 1; + int is_cond = 0; // is one of {is,ifdef,ifndef,elif,else,endif} if (eof_token(token)) return; @@ -2082,6 +2087,7 @@ static void handle_preprocessor_line(struct stream *stream, struct token **line, if (sym) { handler = sym->handler; is_normal = sym->normal; + is_cond = !sym->normal; } else { handler = handle_nondirective; } @@ -2096,6 +2102,11 @@ static void handle_preprocessor_line(struct stream *stream, struct token **line, if (false_nesting) goto out; } + + if (expanding) { + if (!is_cond || Wpedantic) + warning(start->pos, "directive in macro's argument list"); + } if (!handler(stream, line, token)) /* all set */ return; diff --git a/validation/preprocessor/directive-within-macro.c b/validation/preprocessor/directive-within-macro.c new file mode 100644 index 00000000..5269d4a7 --- /dev/null +++ b/validation/preprocessor/directive-within-macro.c @@ -0,0 +1,40 @@ +#define f(x) x + +f(1 +#if 1 // OK + a +#elif 2 // OK + b +#else // OK + c +#endif // OK +#ifdef f // OK + d +#endif // OK +#ifndef f // OK + e +#endif // OK + 3) + +f(1 +#define x y // KO + 3) + +/* + * check-name: directive-within-macro + * check-command: sparse -E $file + * + * check-output-start + +1 +a +d +3 +1 +3 + * check-output-end + * + * check-error-start +preprocessor/directive-within-macro.c:20:1: warning: directive in macro's argument list + * check-error-end + */ diff --git a/validation/preprocessor/preprocessor22.c b/validation/preprocessor/preprocessor22.c index 277334c6..11f625c0 100644 --- a/validation/preprocessor/preprocessor22.c +++ b/validation/preprocessor/preprocessor22.c @@ -17,7 +17,7 @@ define_struct(a, { * check-description: Directives are not allowed within a macro argument list, * although cpp deals with it to treat macro more like C functions. * - * check-command: sparse -E $file + * check-command: sparse -pedantic -E $file * * check-error-start preprocessor/preprocessor22.c:6:1: warning: directive in macro's argument list -- cgit 1.2.3-korg