From c7eb0024c72197fcbaa47a6500d12c03f4fec106 Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Tue, 21 Jan 2025 00:38:24 +0100 Subject: [PATCH] Error on switch case fallthough if there is more than one newline #1849. --- releasenotes.md | 1 + src/compiler/parse_stmt.c | 18 ++++++++++++++---- .../statements/switch_error_fallthrough.c3 | 11 +++++++++++ 3 files changed, 26 insertions(+), 4 deletions(-) create mode 100644 test/test_suite/statements/switch_error_fallthrough.c3 diff --git a/releasenotes.md b/releasenotes.md index 7e76c5a33..d21daf396 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -5,6 +5,7 @@ ### Changes / improvements - Contracts @require/@ensure are no longer treated as conditionals, but must be explicitly bool. - Add `win-debug` setting to be able to pick dwarf for output #1855. +- Error on switch case fallthough if there is more than one newline #1849. ### Fixes - Fix issue requiring prefix on a generic interface declaration. diff --git a/src/compiler/parse_stmt.c b/src/compiler/parse_stmt.c index e55e7be4c..7628edfb5 100644 --- a/src/compiler/parse_stmt.c +++ b/src/compiler/parse_stmt.c @@ -522,9 +522,17 @@ static inline bool token_type_ends_case(TokenType type, TokenType case_type, Tok return type == case_type || type == default_type || type == TOKEN_RBRACE || type == TOKEN_CT_ENDSWITCH; } -static inline Ast *parse_case_stmts(ParseContext *c, TokenType case_type, TokenType default_type) +static inline Ast *parse_case_stmts(ParseContext *c, TokenType case_type, TokenType default_type, uint32_t row) { - if (token_type_ends_case(c->tok, case_type, default_type)) return NULL; + if (token_type_ends_case(c->tok, case_type, default_type)) + { + if (c->span.row > row + 1 && (c->tok == TOKEN_CASE || c->tok == TOKEN_DEFAULT)) + { + PRINT_ERROR_LAST("Fallthrough cases with empty rows or comments have unclear meaning, an explicit 'break' or 'nextcase' is needed (or remove the spacing!)."); + return poisoned_ast; + } + return NULL; + } Ast *compound = new_ast(AST_COMPOUND_STMT, c->span); AstId *next = &compound->compound_stmt.first_stmt; while (!token_type_ends_case(c->tok, case_type, default_type)) @@ -678,13 +686,14 @@ static inline Ast *parse_case_stmt(ParseContext *c, TokenType case_type, TokenTy { ASSIGN_EXPRID_OR_RET(ast->case_stmt.to_expr, parse_expr(c), poisoned_ast); } + uint32_t row = c->span.row; if (!try_consume(c, TOKEN_COLON)) { print_error_at(c->prev_span, "Missing ':' after case"); return poisoned_ast; } RANGE_EXTEND_PREV(ast); - ASSIGN_AST_OR_RET(ast->case_stmt.body, parse_case_stmts(c, case_type, default_type), poisoned_ast); + ASSIGN_AST_OR_RET(ast->case_stmt.body, parse_case_stmts(c, case_type, default_type, row), poisoned_ast); return ast; } @@ -697,8 +706,9 @@ static inline Ast *parse_default_stmt(ParseContext *c, TokenType case_type, Toke Ast *ast = new_ast(AST_DEFAULT_STMT, c->span); advance(c); TRY_CONSUME_OR_RET(TOKEN_COLON, "Expected ':' after 'default'.", poisoned_ast); + uint32_t row = c->span.row; RANGE_EXTEND_PREV(ast); - ASSIGN_AST_OR_RET(ast->case_stmt.body, parse_case_stmts(c, case_type, default_type), poisoned_ast); + ASSIGN_AST_OR_RET(ast->case_stmt.body, parse_case_stmts(c, case_type, default_type, row), poisoned_ast); ast->case_stmt.expr = 0; return ast; } diff --git a/test/test_suite/statements/switch_error_fallthrough.c3 b/test/test_suite/statements/switch_error_fallthrough.c3 new file mode 100644 index 000000000..991d1fd1d --- /dev/null +++ b/test/test_suite/statements/switch_error_fallthrough.c3 @@ -0,0 +1,11 @@ +fn int main() +{ + int a; + switch (a) + { + case 1: // #error: Fallthrough cases + // ... + case 2: + } + return 0; +} \ No newline at end of file