aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAl Viro <viro@zeniv.linux.org.uk>2005-11-27 02:33:00 -0500
committerLinus Torvalds <torvalds@g5.osdl.org>2005-11-27 17:40:51 -0800
commiteac4d539b83ef7a917ed74a4fbf49033de88206d (patch)
tree1913de4b156014bc9c5aa254619147c9168027f1 /pre-process.c
parent[PATCH] fixed handling of out-of-place #elif/#else/#endif (diff)
downloadsparse-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.c90
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);
- }
}
}