A statement_base_t is enough space for break and continue statements.
[cparser] / parser.c
index 628ac66..7d9b334 100644 (file)
--- a/parser.c
+++ b/parser.c
@@ -50,6 +50,8 @@ static declaration_t      *last_declaration  = NULL;
 static declaration_t      *current_function  = NULL;
 static switch_statement_t *current_switch    = NULL;
 static statement_t        *current_loop      = NULL;
+static goto_statement_t   *goto_first        = NULL;
+static goto_statement_t   *goto_last         = NULL;
 static struct obstack  temp_obst;
 
 /** The current source position. */
@@ -461,14 +463,6 @@ static void eat_paren(void)
     }                                              \
     next_token();
 
-#define expect_fail(expected)                      \
-    if(UNLIKELY(token.type != (expected))) {       \
-        parse_error_expected(NULL, (expected), 0); \
-        eat_statement();                           \
-        goto fail;                                 \
-    }                                              \
-    next_token();
-
 #define expect_block(expected)                     \
     if(UNLIKELY(token.type != (expected))) {       \
         parse_error_expected(NULL, (expected), 0); \
@@ -2694,6 +2688,31 @@ static void parse_kr_declaration_list(declaration_t *declaration)
        declaration->type = type;
 }
 
+/**
+ * Check if all labels are defined in the current function.
+ */
+static void check_for_missing_labels(void)
+{
+       bool first_err = true;
+       for (const goto_statement_t *goto_statement = goto_first;
+            goto_statement != NULL;
+            goto_statement = goto_statement->next) {
+                const declaration_t *label = goto_statement->label;
+
+                if (label->source_position.input_name == NULL) {
+                        if (first_err) {
+                                first_err = false;
+                                diagnosticf("%s: In function '%Y':\n",
+                                        current_function->source_position.input_name,
+                                        current_function->symbol);
+                        }
+                        errorf(goto_statement->statement.source_position,
+                                "label '%Y' used but not defined", label->symbol);
+                }
+       }
+       goto_first = goto_last = NULL;
+}
+
 static void parse_external_declaration(void)
 {
        /* function-definitions and declarations both start with declaration
@@ -2787,6 +2806,7 @@ static void parse_external_declaration(void)
                current_function                    = declaration;
 
                declaration->init.statement = parse_compound_statement();
+               check_for_missing_labels();
 
                assert(current_function == declaration);
                current_function = old_current_function;
@@ -4858,23 +4878,44 @@ static statement_t *parse_case_statement(void)
 
        expect(':');
 
-       if (current_switch != NULL) {
-               /* link all cases into the switch statement */
-               if (current_switch->last_case == NULL) {
-                       current_switch->first_case =
-                       current_switch->last_case  = &statement->case_label;
+       if (! is_constant_expression(statement->case_label.expression)) {
+               errorf(statement->base.source_position,
+                       "case label does not reduce to an integer constant");
+       } else {
+               /* TODO: check if the case label is already known */
+               if (current_switch != NULL) {
+                       /* link all cases into the switch statement */
+                       if (current_switch->last_case == NULL) {
+                               current_switch->first_case =
+                               current_switch->last_case  = &statement->case_label;
+                       } else {
+                               current_switch->last_case->next = &statement->case_label;
+                       }
                } else {
-                       current_switch->last_case->next = &statement->case_label;
+                       errorf(statement->base.source_position,
+                               "case label not within a switch statement");
                }
-       } else {
-               errorf(statement->base.source_position,
-                       "case label not within a switch statement");
        }
        statement->case_label.label_statement = parse_statement();
 
        return statement;
 }
 
+/**
+ * Finds an existing default label of a switch statement.
+ */
+static case_label_statement_t *
+find_default_label(const switch_statement_t *statement)
+{
+       for (case_label_statement_t *label = statement->first_case;
+            label != NULL;
+                label = label->next) {
+               if (label->expression == NULL)
+                       return label;
+       }
+       return NULL;
+}
+
 /**
  * Parse a default statement.
  */
@@ -4887,6 +4928,25 @@ static statement_t *parse_default_statement(void)
        statement->base.source_position = token.source_position;
 
        expect(':');
+       if (current_switch != NULL) {
+               const case_label_statement_t *def_label = find_default_label(current_switch);
+               if (def_label != NULL) {
+                       errorf(HERE, "multiple default labels in one switch");
+                       errorf(def_label->statement.source_position,
+                               "this is the first default label");
+               } else {
+                       /* link all cases into the switch statement */
+                       if (current_switch->last_case == NULL) {
+                               current_switch->first_case =
+                                       current_switch->last_case  = &statement->case_label;
+                       } else {
+                               current_switch->last_case->next = &statement->case_label;
+                       }
+               }
+       } else {
+               errorf(statement->base.source_position,
+                       "'default' label not within a switch statement");
+       }
        statement->label.label_statement = parse_statement();
 
        return statement;
@@ -5005,6 +5065,15 @@ static statement_t *parse_switch(void)
        return (statement_t*) statement;
 }
 
