From 5df53c841ebc779bd20cd8b9a162adb115ade384 Mon Sep 17 00:00:00 2001 From: Akuli Date: Mon, 11 Dec 2023 19:44:10 +0200 Subject: [PATCH] Add unused variable warning (#443) --- examples/aoc2023/day05/part2.jou | 1 - self_hosted/runs_wrong.txt | 1 + src/simplify_cfg.c | 23 +++++++++++++++++++---- tests/should_succeed/and_or_not.jou | 3 +++ tests/should_succeed/unused_import.jou | 6 ++++++ tests/should_succeed/unused_variable.jou | 3 +++ 6 files changed, 32 insertions(+), 5 deletions(-) create mode 100644 tests/should_succeed/unused_variable.jou diff --git a/examples/aoc2023/day05/part2.jou b/examples/aoc2023/day05/part2.jou index e04b1a42..746e30b8 100644 --- a/examples/aoc2023/day05/part2.jou +++ b/examples/aoc2023/day05/part2.jou @@ -72,7 +72,6 @@ class Map: for i = 0; i < self->ntriples; i++: dest_start = self->triples[i][0] source_start = self->triples[i][1] - range_length = self->triples[i][2] # Solve equation: output = n - source_start + dest_start n = output + source_start - dest_start diff --git a/self_hosted/runs_wrong.txt b/self_hosted/runs_wrong.txt index 96e30101..26580344 100644 --- a/self_hosted/runs_wrong.txt +++ b/self_hosted/runs_wrong.txt @@ -49,3 +49,4 @@ tests/should_succeed/if_WINDOWS_at_runtime.jou tests/should_succeed/return_none.jou tests/syntax_error/assign_to_None.jou tests/syntax_error/None_as_value.jou +tests/should_succeed/unused_variable.jou diff --git a/src/simplify_cfg.c b/src/simplify_cfg.c index f91ed0cc..79695208 100644 --- a/src/simplify_cfg.c +++ b/src/simplify_cfg.c @@ -435,25 +435,40 @@ static void remove_unreachable_blocks(CfGraph *cfg) static void remove_unused_variables(CfGraph *cfg) { + enum { READ=1, WRITE=2 }; char *used = calloc(1, cfg->locals.len); + Location *write_locations = malloc(sizeof(write_locations[0]) * cfg->locals.len); for (CfBlock **b = cfg->all_blocks.ptr; b < End(cfg->all_blocks); b++) { for (CfInstruction *ins = (*b)->instructions.ptr; ins < End((*b)->instructions); ins++) { - if (ins->destvar) - used[find_var_index(cfg, ins->destvar)] = true; + if (ins->destvar) { + used[find_var_index(cfg, ins->destvar)] |= WRITE; + write_locations[find_var_index(cfg, ins->destvar)] = ins->location; + } for (int i = 0; i < ins->noperands; i++) - used[find_var_index(cfg, ins->operands[i])] = true; + used[find_var_index(cfg, ins->operands[i])] |= READ; + } + } + + for (int i = 0; i < cfg->locals.len; i++) { + if ( + used[i] == WRITE + && cfg->locals.ptr[i]->name[0] != '\0' + && strcmp(cfg->locals.ptr[i]->name, "return") != 0 + ) { + show_warning(write_locations[i], "variable '%s' is never used", cfg->locals.ptr[i]->name); } } for (int i = cfg->locals.len - 1; i>=0; i--) { - if (!used[i] && !cfg->locals.ptr[i]->is_argument) { + if (used[i] == 0 && !cfg->locals.ptr[i]->is_argument) { free(cfg->locals.ptr[i]); cfg->locals.ptr[i] = Pop(&cfg->locals); } } free(used); + free(write_locations); } static void warn_about_undefined_variables(CfGraph *cfg) diff --git a/tests/should_succeed/and_or_not.jou b/tests/should_succeed/and_or_not.jou index a14b94c4..f9672ec9 100644 --- a/tests/should_succeed/and_or_not.jou +++ b/tests/should_succeed/and_or_not.jou @@ -99,4 +99,7 @@ def main() -> int: result = not side_effect('b', False) printf("\n") + # prevent unused variable warnings + assert result + return 0 diff --git a/tests/should_succeed/unused_import.jou b/tests/should_succeed/unused_import.jou index 53771033..8d52f22e 100644 --- a/tests/should_succeed/unused_import.jou +++ b/tests/should_succeed/unused_import.jou @@ -8,4 +8,10 @@ def main() -> int: x = stdout y = strcmp("foo", "bar") z = Point{x=1, y=2} + + # prevent unused variable warnings + assert x != NULL + assert y != 0 + assert z.x != 0 + return 0 diff --git a/tests/should_succeed/unused_variable.jou b/tests/should_succeed/unused_variable.jou new file mode 100644 index 00000000..550409b6 --- /dev/null +++ b/tests/should_succeed/unused_variable.jou @@ -0,0 +1,3 @@ +def main() -> int: + x = 4 # Warning: variable 'x' is never used + return 0