diff options
author | Rico Tzschichholz <ricotz@ubuntu.com> | 2019-03-29 14:27:14 +0100 |
---|---|---|
committer | Rico Tzschichholz <ricotz@ubuntu.com> | 2020-04-24 08:50:08 +0200 |
commit | 90fc5b11e9a3b2c2fdbf7caf53a5ffb0fafd5545 (patch) | |
tree | dc25b14eebec4a68947fb27319f307db698a9308 | |
parent | 71af8d680d1277a1d0b187ce425b75375df7bf85 (diff) | |
download | vala-wip/issue/777.tar.gz |
WIP vala: Check coverage of switch on enum-type and issue warnings if neededwip/issue/777
Also don't emit implicit default label.
Fixes https://gitlab.gnome.org/GNOME/vala/issues/777
-rw-r--r-- | codegen/valaccodecontrolflowmodule.vala | 2 | ||||
-rw-r--r-- | tests/Makefile.am | 1 | ||||
-rw-r--r-- | tests/control-flow/switch-enum.vala | 20 | ||||
-rw-r--r-- | vala/valaflowanalyzer.vala | 39 |
4 files changed, 61 insertions, 1 deletions
diff --git a/codegen/valaccodecontrolflowmodule.vala b/codegen/valaccodecontrolflowmodule.vala index df31fcef3..a61b3d9fe 100644 --- a/codegen/valaccodecontrolflowmodule.vala +++ b/codegen/valaccodecontrolflowmodule.vala @@ -182,7 +182,7 @@ public abstract class Vala.CCodeControlFlowModule : CCodeMethodModule { section.emit (this); } - if (!has_default) { + if (!has_default && !(stmt.expression.value_type is EnumValueType)) { // silence C compiler ccode.add_default (); ccode.add_break (); diff --git a/tests/Makefile.am b/tests/Makefile.am index 2f3906f38..af8fe4d74 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -207,6 +207,7 @@ TESTS = \ control-flow/missing-return.test \ control-flow/nested-conditional.vala \ control-flow/switch.vala \ + control-flow/switch-enum.vala \ control-flow/sideeffects.vala \ control-flow/unassigned-captured-local-variable.test \ control-flow/unassigned-local-block-variable.test \ diff --git a/tests/control-flow/switch-enum.vala b/tests/control-flow/switch-enum.vala new file mode 100644 index 000000000..51aaaba4c --- /dev/null +++ b/tests/control-flow/switch-enum.vala @@ -0,0 +1,20 @@ +enum Foo { + FOO, + BAR, + MANAM +} + +Foo foo () { + Foo foo = Foo.BAR; + + switch (foo) { + case Foo.MANAM: + case Foo.FOO: + case Foo.BAR: + return foo; + } +} + +void main () { + assert (foo () == Foo.BAR); +} diff --git a/vala/valaflowanalyzer.vala b/vala/valaflowanalyzer.vala index ea0df7a83..07ba88998 100644 --- a/vala/valaflowanalyzer.vala +++ b/vala/valaflowanalyzer.vala @@ -664,6 +664,14 @@ public class Vala.FlowAnalyzer : CodeVisitor { handle_errors (stmt.expression); bool has_default_label = false; + bool is_enum_typed = stmt.expression.value_type is EnumValueType; + + unowned Enum? en = null; + HashSet<unowned EnumValue>? enum_values = null; + if (is_enum_typed) { + en = (Enum) ((EnumValueType) stmt.expression.value_type).type_symbol; + enum_values = new HashSet<unowned EnumValue> (direct_hash, direct_equal); + } foreach (SwitchSection section in stmt.get_sections ()) { current_block = new BasicBlock (); @@ -673,6 +681,17 @@ public class Vala.FlowAnalyzer : CodeVisitor { section_stmt.accept (this); } + if (is_enum_typed) { + foreach (SwitchLabel label in section.get_labels ()) { + if (label.expression != null) { + unowned EnumValue? val = label.expression.symbol_reference as EnumValue; + if (val != null) { + enum_values.add (val); + } + } + } + } + if (section.has_default_label ()) { has_default_label = true; } @@ -688,6 +707,26 @@ public class Vala.FlowAnalyzer : CodeVisitor { } } + if (is_enum_typed) { + // Check if enum-based switching as fully covered, and if so, + // handle it like there was a default-label given + + HashSet<EnumValue> remaining_values = new HashSet<EnumValue> (); + remaining_values.add_all (en.get_values ()); + foreach (var val in enum_values) { + remaining_values.remove (val); + } + if (remaining_values.size == 0) { + has_default_label = true; + } else if (!has_default_label) { + string[] missing_vals = {}; + foreach (var val in remaining_values) { + missing_vals += val.name; + } + Report.warning (stmt.source_reference, "switch does not handle `%s' of enum `%s'".printf (string.joinv ("', `", missing_vals), en.get_full_name ())); + } + } + if (!has_default_label) { condition_block.connect (after_switch_block); } |