+static statement_t *parse_loop_body(statement_t *const loop)
+{
+       statement_t *const rem = current_loop;
+       current_loop = loop;
+       statement_t *const body = parse_statement();
+       current_loop = rem;
+       return body;
+}
+
 /**
  * Parse a while statement.
  */
@@ -5016,18 +5085,13 @@ static statement_t *parse_while(void)
        statement->statement.kind            = STATEMENT_WHILE;
        statement->statement.source_position = token.source_position;
 
-       statement_t *rem = current_loop;
-       expect_fail('(');
+       expect('(');
        statement->condition = parse_expression();
-       expect_fail(')');
+       expect(')');
 
-       statement->body = parse_statement();
-       current_loop = rem;
+       statement->body = parse_loop_body((statement_t*)statement);
 
        return (statement_t*) statement;
-fail:
-       current_loop = rem;
-       return NULL;
 }
 
 /**
@@ -5041,19 +5105,14 @@ static statement_t *parse_do(void)
        statement->statement.kind            = STATEMENT_DO_WHILE;
        statement->statement.source_position = token.source_position;
 
-       statement_t *rem = current_loop;
-       statement->body = parse_statement();
-       expect_fail(T_while);
-       expect_fail('(');
+       statement->body = parse_loop_body((statement_t*)statement);
+       expect(T_while);
+       expect('(');
        statement->condition = parse_expression();
-       expect_fail(')');
-       current_loop = rem;
+       expect(')');
        expect(';');
 
        return (statement_t*) statement;
-fail:
-       current_loop = rem;
-       return NULL;
 }
 
 /**
@@ -5069,7 +5128,6 @@ static statement_t *parse_for(void)
 
        expect('(');
 
-       statement_t *rem = current_loop;
        int         top          = environment_top();
        context_t  *last_context = context;
        set_context(&statement->context);
@@ -5079,31 +5137,27 @@ static statement_t *parse_for(void)
                        parse_declaration(record_declaration);
                } else {
                        statement->initialisation = parse_expression();
-                       expect_fail(';');
+                       expect(';');
                }
        } else {
-               expect_fail(';');
+               expect(';');
        }
 
        if(token.type != ';') {
                statement->condition = parse_expression();
        }
-       expect_fail(';');
+       expect(';');
        if(token.type != ')') {
                statement->step = parse_expression();
        }
-       expect_fail(')');
-       statement->body = parse_statement();
+       expect(')');
+       statement->body = parse_loop_body((statement_t*)statement);
 
        assert(context == &statement->context);
        set_context(last_context);
        environment_pop_to(top);
-       current_loop = rem;
 
        return (statement_t*) statement;
-fail:
-       current_loop = rem;
-       return NULL;
 }
 
 /**
@@ -5130,6 +5184,13 @@ static statement_t *parse_goto(void)
 
        statement->label = label;
 
+       /* remember the goto's in a list for later checking */
+       if (goto_last == NULL) {
+               goto_first = goto_last = statement;
+       } else {
+               goto_last->next = statement;
+       }
+
        expect(';');
 
        return (statement_t*) statement;
@@ -5140,19 +5201,20 @@ static statement_t *parse_goto(void)
  */
 static statement_t *parse_continue(void)
 {
-       eat(T_continue);
-       expect(';');
-
-       statement_t *statement          = allocate_ast_zero(sizeof(statement[0]));
-       statement->kind                 = STATEMENT_CONTINUE;
-       statement->base.source_position = token.source_position;
-
+       statement_base_t *statement;
        if (current_loop == NULL) {
                errorf(HERE, "continue statement not within loop");
-               return NULL;
+               statement = NULL;
+       } else {
+               statement                  = allocate_ast_zero(sizeof(statement[0]));
+               statement->kind            = STATEMENT_CONTINUE;
+               statement->source_position = token.source_position;
        }
 
-       return statement;
+       eat(T_continue);
+       expect(';');
+
+       return (statement_t*)statement;
 }
 
 /**
@@ -5160,18 +5222,20 @@ static statement_t *parse_continue(void)
  */
 static statement_t *parse_break(void)
 {
-       eat(T_break);
-       expect(';');
-
-       statement_t *statement          = allocate_ast_zero(sizeof(statement[0]));
-       statement->kind                 = STATEMENT_BREAK;
-       statement->base.source_position = token.source_position;
-
+       statement_base_t *statement;
        if (current_switch == NULL && current_loop == NULL) {
                errorf(HERE, "break statement not within loop or switch");
-               return NULL;
+               statement = NULL;
+       } else {
+               statement                  = allocate_ast_zero(sizeof(statement[0]));
+               statement->kind            = STATEMENT_BREAK;
+               statement->source_position = token.source_position;
        }
-       return statement;
+
+       eat(T_break);
+       expect(';');
+
+       return (statement_t*)statement;
 }
 
 /**