diff --git a/src/build_settings.cpp b/src/build_settings.cpp index fcc28e861..8ffc4955a 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -196,6 +196,7 @@ struct BuildContext { bool keep_object_files; bool disallow_do; bool insert_semicolon; + bool strict_style; bool ignore_warnings; bool warnings_as_errors; diff --git a/src/main.cpp b/src/main.cpp index 0897e5fa6..0f6208134 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -602,6 +602,7 @@ enum BuildFlagKind { BuildFlag_DisallowDo, BuildFlag_DefaultToNilAllocator, BuildFlag_InsertSemicolon, + BuildFlag_StrictStyle, BuildFlag_Compact, BuildFlag_GlobalDefinitions, @@ -716,6 +717,7 @@ bool parse_build_flags(Array args) { add_flag(&build_flags, BuildFlag_DisallowDo, str_lit("disallow-do"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_DefaultToNilAllocator, str_lit("default-to-nil-allocator"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_InsertSemicolon, str_lit("insert-semicolon"), BuildFlagParam_None, Command__does_check); + add_flag(&build_flags, BuildFlag_StrictStyle, str_lit("strict-style"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_Compact, str_lit("compact"), BuildFlagParam_None, Command_query); add_flag(&build_flags, BuildFlag_GlobalDefinitions, str_lit("global-definitions"), BuildFlagParam_None, Command_query); add_flag(&build_flags, BuildFlag_GoToDefinitions, str_lit("go-to-definitions"), BuildFlagParam_None, Command_query); @@ -1183,6 +1185,12 @@ bool parse_build_flags(Array args) { build_context.insert_semicolon = true; break; + case BuildFlag_StrictStyle: + build_context.insert_semicolon = true; + build_context.strict_style = true; + break; + + case BuildFlag_Compact: if (!build_context.query_data_set_settings.ok) { gb_printf_err("Invalid use of -compact flag, only allowed with 'odin query'\n"); @@ -1733,6 +1741,14 @@ void print_show_help(String const arg0, String const &command) { print_usage_line(2, "Sets the default allocator to be the nil_allocator, an allocator which does nothing"); print_usage_line(0, ""); + print_usage_line(1, "-insert-semicolon"); + print_usage_line(2, "Inserts semicolons on newlines during tokenization using a basic rule"); + print_usage_line(0, ""); + + print_usage_line(1, "-strict-style"); + print_usage_line(2, "Enforces code style stricter whilst parsing, requiring such things as trailing commas"); + print_usage_line(0, ""); + print_usage_line(1, "-ignore-warnings"); print_usage_line(2, "Ignores warning messages"); print_usage_line(0, ""); diff --git a/src/parser.cpp b/src/parser.cpp index 3f189e96b..63099cf55 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -106,6 +106,16 @@ Token ast_token(Ast *node) { return empty_token; } +Token token_end_of_line(AstFile *f, Token tok) { + u8 const *start = f->tokenizer.start + tok.pos.offset; + u8 const *s = start; + while (*s && *s != '\n' && s < f->tokenizer.end) { + s += 1; + } + tok.pos.column += cast(i32)(s - start) - 1; + return tok; +} + isize ast_node_size(AstKind kind) { return align_formula_isize(gb_size_of(AstCommonStuff) + ast_variant_sizes[kind], gb_align_of(void *)); @@ -1379,6 +1389,17 @@ bool allow_token(AstFile *f, TokenKind kind) { return false; } +Token expect_closing_brace_of_field_list(AstFile *f) { + Token token = f->curr_token; + if (allow_token(f, Token_CloseBrace)) { + return token; + } + if (allow_token(f, Token_Semicolon)) { + String p = token_to_string(token); + syntax_error(token_end_of_line(f, f->prev_token), "Expected a comma, got a %.*s", LIT(p)); + } + return expect_token(f, Token_CloseBrace); +} bool is_blank_ident(String str) { if (str.len == 1) { @@ -1451,7 +1472,9 @@ Token expect_closing(AstFile *f, TokenKind kind, String context) { if (f->curr_token.kind != kind && f->curr_token.kind == Token_Semicolon && f->curr_token.string == "\n") { - syntax_error(f->curr_token, "Missing ',' before newline in %.*s", LIT(context)); + Token tok = f->prev_token; + tok.pos.column += cast(i32)tok.string.len; + syntax_error(tok, "Missing ',' before newline in %.*s", LIT(context)); advance_token(f); } return expect_token(f, kind); @@ -1512,15 +1535,41 @@ bool is_semicolon_optional_for_node(AstFile *f, Ast *s) { return false; } -void expect_semicolon(AstFile *f, Ast *s) { - if (allow_token(f, Token_Semicolon)) { - return; - } - Token prev_token = f->prev_token; - if (prev_token.kind == Token_Semicolon) { - return; - } +void expect_semicolon_newline_error(AstFile *f, Token const &token, Ast *s) { + if (build_context.strict_style && false) { + if (f->curr_proc != nullptr && token.string == "\n") { + switch (token.kind) { + case Token_CloseBrace: + case Token_CloseParen: + case Token_else: + case Token_EOF: + return; + } + if (s != nullptr) { + if (is_semicolon_optional_for_node(f, s)) { + return; + } + } + Token tok = token; + tok.pos.column -= 1; + syntax_error(tok, "Expected ';', got newline"); + } + } +} + +void expect_semicolon(AstFile *f, Ast *s) { + Token prev_token = {}; + + if (allow_token(f, Token_Semicolon)) { + expect_semicolon_newline_error(f, f->prev_token, s); + return; + } + prev_token = f->prev_token; + if (prev_token.kind == Token_Semicolon) { + expect_semicolon_newline_error(f, f->prev_token, s); + return; + } if (s != nullptr) { bool insert_semi = (f->tokenizer.flags & TokenizerFlag_InsertSemicolon) != 0; @@ -2241,9 +2290,9 @@ Ast *parse_operand(AstFile *f, bool lhs) { Token open = expect_token_after(f, Token_OpenBrace, "struct"); - isize name_count = 0; + isize name_count = 0; Ast *fields = parse_struct_field_list(f, &name_count); - Token close = expect_token(f, Token_CloseBrace); + Token close = expect_closing_brace_of_field_list(f); Slice decls = {}; if (fields != nullptr) { @@ -2328,7 +2377,7 @@ Ast *parse_operand(AstFile *f, bool lhs) { } } - Token close = expect_token(f, Token_CloseBrace); + Token close = expect_closing_brace_of_field_list(f); return ast_union_type(f, token, variants, polymorphic_params, align, no_nil, maybe, where_token, where_clauses); } break; @@ -2342,7 +2391,7 @@ Ast *parse_operand(AstFile *f, bool lhs) { Token open = expect_token(f, Token_OpenBrace); Array values = parse_element_list(f); - Token close = expect_token(f, Token_CloseBrace); + Token close = expect_closing_brace_of_field_list(f); return ast_enum_type(f, token, base_type, values); } break; @@ -2435,7 +2484,7 @@ Ast *parse_operand(AstFile *f, bool lhs) { expect_token(f, Token_Comma); Ast *constraints_string = parse_expr(f, false); allow_token(f, Token_Comma); - Token close = expect_token(f, Token_CloseBrace); + Token close = expect_closing_brace_of_field_list(f); return ast_inline_asm_expr(f, token, open, close, param_types, return_type, asm_string, constraints_string, has_side_effects, is_align_stack, dialect); } @@ -3370,7 +3419,7 @@ bool parse_expect_field_separator(AstFile *f, Ast *param) { } if (token.kind == Token_Semicolon) { String p = token_to_string(token); - syntax_error(f->curr_token, "Expected a comma, got a %.*s", LIT(p)); + syntax_error(token_end_of_line(f, f->prev_token), "Expected a comma, got a %.*s", LIT(p)); advance_token(f); return true; } @@ -3648,6 +3697,18 @@ Ast *parse_body(AstFile *f) { return ast_block_stmt(f, stmts, open, close); } +bool parse_control_statement_semicolon_separator(AstFile *f) { + Token tok = peek_token(f); + if (tok.kind != Token_OpenBrace) { + return allow_token(f, Token_Semicolon); + } + if (f->curr_token.string == ";") { + return allow_token(f, Token_Semicolon); + } + return false; +} + + Ast *parse_if_stmt(AstFile *f) { if (f->curr_proc == nullptr) { syntax_error(f->curr_token, "You cannot use an if statement in the file scope"); @@ -3669,7 +3730,7 @@ Ast *parse_if_stmt(AstFile *f) { cond = parse_expr(f, false); } else { init = parse_simple_stmt(f, StmtAllowFlag_None); - if (allow_token(f, Token_Semicolon)) { + if (parse_control_statement_semicolon_separator(f)) { cond = parse_expr(f, false); } else { cond = convert_stmt_to_expr(f, init, str_lit("boolean expression")); @@ -3977,7 +4038,7 @@ Ast *parse_switch_stmt(AstFile *f) { tag = parse_simple_stmt(f, StmtAllowFlag_In); if (tag->kind == Ast_AssignStmt && tag->AssignStmt.op.kind == Token_in) { is_type_switch = true; - } else if (allow_token(f, Token_Semicolon)) { + } else if (parse_control_statement_semicolon_separator(f)) { init = tag; tag = nullptr; if (f->curr_token.kind != Token_OpenBrace) { @@ -3986,6 +4047,7 @@ Ast *parse_switch_stmt(AstFile *f) { } } } + skip_possible_newline(f); open = expect_token(f, Token_OpenBrace); while (f->curr_token.kind == Token_case) {