>From 3d3a9b173cf11a832a95e581dbfddb2da092b08a Mon Sep 17 00:00:00 2001 From: "Arnold D. Robbins" Date: Wed, 7 Sep 2016 06:42:22 +0300 Subject: [PATCH] Add duplicate case value checking to switch statements. --- tccgen.c | 97 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) diff --git a/tccgen.c b/tccgen.c index 9ebd80a..6b4c682 100644 --- a/tccgen.c +++ b/tccgen.c @@ -4876,6 +4876,99 @@ static void label_or_decl(int l) decl(l); } +struct case_label { + int value; + struct case_label *next; + size_t line; +}; + +struct switch_list { + struct switch_list *outer; + struct case_label *labels; + size_t num_labels; +} *switch_list = NULL; + +static int label_compare(const void *l, const void *r) +{ + struct case_label **left = (struct case_label **) l; + struct case_label **right = (struct case_label **) r; + int ret; + + ret = ((*left)->value - (*right)->value); + if (ret != 0) + return ret; + + return ((*left)->line - (*right)->line); +} + +static void switch_start() +{ + struct switch_list *nsw = tcc_malloc(sizeof(struct switch_list)); + memset(nsw, 0, sizeof(*nsw)); + + if (switch_list != NULL) + nsw->outer = switch_list; + + switch_list = nsw; +} + +static void switch_end() +{ + struct case_label **labels = tcc_malloc(switch_list->num_labels * sizeof(struct case_label *)); + int i; + struct case_label *l = switch_list->labels; + struct case_label *l2; + struct switch_list *outer = switch_list->outer; + static int have_dups = 0; + + for (i = 0; i < switch_list->num_labels; i++) { + labels[i] = l; + l = l->next; + } + + qsort(labels, switch_list->num_labels, sizeof(struct case_label *), label_compare); + + for (i = 1; i < switch_list->num_labels; i++) { + if (labels[i-1]->value == labels[i]->value) { + have_dups = 1; + tcc_error_noabort("duplicate case label %d: lines %d and %d", + labels[i]->value, + labels[i-1]->line, + labels[i]->line); + } + } + + /* release all the memory */ + tcc_free(labels); + + for (l = switch_list->labels; l != NULL; l = l2) { + l2 = l->next; + tcc_free(l); + } + + tcc_free(switch_list); + switch_list = outer; + + if (have_dups && outer == NULL) + exit(1); +} + +static void switch_add_case(int case_val) +{ + struct case_label *label = tcc_malloc(sizeof(struct case_label)); + + memset(label, 0, sizeof(*label)); + label->value = case_val; + label->line = file->line_num; + + if (switch_list) { + label->next = switch_list->labels; + switch_list->labels = label; + switch_list->num_labels++; + } else + tcc_free(label); +} + static void block(int *bsym, int *csym, int *case_sym, int *def_sym, int case_reg, int is_expr) { @@ -5152,7 +5245,9 @@ static void block(int *bsym, int *csym, int *case_sym, int *def_sym, a = 0; b = gjmp(0); /* jump to first case */ c = 0; + switch_start(); block(&a, csym, &b, &c, case_reg, 0); + switch_end(); /* if no default, jmp after switch */ if (c == 0) c = ind; @@ -5167,10 +5262,12 @@ static void block(int *bsym, int *csym, int *case_sym, int *def_sym, expect("switch"); next(); v1 = expr_const(); + switch_add_case(v1); v2 = v1; if (gnu_ext && tok == TOK_DOTS) { next(); v2 = expr_const(); + switch_add_case(v2); if (v2 < v1) tcc_warning("empty case range"); } -- 2.7.4