diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2005-11-27 02:33:00 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2005-11-27 17:40:51 -0800 |
commit | eac4d539b83ef7a917ed74a4fbf49033de88206d (patch) | |
tree | 1913de4b156014bc9c5aa254619147c9168027f1 /pre-process.c | |
parent | [PATCH] fixed handling of out-of-place #elif/#else/#endif (diff) | |
download | sparse-eac4d539b83ef7a917ed74a4fbf49033de88206d.tar.gz sparse-eac4d539b83ef7a917ed74a4fbf49033de88206d.tar.bz2 sparse-eac4d539b83ef7a917ed74a4fbf49033de88206d.zip |
[PATCH] fixed stream->protect handling
New mechanism: we introduce new fields of struct stream - ifndef and
dirty. The former is the nesting level of ifndef that currently
protects us, or 0 if there's none. The latter is a boolean - "do we
ignore new non-empty stuff shows up right now?".
There are five states, more or less matching the old ->constant ones:
1) nothing - dirty = 0, ->protect = NULL, ->ifndef = 0.
2) candidate - dirty = 0, ->protect = <ident>, ->ifndef > 0.
3) checking - dirty = 0, ->protect = <ident>, ->ifndef = 0.
4) maybe - dirty = 1, ->protect = <ident>, ->ifndef > 0.
5) not - dirty = 1, ->protect = NULL, ->ifndef = 0.
Rules:
- any directive except #ifdef, #ifndef, #else, #endif, #<newline> is
considered dirty.
- any non-directive (#<not a directive name>...) is considered dirty
- any tokens in lines that do not begin with # are considered dirty
- #if[n]def with syntax errors is considered dirty.
- any nesting errors => stream is non-constant.
- any dirty material not under ifndef => stream is non-constant.
- the outermost ifndef at the first time we run into dirty material
will be the candidate for stream protector.
- any subsequent dirty material not under ifndef <protector> => stream
is non-constant.
We start in "nothing". Transitions:
- "nothing": dirty material => "not", see ifndef => "candidate"
- "candidate": dirty material => "maybe", leave that ifndef => "nothing"
- "maybe": dirty material => "maybe", leave that ifndef => "checking"
- "checking": dirty material => "not", same ifndef => "maybe
- "not": they check in, but they don't check out...
Seeing a nesting error (#else/#elif/#endif out of place/order or
unclosed #if... in the end) gets us to "not", no matter which state we
were in.
That's it. Implementation is actually pretty simple and
straightforward; it's less scary than the description above.
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'pre-process.c')
-rw-r--r-- | pre-process.c | 90 |
1 files changed, 46 insertions, 44 deletions
diff --git a/pre-process.c b/pre-process.c index 693cdf3..47ef7ae 100644 --- a/pre-process.c +++ b/pre-process.c @@ -49,14 +49,28 @@ static const char **quote_includepath = includepath; static const char **angle_includepath = includepath + 1; static const char **sys_includepath = includepath + 1; -#define MARK_STREAM_NONCONST(pos) do { \ - if (stream->constant != CONSTANT_FILE_NOPE) { \ - if (0) \ - info(pos, "%s triggers non-const", __func__); \ - stream->constant = CONSTANT_FILE_NOPE; \ - } \ -} while (0) - +#define dirty_stream(stream) \ + do if (!stream->dirty) { \ + stream->dirty = 1; \ + if (!stream->ifndef) \ + stream->protect = NULL; \ + } while(0) + +#define end_group(stream) \ + do if (stream->ifndef == if_nesting) { \ + stream->ifndef = 0; \ + if (!stream->dirty) \ + stream->protect = NULL; \ + else if (stream->protect) \ + stream->dirty = 0; \ + } while(0) + +#define nesting_error(stream) \ + do { \ + stream->dirty = 1; \ + stream->ifndef = 0; \ + stream->protect = NULL; \ + } while(0) static struct token *alloc_token(struct position *pos) { @@ -615,7 +629,7 @@ static int already_tokenized(const char *path) continue; if (strcmp(path, s->name)) continue; - if (!lookup_macro(s->protect)) + if (s->protect && !lookup_macro(s->protect)) continue; return 1; } @@ -1156,6 +1170,7 @@ static int handle_ifdef(struct stream *stream, struct token **line, struct token if (token_type(next) == TOKEN_IDENT) { arg = token_defined(next); } else { + dirty_stream(stream); if (!false_nesting) sparse_error(token->pos, "expected preprocessor identifier"); arg = -1; @@ -1168,17 +1183,18 @@ static int handle_ifndef(struct stream *stream, struct token **line, struct toke struct token *next = token->next; int arg; if (token_type(next) == TOKEN_IDENT) { - if (stream->constant == CONSTANT_FILE_MAYBE) { - if (!stream->protect || stream->protect == next->ident) { - stream->constant = CONSTANT_FILE_IFNDEF; + if (!stream->dirty && !stream->ifndef) { + if (!stream->protect) { + stream->ifndef = if_nesting + 1; stream->protect = next->ident; - } else - MARK_STREAM_NONCONST(token->pos); + } else if (stream->protect == next->ident) { + stream->ifndef = if_nesting + 1; + stream->dirty = 1; + } } arg = !token_defined(next); } else { - if (stream->constant == CONSTANT_FILE_MAYBE) - MARK_STREAM_NONCONST(token->pos); + dirty_stream(stream); if (!false_nesting) sparse_error(token->pos, "expected preprocessor identifier"); arg = -1; @@ -1255,30 +1271,27 @@ static int handle_if(struct stream *stream, struct token **line, struct token *t if (!false_nesting) value = expression_value(&token->next); - // This is an approximation. We really only need this if the - // condition does depends on a pre-processor symbol. Note, that - // the important #ifndef case has already changed ->constant. - if (stream->constant == CONSTANT_FILE_MAYBE) - MARK_STREAM_NONCONST(token->pos); - + dirty_stream(stream); return preprocessor_if(token, value); } static int handle_elif(struct stream * stream, struct token **line, struct token *token) { - if (stream->nesting == if_nesting) - MARK_STREAM_NONCONST(token->pos); + end_group(stream); if (stream->nesting > if_nesting) { + nesting_error(stream); sparse_error(token->pos, "unmatched #elif within stream"); return 1; } if (elif_ignore[if_nesting-1] & ELIF_SEEN_ELSE) { + nesting_error(stream); sparse_error(token->pos, "#elif after #else"); return 1; } + dirty_stream(stream); if (false_nesting) { /* If this whole if-thing is if'ed out, an elif cannot help */ if (elif_ignore[if_nesting-1] & ELIF_IGNORE) @@ -1295,15 +1308,16 @@ static int handle_elif(struct stream * stream, struct token **line, struct token static int handle_else(struct stream *stream, struct token **line, struct token *token) { - if (stream->nesting == if_nesting) - MARK_STREAM_NONCONST(token->pos); + end_group(stream); if (stream->nesting > if_nesting) { + nesting_error(stream); sparse_error(token->pos, "unmatched #else within stream"); return 1; } if (elif_ignore[if_nesting-1] & ELIF_SEEN_ELSE) { + nesting_error(stream); sparse_error(token->pos, "#else after #else"); return 1; } @@ -1324,10 +1338,9 @@ static int handle_else(struct stream *stream, struct token **line, struct token static int handle_endif(struct stream *stream, struct token **line, struct token *token) { - if (stream->constant == CONSTANT_FILE_IFNDEF && stream->nesting == if_nesting) - stream->constant = CONSTANT_FILE_MAYBE; - + end_group(stream); if (stream->nesting > if_nesting) { + nesting_error(stream); sparse_error(token->pos, "unmatched #endif in stream"); return 1; } @@ -1611,10 +1624,9 @@ static void handle_preprocessor_line(struct stream *stream, struct token **line, } if (is_normal) { + dirty_stream(stream); if (false_nesting) goto out; - if (stream->constant == CONSTANT_FILE_MAYBE) - MARK_STREAM_NONCONST(token->pos); } if (!handler(stream, line, token)) /* all set */ return; @@ -1657,15 +1669,13 @@ static void do_preprocess(struct token **list) switch (token_type(next)) { case TOKEN_STREAMEND: if (stream->nesting < if_nesting + 1) { + nesting_error(stream); sparse_error(unmatched_if_pos, "unterminated preprocessor conditional"); - // Pretend to see a series of #endifs - MARK_STREAM_NONCONST(next->pos); if_nesting = stream->nesting - 1; false_nesting = 0; } - if (stream->constant == CONSTANT_FILE_MAYBE && stream->protect) { + if (!stream->dirty) stream->constant = CONSTANT_FILE_YES; - } *list = next->next; continue; case TOKEN_STREAMBEGIN: @@ -1674,6 +1684,7 @@ static void do_preprocess(struct token **list) continue; default: + dirty_stream(stream); if (false_nesting) { *list = next->next; __free_token(next); @@ -1684,15 +1695,6 @@ static void do_preprocess(struct token **list) expand_one_symbol(list)) list = &next->next; } - - if (stream->constant == CONSTANT_FILE_MAYBE) { - /* - * Any token expansion (even if it ended up being an - * empty expansion) in this stream implies it can't - * be constant. - */ - MARK_STREAM_NONCONST(next->pos); - } } } |