diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index c87d29204..f272b0b77 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -3322,6 +3322,11 @@ INLINE bool expr_is_const_string(Expr *expr) return expr->expr_kind == EXPR_CONST && expr->const_expr.const_kind == CONST_STRING; } +INLINE bool expr_is_const_enum(Expr *expr) +{ + return expr->expr_kind == EXPR_CONST && expr->const_expr.const_kind == CONST_ENUM; +} + INLINE bool expr_is_const_pointer(Expr *expr) { return expr->expr_kind == EXPR_CONST && expr->const_expr.const_kind == CONST_POINTER; diff --git a/src/compiler/sema_stmts.c b/src/compiler/sema_stmts.c index 0ed60dfeb..40af33590 100644 --- a/src/compiler/sema_stmts.c +++ b/src/compiler/sema_stmts.c @@ -2101,6 +2101,48 @@ static inline bool sema_check_value_case(SemaContext *context, Type *switch_type return true; } +INLINE const char *create_missing_enums_in_switch_error(Ast **cases, unsigned case_count, Decl **enums) +{ + unsigned missing = vec_size(enums) - case_count; + scratch_buffer_clear(); + if (missing == 1) + { + scratch_buffer_append("Enum value "); + } + else + { + scratch_buffer_printf("%u enum values were not handled in the switch: ", missing); + } + unsigned printed = 0; + FOREACH_BEGIN(Decl *decl, enums) + for (unsigned i = 0; i < case_count; i++) + { + Expr *e = cases[i]->case_stmt.expr; + assert(expr_is_const_enum(e)); + if (e->const_expr.enum_err_val == decl) goto CONTINUE; + } + if (++printed != 1) + { + scratch_buffer_append(printed == missing ? " and " : ", "); + } + scratch_buffer_append(decl->name); + if (printed > 2 && missing > 3) + { + scratch_buffer_append(", ..."); + goto DONE; + } + if (printed == missing) goto DONE; +CONTINUE:; + FOREACH_END(); +DONE:; + if (missing == 1) + { + scratch_buffer_append(" was not handled in the switch - either add it or add 'default'."); + return scratch_buffer_to_string(); + } + scratch_buffer_append(" - either add them or use 'default'."); + return scratch_buffer_to_string(); +} static bool sema_analyse_switch_body(SemaContext *context, Ast *statement, SourceSpan expr_span, Type *switch_type, Ast **cases, ExprAnySwitch *any_switch, Decl *var_holder) { bool use_type_id = false; @@ -2165,7 +2207,7 @@ static bool sema_analyse_switch_body(SemaContext *context, Ast *statement, Sourc POP_NEXT(); } - if (!exhaustive && is_enum_switch && case_count == vec_size(flat->decl->enums.values)) exhaustive = true; + if (!exhaustive && is_enum_switch) exhaustive = case_count >= vec_size(flat->decl->enums.values); bool all_jump_end = exhaustive; for (unsigned i = 0; i < case_count; i++) { @@ -2217,6 +2259,11 @@ static bool sema_analyse_switch_body(SemaContext *context, Ast *statement, Sourc all_jump_end &= context->active_scope.jump_end; SCOPE_END; } + if (is_enum_switch && !exhaustive && success) + { + SEMA_ERROR(statement, create_missing_enums_in_switch_error(cases, case_count, flat->decl->enums.values)); + success = false; + } statement->flow.no_exit = all_jump_end; statement->switch_stmt.flow.if_chain = if_chain || max_ranged; return success; diff --git a/test/test_suite/statements/switch_errors.c3 b/test/test_suite/statements/switch_errors.c3 index 43a401ead..bfc2af712 100644 --- a/test/test_suite/statements/switch_errors.c3 +++ b/test/test_suite/statements/switch_errors.c3 @@ -91,14 +91,14 @@ enum Baz fn void test_missing_all_cases(Baz x) { - switch (x) // -error: 4 enumeration values not handled in switch: A, B, C, ... + switch (x) // #error: not handled in the switch: A, B, C, ... { } } fn void test_missing_some_cases(Baz x) { - switch (x) // -error: 4 enumeration B, C and D not handled in switch + switch (x) // #error: not handled in the switch: B, C and D { case A: break; @@ -107,7 +107,7 @@ fn void test_missing_some_cases(Baz x) fn void test_missing_some_cases2(Baz x) { - switch (x) // -error: 4 enumeration B and D not handled in switch + switch (x) // #error: B and D { case C: case A: @@ -117,7 +117,7 @@ fn void test_missing_some_cases2(Baz x) fn void test_missing_some_cases3(Baz x) { - switch (x) // -error: 4 enumeration B and D not handled in switch + switch (x) // #error: Enum value D was not handled in the switch { case B: case C: