A statement_base_t is enough space for break and continue statements.
[cparser] / parser.c
index d34696a..7d9b334 100644 (file)
--- a/parser.c
+++ b/parser.c
@@ -49,6 +49,9 @@ static context_t          *context           = NULL;
 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. */
@@ -2685,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
@@ -2778,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;
@@ -4849,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.
  */
@@ -4878,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;
@@ -4989,13 +5058,22 @@ static statement_t *parse_switch(void)
        expect(')');
 
        switch_statement_t *rem = current_switch;
-       current_switch  = &statement;
+       current_switch  = statement;
        statement->body = parse_statement();
        current_switch  = rem;
 
        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.
  */
@@ -5010,7 +5088,8 @@ static statement_t *parse_while(void)
        expect('(');
        statement->condition = parse_expression();
        expect(')');
-       statement->body = parse_statement();
+
+       statement->body = parse_loop_body((statement_t*)statement);
 
        return (statement_t*) statement;
 }
@@ -5026,7 +5105,7 @@ static statement_t *parse_do(void)
        statement->statement.kind            = STATEMENT_DO_WHILE;
        statement->statement.source_position = token.source_position;
 
-       statement->body = parse_statement();
+       statement->body = parse_loop_body((statement_t*)statement);
        expect(T_while);
        expect('(');
        statement->condition = parse_expression();
@@ -5072,7 +5151,7 @@ static statement_t *parse_for(void)
                statement->step = parse_expression();
        }
        expect(')');
-       statement->body = parse_statement();
+       statement->body = parse_loop_body((statement_t*)statement);
 
        assert(context == &statement->context);
        set_context(last_context);
@@ -5105,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;
@@ -5115,14 +5201,20 @@ static statement_t *parse_goto(void)
  */
 static statement_t *parse_continue(void)
 {
+       statement_base_t *statement;
+       if (current_loop == NULL) {
+               errorf(HERE, "continue statement not within loop");
+               statement = NULL;
+       } else {
+               statement                  = allocate_ast_zero(sizeof(statement[0]));
+               statement->kind            = STATEMENT_CONTINUE;
+               statement->source_position = token.source_position;
+       }
+
        eat(T_continue);
        expect(';');
 
-       statement_t *statement          = allocate_ast_zero(sizeof(statement[0]));
-       statement->kind                 = STATEMENT_CONTINUE;
-       statement->base.source_position = token.source_position;
-
-       return statement;
+       return (statement_t*)statement;
 }
 
 /**
@@ -5130,14 +5222,20 @@ static statement_t *parse_continue(void)
  */
 static statement_t *parse_break(void)
 {
+       statement_base_t *statement;
+       if (current_switch == NULL && current_loop == NULL) {
+               errorf(HERE, "break statement not within loop or switch");
+               statement = NULL;
+       } else {
+               statement                  = allocate_ast_zero(sizeof(statement[0]));
+               statement->kind            = STATEMENT_BREAK;
+               statement->source_position = token.source_position;
+       }
+
        eat(T_break);
        expect(';');
 
-       statement_t *statement          = allocate_ast_zero(sizeof(statement[0]));
-       statement->kind                 = STATEMENT_BREAK;
-       statement->base.source_position = token.source_position;
-
-       return statement;
+       return (statement_t*)statement;
 }
 
 /**